Commit 0d929861 authored by Eric Seckler's avatar Eric Seckler Committed by Commit Bot

perfetto: Use TraceProcessor for proto2json conversion

TraceProcessor replaces the existing (unmaintainable) proto2json
converter and adds features like combining of BEGIN/END events
and support for heap profiles.

Also updates a devtools test that was expecting separate begin/end
events where TraceProcessor now combines them into a complete event.

We have reduced the trace procesor's binary size overhead
by selectively excluding features Chrome doesn't need from >400kB to
~50kB. We could probably save a couple more kB if we invest even more
eng time and accept the maintenance burden for this in trace processor,
however, we feel that we've reached an acceptable state that balances
binary size & code health.

Binary-Size: see paragraph above.
Bug: 1006770
Change-Id: Ia7f6b3a784c1a9c40574e1dcd80eca1ca2dfb03f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1746258
Commit-Queue: Eric Seckler <eseckler@chromium.org>
Reviewed-by: default avatarAndrey Kosyakov <caseq@chromium.org>
Reviewed-by: default avataroysteine <oysteine@chromium.org>
Reviewed-by: default avatarSami Kyöstilä <skyostil@chromium.org>
Cr-Commit-Position: refs/heads/master@{#713593}
parent 641234d5
...@@ -16,14 +16,10 @@ source_set("lib") { ...@@ -16,14 +16,10 @@ source_set("lib") {
sources = [ sources = [
"perfetto/consumer_host.cc", "perfetto/consumer_host.cc",
"perfetto/consumer_host.h", "perfetto/consumer_host.h",
"perfetto/json_trace_exporter.cc",
"perfetto/json_trace_exporter.h",
"perfetto/perfetto_service.cc", "perfetto/perfetto_service.cc",
"perfetto/perfetto_service.h", "perfetto/perfetto_service.h",
"perfetto/producer_host.cc", "perfetto/producer_host.cc",
"perfetto/producer_host.h", "perfetto/producer_host.h",
"perfetto/track_event_json_exporter.cc",
"perfetto/track_event_json_exporter.h",
"tracing_service.cc", "tracing_service.cc",
"tracing_service.h", "tracing_service.h",
] ]
...@@ -40,31 +36,40 @@ source_set("lib") { ...@@ -40,31 +36,40 @@ source_set("lib") {
] ]
deps = [ deps = [
"//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite", "//third_party/perfetto:libproto_to_json",
] ]
} }
executable("trace_json_exporter") { source_set("json_trace_exporter") {
sources = [ sources = [
"perfetto/json_exporter_main.cc",
"perfetto/json_trace_exporter.cc", "perfetto/json_trace_exporter.cc",
"perfetto/json_trace_exporter.h", "perfetto/json_trace_exporter.h",
"perfetto/track_event_json_exporter.cc", "perfetto/track_event_json_exporter.cc",
"perfetto/track_event_json_exporter.h", "perfetto/track_event_json_exporter.h",
] ]
configs += [ "//build/config/compiler:rtti" ]
deps = [ deps = [
"//base", "//base",
"//third_party/perfetto:libperfetto", "//third_party/perfetto:libperfetto",
"//third_party/perfetto/include/perfetto/protozero:protozero",
"//third_party/perfetto/protos/perfetto/common:lite", "//third_party/perfetto/protos/perfetto/common:lite",
"//third_party/perfetto/protos/perfetto/trace:lite", "//third_party/perfetto/protos/perfetto/trace:lite",
"//third_party/perfetto/protos/perfetto/trace/chrome:lite", "//third_party/perfetto/protos/perfetto/trace/chrome:lite",
"//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite", "//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite",
"//third_party/perfetto/protos/perfetto/trace/interned_data:lite", "//third_party/perfetto/protos/perfetto/trace/interned_data:lite",
"//third_party/perfetto/protos/perfetto/trace/track_event:lite", "//third_party/perfetto/protos/perfetto/trace/track_event:lite",
]
}
executable("trace_json_exporter") {
sources = [
"perfetto/json_exporter_main.cc",
]
configs += [ "//build/config/compiler:rtti" ]
deps = [
":json_trace_exporter",
"//base",
"//third_party/perfetto:libperfetto",
"//third_party/perfetto/src/protozero:protozero", "//third_party/perfetto/src/protozero:protozero",
] ]
} }
...@@ -145,6 +150,7 @@ source_set("tests") { ...@@ -145,6 +150,7 @@ source_set("tests") {
} }
deps = [ deps = [
":json_trace_exporter",
":lib", ":lib",
":test_utils", ":test_utils",
"//base", "//base",
......
...@@ -24,13 +24,15 @@ ...@@ -24,13 +24,15 @@
#include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/bindings/strong_binding.h" #include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/system/wait.h" #include "mojo/public/cpp/system/wait.h"
#include "services/tracing/perfetto/json_trace_exporter.h"
#include "services/tracing/perfetto/perfetto_service.h" #include "services/tracing/perfetto/perfetto_service.h"
#include "services/tracing/perfetto/track_event_json_exporter.h"
#include "services/tracing/public/cpp/trace_event_args_whitelist.h" #include "services/tracing/public/cpp/trace_event_args_whitelist.h"
#include "third_party/perfetto/include/perfetto/ext/trace_processor/export_json.h"
#include "third_party/perfetto/include/perfetto/ext/tracing/core/observable_events.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/observable_events.h"
#include "third_party/perfetto/include/perfetto/ext/tracing/core/slice.h"
#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_packet.h"
#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_stats.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_stats.h"
#include "third_party/perfetto/include/perfetto/trace_processor/basic_types.h"
#include "third_party/perfetto/include/perfetto/trace_processor/trace_processor_storage.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h" #include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
#include "third_party/perfetto/protos/perfetto/config/trace_config.pb.h" #include "third_party/perfetto/protos/perfetto/config/trace_config.pb.h"
...@@ -40,6 +42,44 @@ namespace { ...@@ -40,6 +42,44 @@ namespace {
const int32_t kEnableTracingTimeoutSeconds = 10; const int32_t kEnableTracingTimeoutSeconds = 10;
class JsonStringOutputWriter
: public perfetto::trace_processor::json::OutputWriter {
public:
using FlushCallback =
base::RepeatingCallback<void(std::string json, bool has_more)>;
JsonStringOutputWriter(FlushCallback flush_callback)
: flush_callback_(std::move(flush_callback)) {
buffer_.reserve(kBufferReserveCapacity);
}
~JsonStringOutputWriter() override {
flush_callback_.Run(std::move(buffer_), false);
}
perfetto::trace_processor::util::Status AppendString(
const std::string& string) override {
buffer_ += string;
if (buffer_.size() > kBufferLimitInBytes) {
flush_callback_.Run(std::move(buffer_), true);
// Reset the buffer_ after moving it above.
buffer_.clear();
buffer_.reserve(kBufferReserveCapacity);
}
return perfetto::trace_processor::util::OkStatus();
}
private:
static constexpr size_t kBufferLimitInBytes = 100 * 1024;
// Since we write each string before checking the limit, we'll always go
// slightly over and hence we reserve some extra space to avoid most
// reallocs.
static constexpr size_t kBufferReserveCapacity = kBufferLimitInBytes * 5 / 4;
FlushCallback flush_callback_;
std::string buffer_;
};
} // namespace } // namespace
class ConsumerHost::StreamWriter { class ConsumerHost::StreamWriter {
...@@ -291,7 +331,7 @@ void ConsumerHost::TracingSession::OnTracingDisabled() { ...@@ -291,7 +331,7 @@ void ConsumerHost::TracingSession::OnTracingDisabled() {
tracing_session_client_->OnTracingDisabled(); tracing_session_client_->OnTracingDisabled();
if (json_trace_exporter_) { if (trace_processor_) {
host_->consumer_endpoint()->ReadBuffers(); host_->consumer_endpoint()->ReadBuffers();
} }
...@@ -343,6 +383,24 @@ void ConsumerHost::TracingSession::DisableTracingAndEmitJson( ...@@ -343,6 +383,24 @@ void ConsumerHost::TracingSession::DisableTracingAndEmitJson(
weak_factory_.GetWeakPtr()), weak_factory_.GetWeakPtr()),
base::SequencedTaskRunnerHandle::Get()); base::SequencedTaskRunnerHandle::Get());
if (privacy_filtering_enabled) {
// For filtering/whitelisting to be possible at JSON export time,
// filtering must not have been enabled during proto emission time
// (or there's nothing to pass through the whitelist).
DCHECK(!privacy_filtering_enabled_);
privacy_filtering_enabled_ = true;
}
json_agent_label_filter_ = agent_label_filter;
trace_processor_ =
perfetto::trace_processor::TraceProcessorStorage::CreateInstance(
perfetto::trace_processor::Config());
DisableTracing();
}
void ConsumerHost::TracingSession::ExportJson() {
// In legacy backend, the trace event agent sets the predicate used by // In legacy backend, the trace event agent sets the predicate used by
// TraceLog. For perfetto backend, ensure that predicate is always set // TraceLog. For perfetto backend, ensure that predicate is always set
// before creating the exporter. The agent can be created later than this // before creating the exporter. The agent can be created later than this
...@@ -356,34 +414,55 @@ void ConsumerHost::TracingSession::DisableTracingAndEmitJson( ...@@ -356,34 +414,55 @@ void ConsumerHost::TracingSession::DisableTracingAndEmitJson(
base::BindRepeating(&IsMetadataWhitelisted)); base::BindRepeating(&IsMetadataWhitelisted));
} }
JSONTraceExporter::ArgumentFilterPredicate arg_filter_predicate; perfetto::trace_processor::json::ArgumentFilterPredicate argument_filter;
JSONTraceExporter::MetadataFilterPredicate metadata_filter_predicate; perfetto::trace_processor::json::MetadataFilterPredicate metadata_filter;
if (privacy_filtering_enabled) { perfetto::trace_processor::json::LabelFilterPredicate label_filter;
// For filtering/whitelisting to be possible at JSON export time,
// filtering must not have been enabled during proto emission time if (privacy_filtering_enabled_) {
// (or there's nothing to pass through the whitelist).
DCHECK(!privacy_filtering_enabled_);
auto* trace_log = base::trace_event::TraceLog::GetInstance(); auto* trace_log = base::trace_event::TraceLog::GetInstance();
arg_filter_predicate = trace_log->GetArgumentFilterPredicate(); base::trace_event::ArgumentFilterPredicate argument_filter_predicate =
metadata_filter_predicate = trace_log->GetMetadataFilterPredicate(); trace_log->GetArgumentFilterPredicate();
argument_filter =
[argument_filter_predicate](
const char* category_group_name, const char* event_name,
perfetto::trace_processor::json::ArgumentNameFilterPredicate*
name_filter) {
base::trace_event::ArgumentNameFilterPredicate name_filter_predicate;
bool result = argument_filter_predicate.Run(
category_group_name, event_name, &name_filter_predicate);
if (name_filter_predicate) {
*name_filter = [name_filter_predicate](const char* arg_name) {
return name_filter_predicate.Run(arg_name);
};
}
return result;
};
base::trace_event::MetadataFilterPredicate metadata_filter_predicate =
trace_log->GetMetadataFilterPredicate();
metadata_filter = [metadata_filter_predicate](const char* metadata_name) {
return metadata_filter_predicate.Run(metadata_name);
};
} }
json_trace_exporter_ = std::make_unique<TrackEventJSONExporter>(
std::move(arg_filter_predicate), std::move(metadata_filter_predicate),
base::BindRepeating(&ConsumerHost::TracingSession::OnJSONTraceData,
base::Unretained(this)));
json_trace_exporter_->set_label_filter(agent_label_filter); if (!json_agent_label_filter_.empty()) {
label_filter = [this](const char* label) {
return strcmp(label, json_agent_label_filter_.c_str()) == 0;
};
}
DisableTracing(); JsonStringOutputWriter output_writer(base::BindRepeating(
&ConsumerHost::TracingSession::OnJSONTraceData, base::Unretained(this)));
auto status = perfetto::trace_processor::json::ExportJson(
trace_processor_.get(), &output_writer, argument_filter, metadata_filter,
label_filter);
DCHECK(status.ok()) << status.message();
} }
void ConsumerHost::TracingSession::OnJSONTraceData( void ConsumerHost::TracingSession::OnJSONTraceData(std::string json,
std::string* json, bool has_more) {
base::DictionaryValue* metadata,
bool has_more) {
auto slices = std::make_unique<StreamWriter::Slices>(); auto slices = std::make_unique<StreamWriter::Slices>();
slices->push_back(std::string()); slices->push_back(std::string());
slices->back().swap(*json); slices->back().swap(json);
read_buffers_stream_writer_.Post(FROM_HERE, &StreamWriter::WriteToStream, read_buffers_stream_writer_.Post(FROM_HERE, &StreamWriter::WriteToStream,
std::move(slices), has_more); std::move(slices), has_more);
...@@ -396,10 +475,40 @@ void ConsumerHost::TracingSession::OnTraceData( ...@@ -396,10 +475,40 @@ void ConsumerHost::TracingSession::OnTraceData(
std::vector<perfetto::TracePacket> packets, std::vector<perfetto::TracePacket> packets,
bool has_more) { bool has_more) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (json_trace_exporter_) { if (trace_processor_) {
json_trace_exporter_->OnTraceData(std::move(packets), has_more); // Calculate space needed for trace chunk. Each packet has a preamble and
// payload size.
size_t max_size = packets.size() * perfetto::TracePacket::kMaxPreambleBytes;
for (const auto& packet : packets) {
max_size += packet.size();
}
// Copy packets into a trace file chunk.
size_t position = 0;
std::unique_ptr<uint8_t[]> data(new uint8_t[max_size]);
for (perfetto::TracePacket& packet : packets) {
char* preamble;
size_t preamble_size;
std::tie(preamble, preamble_size) = packet.GetProtoPreamble();
DCHECK_LT(position + preamble_size, max_size);
memcpy(&data[position], preamble, preamble_size);
position += preamble_size;
for (const perfetto::Slice& slice : packet.slices()) {
DCHECK_LT(position + slice.size, max_size);
memcpy(&data[position], slice.start, slice.size);
position += slice.size;
}
}
auto status = trace_processor_->Parse(std::move(data), position);
// TODO(eseckler): There's no way to propagate this error at the moment - If
// one occurs on production builds, we silently ignore it and will end up
// producing an empty JSON result.
DCHECK(status.ok()) << status.message();
if (!has_more) { if (!has_more) {
json_trace_exporter_.reset(); trace_processor_->NotifyEndOfFile();
ExportJson();
trace_processor_.reset();
} }
return; return;
} }
......
...@@ -23,17 +23,18 @@ ...@@ -23,17 +23,18 @@
#include "third_party/perfetto/include/perfetto/ext/tracing/core/consumer.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/consumer.h"
#include "third_party/perfetto/include/perfetto/ext/tracing/core/tracing_service.h" #include "third_party/perfetto/include/perfetto/ext/tracing/core/tracing_service.h"
namespace base {
class DictionaryValue;
}
namespace service_manager { namespace service_manager {
struct BindSourceInfo; struct BindSourceInfo;
} // namespace service_manager } // namespace service_manager
namespace perfetto {
namespace trace_processor {
class TraceProcessorStorage;
} // namespace trace_processor
} // namespace perfetto
namespace tracing { namespace tracing {
class JSONTraceExporter;
class PerfettoService; class PerfettoService;
// This is a Mojo interface which enables any client // This is a Mojo interface which enables any client
...@@ -89,9 +90,8 @@ class ConsumerHost : public perfetto::Consumer, public mojom::ConsumerHost { ...@@ -89,9 +90,8 @@ class ConsumerHost : public perfetto::Consumer, public mojom::ConsumerHost {
DisableTracingAndEmitJsonCallback callback) override; DisableTracingAndEmitJsonCallback callback) override;
private: private:
void OnJSONTraceData(std::string* json, void ExportJson();
base::DictionaryValue* metadata, void OnJSONTraceData(std::string json, bool has_more);
bool has_more);
void OnEnableTracingTimeout(); void OnEnableTracingTimeout();
void MaybeSendEnableTracingAck(); void MaybeSendEnableTracingAck();
bool IsExpectedPid(base::ProcessId pid) const; bool IsExpectedPid(base::ProcessId pid) const;
...@@ -102,7 +102,9 @@ class ConsumerHost : public perfetto::Consumer, public mojom::ConsumerHost { ...@@ -102,7 +102,9 @@ class ConsumerHost : public perfetto::Consumer, public mojom::ConsumerHost {
bool privacy_filtering_enabled_ = false; bool privacy_filtering_enabled_ = false;
base::SequenceBound<StreamWriter> read_buffers_stream_writer_; base::SequenceBound<StreamWriter> read_buffers_stream_writer_;
RequestBufferUsageCallback request_buffer_usage_callback_; RequestBufferUsageCallback request_buffer_usage_callback_;
std::unique_ptr<JSONTraceExporter> json_trace_exporter_; std::unique_ptr<perfetto::trace_processor::TraceProcessorStorage>
trace_processor_;
std::string json_agent_label_filter_;
base::OnceCallback<void(bool)> flush_callback_; base::OnceCallback<void(bool)> flush_callback_;
const mojom::TracingClientPriority tracing_priority_; const mojom::TracingClientPriority tracing_priority_;
base::OnceClosure on_disabled_callback_; base::OnceClosure on_disabled_callback_;
......
...@@ -21,23 +21,21 @@ ...@@ -21,23 +21,21 @@
await tracingHelper.invokeAsyncWithTracing(performActions); await tracingHelper.invokeAsyncWithTracing(performActions);
var schedRecalc = tracingHelper.findEvent('ScheduleStyleRecalculation', 'I'); var schedRecalc = tracingHelper.findEvent('ScheduleStyleRecalculation', 'I');
var recalcBegin = tracingHelper.findEvent('UpdateLayoutTree', 'B'); var recalc = tracingHelper.findEvent('UpdateLayoutTree', 'X');
var recalcEnd = tracingHelper.findEvent('UpdateLayoutTree', 'E'); testRunner.log('UpdateLayoutTree frames match: ' + (schedRecalc.args.data.frame === recalc.args.beginData.frame));
testRunner.log('UpdateLayoutTree frames match: ' + (schedRecalc.args.data.frame === recalcBegin.args.beginData.frame)); testRunner.log('UpdateLayoutTree elementCount > 0: ' + (recalc.args.elementCount > 0));
testRunner.log('UpdateLayoutTree elementCount > 0: ' + (recalcEnd.args.elementCount > 0));
var invalidate = tracingHelper.findEvent('InvalidateLayout', 'I'); var invalidate = tracingHelper.findEvent('InvalidateLayout', 'I');
var layoutBegin = tracingHelper.findEvent('Layout', 'B'); var layout = tracingHelper.findEvent('Layout', 'X');
var layoutEnd = tracingHelper.findEvent('Layout', 'E');
testRunner.log('InvalidateLayout frames match: ' + (recalcBegin.args.beginData.frame === invalidate.args.data.frame)); testRunner.log('InvalidateLayout frames match: ' + (recalc.args.beginData.frame === invalidate.args.data.frame));
var beginData = layoutBegin.args.beginData; var beginData = layout.args.beginData;
testRunner.log('Layout frames match: ' + (invalidate.args.data.frame === beginData.frame)); testRunner.log('Layout frames match: ' + (invalidate.args.data.frame === beginData.frame));
testRunner.log('dirtyObjects > 0: ' + (beginData.dirtyObjects > 0)); testRunner.log('dirtyObjects > 0: ' + (beginData.dirtyObjects > 0));
testRunner.log('totalObjects > 0: ' + (beginData.totalObjects > 0)); testRunner.log('totalObjects > 0: ' + (beginData.totalObjects > 0));
var endData = layoutEnd.args.endData; var endData = layout.args.endData;
testRunner.log('has rootNode id: ' + (endData.rootNode > 0)); testRunner.log('has rootNode id: ' + (endData.rootNode > 0));
testRunner.log('has root quad: ' + !!endData.root); testRunner.log('has root quad: ' + !!endData.root);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment