aboutsummaryrefslogtreecommitdiff
path: root/src/trace_processor/importers/proto/proto_trace_parser_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/trace_processor/importers/proto/proto_trace_parser_impl.cc')
-rw-r--r--src/trace_processor/importers/proto/proto_trace_parser_impl.cc482
1 files changed, 482 insertions, 0 deletions
diff --git a/src/trace_processor/importers/proto/proto_trace_parser_impl.cc b/src/trace_processor/importers/proto/proto_trace_parser_impl.cc
new file mode 100644
index 000000000..bbd38c3f0
--- /dev/null
+++ b/src/trace_processor/importers/proto/proto_trace_parser_impl.cc
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/proto/proto_trace_parser_impl.h"
+
+#include <string.h>
+
+#include <cinttypes>
+#include <string>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/metatrace_events.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/base/string_writer.h"
+#include "perfetto/ext/base/uuid.h"
+
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/machine_tracker.h"
+#include "src/trace_processor/importers/common/metadata_tracker.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/common/process_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/importers/etw/etw_module.h"
+#include "src/trace_processor/importers/ftrace/ftrace_module.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/track_event_module.h"
+#include "src/trace_processor/storage/metadata.h"
+#include "src/trace_processor/storage/stats.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/types/variadic.h"
+
+#include "protos/perfetto/common/trace_stats.pbzero.h"
+#include "protos/perfetto/config/trace_config.pbzero.h"
+#include "protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
+#include "protos/perfetto/trace/perfetto/perfetto_metatrace.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+ProtoTraceParserImpl::ProtoTraceParserImpl(TraceProcessorContext* context)
+ : context_(context),
+ metatrace_id_(context->storage->InternString("metatrace")),
+ data_name_id_(context->storage->InternString("data")),
+ raw_chrome_metadata_event_id_(
+ context->storage->InternString("chrome_event.metadata")),
+ raw_chrome_legacy_system_trace_event_id_(
+ context->storage->InternString("chrome_event.legacy_system_trace")),
+ raw_chrome_legacy_user_trace_event_id_(
+ context->storage->InternString("chrome_event.legacy_user_trace")),
+ missing_metatrace_interned_string_id_(
+ context->storage->InternString("MISSING STRING")) {}
+
+ProtoTraceParserImpl::~ProtoTraceParserImpl() = default;
+
+void ProtoTraceParserImpl::ParseTracePacket(int64_t ts, TracePacketData data) {
+ const TraceBlobView& blob = data.packet;
+ protos::pbzero::TracePacket::Decoder packet(blob.data(), blob.length());
+ // TODO(eseckler): Propagate statuses from modules.
+ auto& modules = context_->modules_by_field;
+ for (uint32_t field_id = 1; field_id < modules.size(); ++field_id) {
+ if (!modules[field_id].empty() && packet.Get(field_id).valid()) {
+ for (ProtoImporterModule* global_module :
+ context_->modules_for_all_fields) {
+ global_module->ParseTracePacketData(packet, ts, data, field_id);
+ }
+ for (ProtoImporterModule* module : modules[field_id])
+ module->ParseTracePacketData(packet, ts, data, field_id);
+ return;
+ }
+ }
+
+ if (packet.has_trace_stats())
+ ParseTraceStats(packet.trace_stats());
+
+ if (packet.has_chrome_events()) {
+ ParseChromeEvents(ts, packet.chrome_events());
+ }
+
+ if (packet.has_perfetto_metatrace()) {
+ ParseMetatraceEvent(ts, packet.perfetto_metatrace());
+ }
+
+ if (packet.has_trace_config()) {
+ // TODO(eseckler): Propagate statuses from modules.
+ protos::pbzero::TraceConfig::Decoder config(packet.trace_config());
+ for (auto& module : context_->modules) {
+ module->ParseTraceConfig(config);
+ }
+ }
+}
+
+void ProtoTraceParserImpl::ParseTrackEvent(int64_t ts, TrackEventData data) {
+ const TraceBlobView& blob = data.trace_packet_data.packet;
+ protos::pbzero::TracePacket::Decoder packet(blob.data(), blob.length());
+ context_->track_module->ParseTrackEventData(packet, ts, data);
+ context_->args_tracker->Flush();
+}
+
+void ProtoTraceParserImpl::ParseEtwEvent(uint32_t cpu,
+ int64_t ts,
+ TracePacketData data) {
+ PERFETTO_DCHECK(context_->etw_module);
+ context_->etw_module->ParseEtwEventData(cpu, ts, data);
+
+ // TODO(lalitm): maybe move this to the flush method in the trace processor
+ // once we have it. This may reduce performance in the ArgsTracker though so
+ // needs to be handled carefully.
+ context_->args_tracker->Flush();
+}
+
+void ProtoTraceParserImpl::ParseFtraceEvent(uint32_t cpu,
+ int64_t ts,
+ TracePacketData data) {
+ PERFETTO_DCHECK(context_->ftrace_module);
+ context_->ftrace_module->ParseFtraceEventData(cpu, ts, data);
+
+ // TODO(lalitm): maybe move this to the flush method in the trace processor
+ // once we have it. This may reduce performance in the ArgsTracker though so
+ // needs to be handled carefully.
+ context_->args_tracker->Flush();
+}
+
+void ProtoTraceParserImpl::ParseInlineSchedSwitch(uint32_t cpu,
+ int64_t ts,
+ InlineSchedSwitch data) {
+ PERFETTO_DCHECK(context_->ftrace_module);
+ context_->ftrace_module->ParseInlineSchedSwitch(cpu, ts, data);
+
+ // TODO(lalitm): maybe move this to the flush method in the trace processor
+ // once we have it. This may reduce performance in the ArgsTracker though so
+ // needs to be handled carefully.
+ context_->args_tracker->Flush();
+}
+
+void ProtoTraceParserImpl::ParseInlineSchedWaking(uint32_t cpu,
+ int64_t ts,
+ InlineSchedWaking data) {
+ PERFETTO_DCHECK(context_->ftrace_module);
+ context_->ftrace_module->ParseInlineSchedWaking(cpu, ts, data);
+
+ // TODO(lalitm): maybe move this to the flush method in the trace processor
+ // once we have it. This may reduce performance in the ArgsTracker though so
+ // needs to be handled carefully.
+ context_->args_tracker->Flush();
+}
+
+void ProtoTraceParserImpl::ParseTraceStats(ConstBytes blob) {
+ protos::pbzero::TraceStats::Decoder evt(blob.data, blob.size);
+ auto* storage = context_->storage.get();
+ storage->SetStats(stats::traced_producers_connected,
+ static_cast<int64_t>(evt.producers_connected()));
+ storage->SetStats(stats::traced_producers_seen,
+ static_cast<int64_t>(evt.producers_seen()));
+ storage->SetStats(stats::traced_data_sources_registered,
+ static_cast<int64_t>(evt.data_sources_registered()));
+ storage->SetStats(stats::traced_data_sources_seen,
+ static_cast<int64_t>(evt.data_sources_seen()));
+ storage->SetStats(stats::traced_tracing_sessions,
+ static_cast<int64_t>(evt.tracing_sessions()));
+ storage->SetStats(stats::traced_total_buffers,
+ static_cast<int64_t>(evt.total_buffers()));
+ storage->SetStats(stats::traced_chunks_discarded,
+ static_cast<int64_t>(evt.chunks_discarded()));
+ storage->SetStats(stats::traced_patches_discarded,
+ static_cast<int64_t>(evt.patches_discarded()));
+ storage->SetStats(stats::traced_flushes_requested,
+ static_cast<int64_t>(evt.flushes_requested()));
+ storage->SetStats(stats::traced_flushes_succeeded,
+ static_cast<int64_t>(evt.flushes_succeeded()));
+ storage->SetStats(stats::traced_flushes_failed,
+ static_cast<int64_t>(evt.flushes_failed()));
+
+ if (evt.has_filter_stats()) {
+ protos::pbzero::TraceStats::FilterStats::Decoder fstat(evt.filter_stats());
+ storage->SetStats(stats::filter_errors,
+ static_cast<int64_t>(fstat.errors()));
+ storage->SetStats(stats::filter_input_bytes,
+ static_cast<int64_t>(fstat.input_bytes()));
+ storage->SetStats(stats::filter_input_packets,
+ static_cast<int64_t>(fstat.input_packets()));
+ storage->SetStats(stats::filter_output_bytes,
+ static_cast<int64_t>(fstat.output_bytes()));
+ storage->SetStats(stats::filter_time_taken_ns,
+ static_cast<int64_t>(fstat.time_taken_ns()));
+ for (auto [i, it] = std::tuple{0, fstat.bytes_discarded_per_buffer()}; it;
+ ++it, ++i) {
+ storage->SetIndexedStats(stats::traced_buf_bytes_filtered_out, i,
+ static_cast<int64_t>(*it));
+ }
+ }
+
+ switch (evt.final_flush_outcome()) {
+ case protos::pbzero::TraceStats::FINAL_FLUSH_SUCCEEDED:
+ storage->IncrementStats(stats::traced_final_flush_succeeded, 1);
+ break;
+ case protos::pbzero::TraceStats::FINAL_FLUSH_FAILED:
+ storage->IncrementStats(stats::traced_final_flush_failed, 1);
+ break;
+ case protos::pbzero::TraceStats::FINAL_FLUSH_UNSPECIFIED:
+ break;
+ }
+
+ int buf_num = 0;
+ for (auto it = evt.buffer_stats(); it; ++it, ++buf_num) {
+ protos::pbzero::TraceStats::BufferStats::Decoder buf(*it);
+ storage->SetIndexedStats(stats::traced_buf_buffer_size, buf_num,
+ static_cast<int64_t>(buf.buffer_size()));
+ storage->SetIndexedStats(stats::traced_buf_bytes_written, buf_num,
+ static_cast<int64_t>(buf.bytes_written()));
+ storage->SetIndexedStats(stats::traced_buf_bytes_overwritten, buf_num,
+ static_cast<int64_t>(buf.bytes_overwritten()));
+ storage->SetIndexedStats(stats::traced_buf_bytes_read, buf_num,
+ static_cast<int64_t>(buf.bytes_read()));
+ storage->SetIndexedStats(stats::traced_buf_padding_bytes_written, buf_num,
+ static_cast<int64_t>(buf.padding_bytes_written()));
+ storage->SetIndexedStats(stats::traced_buf_padding_bytes_cleared, buf_num,
+ static_cast<int64_t>(buf.padding_bytes_cleared()));
+ storage->SetIndexedStats(stats::traced_buf_chunks_written, buf_num,
+ static_cast<int64_t>(buf.chunks_written()));
+ storage->SetIndexedStats(stats::traced_buf_chunks_rewritten, buf_num,
+ static_cast<int64_t>(buf.chunks_rewritten()));
+ storage->SetIndexedStats(stats::traced_buf_chunks_overwritten, buf_num,
+ static_cast<int64_t>(buf.chunks_overwritten()));
+ storage->SetIndexedStats(stats::traced_buf_chunks_discarded, buf_num,
+ static_cast<int64_t>(buf.chunks_discarded()));
+ storage->SetIndexedStats(stats::traced_buf_chunks_read, buf_num,
+ static_cast<int64_t>(buf.chunks_read()));
+ storage->SetIndexedStats(
+ stats::traced_buf_chunks_committed_out_of_order, buf_num,
+ static_cast<int64_t>(buf.chunks_committed_out_of_order()));
+ storage->SetIndexedStats(stats::traced_buf_write_wrap_count, buf_num,
+ static_cast<int64_t>(buf.write_wrap_count()));
+ storage->SetIndexedStats(stats::traced_buf_patches_succeeded, buf_num,
+ static_cast<int64_t>(buf.patches_succeeded()));
+ storage->SetIndexedStats(stats::traced_buf_patches_failed, buf_num,
+ static_cast<int64_t>(buf.patches_failed()));
+ storage->SetIndexedStats(stats::traced_buf_readaheads_succeeded, buf_num,
+ static_cast<int64_t>(buf.readaheads_succeeded()));
+ storage->SetIndexedStats(stats::traced_buf_readaheads_failed, buf_num,
+ static_cast<int64_t>(buf.readaheads_failed()));
+ storage->SetIndexedStats(stats::traced_buf_abi_violations, buf_num,
+ static_cast<int64_t>(buf.abi_violations()));
+ storage->SetIndexedStats(
+ stats::traced_buf_trace_writer_packet_loss, buf_num,
+ static_cast<int64_t>(buf.trace_writer_packet_loss()));
+ }
+}
+
+void ProtoTraceParserImpl::ParseChromeEvents(int64_t ts, ConstBytes blob) {
+ TraceStorage* storage = context_->storage.get();
+ protos::pbzero::ChromeEventBundle::Decoder bundle(blob.data, blob.size);
+ ArgsTracker args(context_);
+ if (bundle.has_metadata()) {
+ RawId id = storage->mutable_raw_table()
+ ->Insert({ts, raw_chrome_metadata_event_id_, 0, 0, 0, 0,
+ context_->machine_id()})
+ .id;
+ auto inserter = args.AddArgsTo(id);
+
+ uint32_t bundle_index =
+ context_->metadata_tracker->IncrementChromeMetadataBundleCount();
+
+ // The legacy untyped metadata is proxied via a special event in the raw
+ // table to JSON export.
+ for (auto it = bundle.metadata(); it; ++it) {
+ protos::pbzero::ChromeMetadata::Decoder metadata(*it);
+ Variadic value;
+ if (metadata.has_string_value()) {
+ value =
+ Variadic::String(storage->InternString(metadata.string_value()));
+ } else if (metadata.has_int_value()) {
+ value = Variadic::Integer(metadata.int_value());
+ } else if (metadata.has_bool_value()) {
+ value = Variadic::Integer(metadata.bool_value());
+ } else if (metadata.has_json_value()) {
+ value = Variadic::Json(storage->InternString(metadata.json_value()));
+ } else {
+ context_->storage->IncrementStats(stats::empty_chrome_metadata);
+ continue;
+ }
+
+ StringId name_id = storage->InternString(metadata.name());
+ args.AddArgsTo(id).AddArg(name_id, value);
+
+ char buffer[2048];
+ base::StringWriter writer(buffer, sizeof(buffer));
+ writer.AppendString("cr-");
+ // If we have data from multiple Chrome instances, append a suffix
+ // to differentiate them.
+ if (bundle_index > 1) {
+ writer.AppendUnsignedInt(bundle_index);
+ writer.AppendChar('-');
+ }
+ writer.AppendString(metadata.name());
+
+ auto metadata_id = storage->InternString(writer.GetStringView());
+ context_->metadata_tracker->SetDynamicMetadata(metadata_id, value);
+ }
+ }
+
+ if (bundle.has_legacy_ftrace_output()) {
+ RawId id = storage->mutable_raw_table()
+ ->Insert({ts, raw_chrome_legacy_system_trace_event_id_, 0, 0,
+ 0, 0, context_->machine_id()})
+ .id;
+
+ std::string data;
+ for (auto it = bundle.legacy_ftrace_output(); it; ++it) {
+ data += (*it).ToStdString();
+ }
+ Variadic value =
+ Variadic::String(storage->InternString(base::StringView(data)));
+ args.AddArgsTo(id).AddArg(data_name_id_, value);
+ }
+
+ if (bundle.has_legacy_json_trace()) {
+ for (auto it = bundle.legacy_json_trace(); it; ++it) {
+ protos::pbzero::ChromeLegacyJsonTrace::Decoder legacy_trace(*it);
+ if (legacy_trace.type() !=
+ protos::pbzero::ChromeLegacyJsonTrace::USER_TRACE) {
+ continue;
+ }
+ RawId id = storage->mutable_raw_table()
+ ->Insert({ts, raw_chrome_legacy_user_trace_event_id_, 0, 0,
+ 0, 0, context_->machine_id()})
+ .id;
+ Variadic value =
+ Variadic::String(storage->InternString(legacy_trace.data()));
+ args.AddArgsTo(id).AddArg(data_name_id_, value);
+ }
+ }
+}
+
+void ProtoTraceParserImpl::ParseMetatraceEvent(int64_t ts, ConstBytes blob) {
+ protos::pbzero::PerfettoMetatrace::Decoder event(blob.data, blob.size);
+ auto utid = context_->process_tracker->GetOrCreateThread(event.thread_id());
+
+ StringId cat_id = metatrace_id_;
+ StringId name_id = kNullStringId;
+
+ for (auto it = event.interned_strings(); it; ++it) {
+ protos::pbzero::PerfettoMetatrace::InternedString::Decoder interned_string(
+ it->data(), it->size());
+ metatrace_interned_strings_.Insert(
+ interned_string.iid(),
+ context_->storage->InternString(interned_string.value()));
+ }
+
+ // This function inserts the args from the proto into the args table.
+ // Args inserted with the same key multiple times are treated as an array:
+ // this function correctly creates the key and flat key for each arg array.
+ auto args_fn = [this, &event](ArgsTracker::BoundInserter* inserter) {
+ using Arg = std::pair<StringId, StringId>;
+
+ // First, get a list of all the args so we can group them by key.
+ std::vector<Arg> interned;
+ for (auto it = event.args(); it; ++it) {
+ protos::pbzero::PerfettoMetatrace::Arg::Decoder arg_proto(*it);
+ StringId key;
+ if (arg_proto.has_key_iid()) {
+ key = GetMetatraceInternedString(arg_proto.key_iid());
+ } else {
+ key = context_->storage->InternString(arg_proto.key());
+ }
+ StringId value;
+ if (arg_proto.has_value_iid()) {
+ value = GetMetatraceInternedString(arg_proto.value_iid());
+ } else {
+ value = context_->storage->InternString(arg_proto.value());
+ }
+ interned.emplace_back(key, value);
+ }
+
+ // We stable sort insted of sorting here to avoid changing the order of the
+ // args in arrays.
+ std::stable_sort(interned.begin(), interned.end(),
+ [](const Arg& a, const Arg& b) {
+ return a.first.raw_id() < b.first.raw_id();
+ });
+
+ // Compute the correct key for each arg, possibly adding an index to
+ // the end of the key if needed.
+ char buffer[2048];
+ uint32_t current_idx = 0;
+ for (auto it = interned.begin(); it != interned.end(); ++it) {
+ auto next = it + 1;
+ StringId key = it->first;
+ StringId next_key = next == interned.end() ? kNullStringId : next->first;
+
+ if (key != next_key && current_idx == 0) {
+ inserter->AddArg(key, Variadic::String(it->second));
+ } else {
+ constexpr size_t kMaxIndexSize = 20;
+ NullTermStringView key_str = context_->storage->GetString(key);
+ if (key_str.size() >= sizeof(buffer) - kMaxIndexSize) {
+ PERFETTO_DLOG("Ignoring arg with unreasonbly large size");
+ continue;
+ }
+
+ base::StackString<2048> array_key("%s[%u]", key_str.c_str(),
+ current_idx);
+ StringId new_key =
+ context_->storage->InternString(array_key.string_view());
+ inserter->AddArg(key, new_key, Variadic::String(it->second));
+
+ current_idx = key == next_key ? current_idx + 1 : 0;
+ }
+ }
+ };
+
+ if (event.has_event_id() || event.has_event_name() ||
+ event.has_event_name_iid()) {
+ if (event.has_event_id()) {
+ auto eid = event.event_id();
+ if (eid < metatrace::EVENTS_MAX) {
+ name_id = context_->storage->InternString(metatrace::kEventNames[eid]);
+ } else {
+ base::StackString<64> fallback("Event %d", eid);
+ name_id = context_->storage->InternString(fallback.string_view());
+ }
+ } else if (event.has_event_name_iid()) {
+ name_id = GetMetatraceInternedString(event.event_name_iid());
+ } else {
+ name_id = context_->storage->InternString(event.event_name());
+ }
+ TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+ context_->slice_tracker->Scoped(
+ ts, track_id, cat_id, name_id,
+ static_cast<int64_t>(event.event_duration_ns()), args_fn);
+ } else if (event.has_counter_id() || event.has_counter_name()) {
+ if (event.has_counter_id()) {
+ auto cid = event.counter_id();
+ if (cid < metatrace::COUNTERS_MAX) {
+ name_id =
+ context_->storage->InternString(metatrace::kCounterNames[cid]);
+ } else {
+ base::StackString<64> fallback("Counter %d", cid);
+ name_id = context_->storage->InternString(fallback.string_view());
+ }
+ } else {
+ name_id = context_->storage->InternString(event.counter_name());
+ }
+ TrackId track =
+ context_->track_tracker->InternThreadCounterTrack(name_id, utid);
+ auto opt_id =
+ context_->event_tracker->PushCounter(ts, event.counter_value(), track);
+ if (opt_id) {
+ auto inserter = context_->args_tracker->AddArgsTo(*opt_id);
+ args_fn(&inserter);
+ }
+ }
+
+ if (event.has_overruns())
+ context_->storage->IncrementStats(stats::metatrace_overruns);
+}
+
+StringId ProtoTraceParserImpl::GetMetatraceInternedString(uint64_t iid) {
+ StringId* maybe_id = metatrace_interned_strings_.Find(iid);
+ if (!maybe_id)
+ return missing_metatrace_interned_string_id_;
+ return *maybe_id;
+}
+
+} // namespace trace_processor
+} // namespace perfetto