diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-07-31 15:50:41 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-08-30 12:35:23 +0000 |
commit | 7b2ffa587235a47d4094787d72f38102089f402a (patch) | |
tree | 30e82af9cbab08a7fa028bb18f4f2987a3f74dfa /chromium/services/tracing | |
parent | d94af01c90575348c4e81a418257f254b6f8d225 (diff) | |
download | qtwebengine-chromium-7b2ffa587235a47d4094787d72f38102089f402a.tar.gz |
BASELINE: Update Chromium to 76.0.3809.94
Change-Id: I321c3f5f929c105aec0f98c5091ef6108822e647
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/services/tracing')
57 files changed, 2314 insertions, 1017 deletions
diff --git a/chromium/services/tracing/BUILD.gn b/chromium/services/tracing/BUILD.gn index f03baabd789..e0c20c7c3cc 100644 --- a/chromium/services/tracing/BUILD.gn +++ b/chromium/services/tracing/BUILD.gn @@ -45,6 +45,31 @@ source_set("lib") { ] } +executable("trace_json_exporter") { + sources = [ + "perfetto/json_exporter_main.cc", + "perfetto/json_trace_exporter.cc", + "perfetto/json_trace_exporter.h", + "perfetto/track_event_json_exporter.cc", + "perfetto/track_event_json_exporter.h", + ] + + configs += [ "//build/config/compiler:rtti" ] + + deps = [ + "//base", + "//third_party/perfetto:libperfetto", + "//third_party/perfetto/include/perfetto/protozero:protozero", + "//third_party/perfetto/protos/perfetto/common:lite", + "//third_party/perfetto/protos/perfetto/trace: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/interned_data:lite", + "//third_party/perfetto/protos/perfetto/trace/track_event:lite", + "//third_party/perfetto/src/protozero:protozero", + ] +} + source_set("manifest") { sources = [ "manifest.cc", @@ -54,10 +79,27 @@ source_set("manifest") { deps = [ "//base", "//services/service_manager/public/cpp", + "//services/tracing/public/cpp", "//services/tracing/public/mojom", ] } +source_set("privacy_check") { + testonly = true + + sources = [ + "perfetto/privacy_filtered_fields-inl.h", + "perfetto/privacy_filtering_check.cc", + "perfetto/privacy_filtering_check.h", + ] + + deps = [ + "//base", + "//third_party/perfetto:libperfetto", + "//third_party/perfetto/src/protozero:protozero", + ] +} + source_set("tests") { testonly = true diff --git a/chromium/services/tracing/coordinator.cc b/chromium/services/tracing/coordinator.cc index fecd79da67c..9c8c296549d 100644 --- a/chromium/services/tracing/coordinator.cc +++ b/chromium/services/tracing/coordinator.cc @@ -12,7 +12,6 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback_forward.h" -#include "base/callback_helpers.h" #include "base/guid.h" #include "base/json/json_writer.h" #include "base/json/string_escape.h" @@ -304,13 +303,13 @@ void Coordinator::Reset() { start_tracing_callback_timer_.Stop(); if (!stop_and_flush_callback_.is_null()) { - base::ResetAndReturn(&stop_and_flush_callback_) + std::move(stop_and_flush_callback_) .Run(base::Value(base::Value::Type::DICTIONARY)); } if (!start_tracing_callback_.is_null()) - base::ResetAndReturn(&start_tracing_callback_).Run(false); + std::move(start_tracing_callback_).Run(false); if (!request_buffer_usage_callback_.is_null()) - base::ResetAndReturn(&request_buffer_usage_callback_).Run(false, 0, 0); + std::move(request_buffer_usage_callback_).Run(false, 0, 0); if (trace_streamer_) { // We are in the middle of flushing trace data. We need to diff --git a/chromium/services/tracing/manifest.cc b/chromium/services/tracing/manifest.cc index 2a413d68017..ef733e8e894 100644 --- a/chromium/services/tracing/manifest.cc +++ b/chromium/services/tracing/manifest.cc @@ -4,8 +4,10 @@ #include "services/tracing/manifest.h" +#include "base/feature_list.h" #include "base/no_destructor.h" #include "services/service_manager/public/cpp/manifest_builder.h" +#include "services/tracing/public/cpp/tracing_features.h" #include "services/tracing/public/mojom/constants.mojom.h" #include "services/tracing/public/mojom/perfetto_service.mojom.h" #include "services/tracing/public/mojom/traced_process.mojom.h" @@ -13,12 +15,23 @@ namespace tracing { +namespace { + +service_manager::Manifest::ExecutionMode GetTracingExecutionMode() { + return base::FeatureList::IsEnabled(features::kTracingServiceInProcess) + ? service_manager::Manifest::ExecutionMode::kInProcessBuiltin + : service_manager::Manifest::ExecutionMode::kOutOfProcessBuiltin; +} + +} // namespace + const service_manager::Manifest& GetManifest() { static base::NoDestructor<service_manager::Manifest> manifest{ service_manager::ManifestBuilder() .WithServiceName(mojom::kServiceName) .WithDisplayName("Tracing") .WithOptions(service_manager::ManifestOptionsBuilder() + .WithExecutionMode(GetTracingExecutionMode()) .WithInstanceSharingPolicy( service_manager::Manifest:: InstanceSharingPolicy::kSingleton) diff --git a/chromium/services/tracing/perfetto/consumer_host.cc b/chromium/services/tracing/perfetto/consumer_host.cc index 1726aeeb080..a8b515f30a3 100644 --- a/chromium/services/tracing/perfetto/consumer_host.cc +++ b/chromium/services/tracing/perfetto/consumer_host.cc @@ -16,12 +16,14 @@ #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" +#include "base/task/post_task.h" #include "build/build_config.h" #include "mojo/public/cpp/bindings/strong_binding.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/track_event_json_exporter.h" +#include "services/tracing/public/cpp/trace_event_args_whitelist.h" #include "third_party/perfetto/include/perfetto/tracing/core/observable_events.h" #include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h" #include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h" @@ -34,62 +36,113 @@ namespace { const int32_t kEnableTracingTimeoutSeconds = 10; -bool StringToProcessId(const std::string& input, base::ProcessId* output) { - // Pid is encoded as uint in the string. - return base::StringToUint(input, reinterpret_cast<uint32_t*>(output)); +perfetto::TraceConfig AdjustTraceConfig( + const perfetto::TraceConfig& trace_config) { + perfetto::TraceConfig trace_config_copy(trace_config); + // Clock snapshotting is incompatible with chrome's process sandboxing. + // Telemetry uses its own way of snapshotting clocks anyway. + auto* builtin_data_sources = trace_config_copy.mutable_builtin_data_sources(); + builtin_data_sources->set_disable_clock_snapshotting(true); + return trace_config_copy; } } // namespace -// static -bool ConsumerHost::ParsePidFromProducerName(const std::string& producer_name, - base::ProcessId* pid) { - if (!base::StartsWith(producer_name, mojom::kPerfettoProducerNamePrefix, - base::CompareCase::SENSITIVE)) { - LOG(DFATAL) << "Unexpected producer name: " << producer_name; - return false; - } +class ConsumerHost::StreamWriter { + public: + using Slices = std::vector<std::string>; - static const size_t kPrefixLength = - strlen(mojom::kPerfettoProducerNamePrefix); - if (!StringToProcessId(producer_name.substr(kPrefixLength), pid)) { - LOG(DFATAL) << "Unexpected producer name: " << producer_name; - return false; + static scoped_refptr<base::SequencedTaskRunner> CreateTaskRunner() { + return base::CreateSequencedTaskRunnerWithTraits( + {base::WithBaseSyncPrimitives(), base::TaskPriority::BEST_EFFORT}); } - return true; -} - -// static -void ConsumerHost::BindConsumerRequest( - PerfettoService* service, - mojom::ConsumerHostRequest request, - const service_manager::BindSourceInfo& source_info) { - mojo::MakeStrongBinding(std::make_unique<ConsumerHost>(service), - std::move(request)); -} -ConsumerHost::ConsumerHost(PerfettoService* service) - : service_(service), weak_factory_(this) { - DETACH_FROM_SEQUENCE(sequence_checker_); - consumer_endpoint_ = - service_->GetService()->ConnectConsumer(this, 0 /*uid_t*/); - consumer_endpoint_->ObserveEvents( - perfetto::TracingService::ConsumerEndpoint::ObservableEventType:: - kDataSourceInstances); - service_->RegisterConsumerHost(this); -} + StreamWriter(mojo::ScopedDataPipeProducerHandle stream, + TracingSession::ReadBuffersCallback callback, + base::OnceClosure disconnect_callback, + scoped_refptr<base::SequencedTaskRunner> callback_task_runner) + : stream_(std::move(stream)), + read_buffers_callback_(std::move(callback)), + disconnect_callback_(std::move(disconnect_callback)), + callback_task_runner_(callback_task_runner) {} + + void WriteToStream(std::unique_ptr<Slices> slices, bool has_more) { + DCHECK(stream_.is_valid()); + for (const auto& slice : *slices) { + uint32_t write_position = 0; + + while (write_position < slice.size()) { + uint32_t write_bytes = slice.size() - write_position; + + MojoResult result = + stream_->WriteData(slice.data() + write_position, &write_bytes, + MOJO_WRITE_DATA_FLAG_NONE); + + if (result == MOJO_RESULT_OK) { + write_position += write_bytes; + continue; + } -ConsumerHost::~ConsumerHost() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - service_->UnregisterConsumerHost(this); -} + if (result == MOJO_RESULT_SHOULD_WAIT) { + result = mojo::Wait(stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE); + } -void ConsumerHost::EnableTracing(mojom::TracingSessionPtr tracing_session, - const perfetto::TraceConfig& trace_config) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (result != MOJO_RESULT_OK) { + if (!disconnect_callback_.is_null()) { + callback_task_runner_->PostTask(FROM_HERE, + std::move(disconnect_callback_)); + } + return; + } + } + } - tracing_session_ = std::move(tracing_session); + if (!has_more && !read_buffers_callback_.is_null()) { + callback_task_runner_->PostTask(FROM_HERE, + std::move(read_buffers_callback_)); + } + } + private: + mojo::ScopedDataPipeProducerHandle stream_; + TracingSession::ReadBuffersCallback read_buffers_callback_; + base::OnceClosure disconnect_callback_; + scoped_refptr<base::SequencedTaskRunner> callback_task_runner_; + + DISALLOW_COPY_AND_ASSIGN(StreamWriter); +}; + +ConsumerHost::TracingSession::TracingSession( + ConsumerHost* host, + mojom::TracingSessionHostRequest tracing_session_host, + mojom::TracingSessionClientPtr tracing_session_client, + const perfetto::TraceConfig& trace_config, + mojom::TracingClientPriority priority) + : host_(host), + tracing_session_client_(std::move(tracing_session_client)), + binding_(this, std::move(tracing_session_host)), + tracing_priority_(priority) { + host_->service()->RegisterTracingSession(this); + + tracing_session_client_.set_connection_error_handler(base::BindOnce( + &ConsumerHost::DestructTracingSession, base::Unretained(host))); + binding_.set_connection_error_handler(base::BindOnce( + &ConsumerHost::DestructTracingSession, base::Unretained(host))); + + privacy_filtering_enabled_ = false; + for (const auto& data_source : trace_config.data_sources()) { + if (data_source.config().chrome_config().privacy_filtering_enabled()) { + privacy_filtering_enabled_ = true; + } + } +#if DCHECK_IS_ON() + if (privacy_filtering_enabled_) { + // If enabled, filtering must be enabled for all data sources. + for (const auto& data_source : trace_config.data_sources()) { + DCHECK(data_source.config().chrome_config().privacy_filtering_enabled()); + } + } +#endif perfetto::TraceConfig trace_config_copy = AdjustTraceConfig(trace_config); filtered_pids_.clear(); @@ -97,7 +150,7 @@ void ConsumerHost::EnableTracing(mojom::TracingSessionPtr tracing_session, if (ds_config.config().name() == mojom::kTraceEventDataSourceName) { for (const auto& filter : ds_config.producer_name_filter()) { base::ProcessId pid; - if (ParsePidFromProducerName(filter, &pid)) { + if (PerfettoService::ParsePidFromProducerName(filter, &pid)) { filtered_pids_.insert(pid); } } @@ -105,11 +158,11 @@ void ConsumerHost::EnableTracing(mojom::TracingSessionPtr tracing_session, } } - pending_enable_tracing_ack_pids_ = service_->active_service_pids(); + pending_enable_tracing_ack_pids_ = host_->service()->active_service_pids(); base::EraseIf(*pending_enable_tracing_ack_pids_, [this](base::ProcessId pid) { return !IsExpectedPid(pid); }); - consumer_endpoint_->EnableTracing(trace_config_copy); + host_->consumer_endpoint()->EnableTracing(trace_config_copy); MaybeSendEnableTracingAck(); if (pending_enable_tracing_ack_pids_) { @@ -119,148 +172,19 @@ void ConsumerHost::EnableTracing(mojom::TracingSessionPtr tracing_session, // case. enable_tracing_ack_timer_.Start( FROM_HERE, base::TimeDelta::FromSeconds(kEnableTracingTimeoutSeconds), - this, &ConsumerHost::OnEnableTracingTimeout); + this, &ConsumerHost::TracingSession::OnEnableTracingTimeout); } } -void ConsumerHost::ChangeTraceConfig( - const perfetto::TraceConfig& trace_config) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - perfetto::TraceConfig trace_config_copy = AdjustTraceConfig(trace_config); - consumer_endpoint_->ChangeTraceConfig(trace_config_copy); -} - -perfetto::TraceConfig ConsumerHost::AdjustTraceConfig( - const perfetto::TraceConfig& trace_config) { - perfetto::TraceConfig trace_config_copy(trace_config); - // Clock snapshotting is incompatible with chrome's process sandboxing. - // Telemetry uses its own way of snapshotting clocks anyway. - trace_config_copy.set_disable_clock_snapshotting(true); - return trace_config_copy; -} - -void ConsumerHost::DisableTracing() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - consumer_endpoint_->DisableTracing(); -} - -void ConsumerHost::Flush(uint32_t timeout, - base::OnceCallback<void(bool)> callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - flush_callback_ = std::move(callback); - base::WeakPtr<ConsumerHost> weak_this = weak_factory_.GetWeakPtr(); - consumer_endpoint_->Flush(timeout, [weak_this](bool success) { - if (!weak_this) { - return; - } - - if (weak_this->flush_callback_) { - std::move(weak_this->flush_callback_).Run(success); - } - }); -} - -void ConsumerHost::ReadBuffers(mojo::ScopedDataPipeProducerHandle stream, - ReadBuffersCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - read_buffers_stream_ = std::move(stream); - read_buffers_callback_ = std::move(callback); - - consumer_endpoint_->ReadBuffers(); -} - -void ConsumerHost::DisableTracingAndEmitJson( - const std::string& agent_label_filter, - mojo::ScopedDataPipeProducerHandle stream, - DisableTracingAndEmitJsonCallback callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!read_buffers_stream_ && !read_buffers_callback_ && - !json_trace_exporter_); - - read_buffers_stream_ = std::move(stream); - read_buffers_callback_ = std::move(callback); - - // TODO(eseckler): Support argument/metadata filtering. - json_trace_exporter_ = std::make_unique<TrackEventJSONExporter>( - JSONTraceExporter::ArgumentFilterPredicate(), - JSONTraceExporter::MetadataFilterPredicate(), - base::BindRepeating(&ConsumerHost::OnJSONTraceData, - base::Unretained(this))); - - json_trace_exporter_->set_label_filter(agent_label_filter); - - consumer_endpoint_->DisableTracing(); -} - -void ConsumerHost::FreeBuffers() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - consumer_endpoint_->FreeBuffers(); -} - -void ConsumerHost::RequestBufferUsage(RequestBufferUsageCallback callback) { - if (!request_buffer_usage_callback_.is_null()) { - std::move(callback).Run(false, 0); - return; - } - - request_buffer_usage_callback_ = std::move(callback); - consumer_endpoint_->GetTraceStats(); -} - -void ConsumerHost::OnConnect() {} - -void ConsumerHost::OnDisconnect() {} - -void ConsumerHost::OnTracingDisabled() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(tracing_session_); - - if (enable_tracing_ack_timer_.IsRunning()) { - enable_tracing_ack_timer_.FireNow(); - } - DCHECK(!pending_enable_tracing_ack_pids_); - - tracing_session_.reset(); - - if (json_trace_exporter_) { - consumer_endpoint_->ReadBuffers(); +ConsumerHost::TracingSession::~TracingSession() { + host_->service()->UnregisterTracingSession(this); + if (host_->consumer_endpoint()) { + host_->consumer_endpoint()->FreeBuffers(); } } -void ConsumerHost::OnTraceData(std::vector<perfetto::TracePacket> packets, - bool has_more) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (json_trace_exporter_) { - json_trace_exporter_->OnTraceData(std::move(packets), has_more); - if (!has_more) { - json_trace_exporter_.reset(); - } - return; - } - - for (auto& packet : packets) { - char* data; - size_t size; - std::tie(data, size) = packet.GetProtoPreamble(); - WriteToStream(data, size); - auto& slices = packet.slices(); - for (auto& slice : slices) { - WriteToStream(slice.start, slice.size); - } - } - - if (!has_more) { - read_buffers_stream_.reset(); - if (read_buffers_callback_) { - std::move(read_buffers_callback_).Run(); - } - } -} - -void ConsumerHost::OnObservableEvents( +void ConsumerHost::TracingSession::OnPerfettoEvents( const perfetto::ObservableEvents& events) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!pending_enable_tracing_ack_pids_) { return; } @@ -278,7 +202,8 @@ void ConsumerHost::OnObservableEvents( // Attempt to parse the PID out of the producer name. base::ProcessId pid; - if (!ParsePidFromProducerName(state_change.producer_name(), &pid)) { + if (!PerfettoService::ParsePidFromProducerName(state_change.producer_name(), + &pid)) { continue; } @@ -287,14 +212,16 @@ void ConsumerHost::OnObservableEvents( MaybeSendEnableTracingAck(); } -void ConsumerHost::OnActiveServicePidAdded(base::ProcessId pid) { +void ConsumerHost::TracingSession::OnActiveServicePidAdded( + base::ProcessId pid) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (pending_enable_tracing_ack_pids_ && IsExpectedPid(pid)) { pending_enable_tracing_ack_pids_->insert(pid); } } -void ConsumerHost::OnActiveServicePidRemoved(base::ProcessId pid) { +void ConsumerHost::TracingSession::OnActiveServicePidRemoved( + base::ProcessId pid) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (pending_enable_tracing_ack_pids_) { pending_enable_tracing_ack_pids_->erase(pid); @@ -302,41 +229,205 @@ void ConsumerHost::OnActiveServicePidRemoved(base::ProcessId pid) { } } -void ConsumerHost::OnActiveServicePidsInitialized() { +void ConsumerHost::TracingSession::OnActiveServicePidsInitialized() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); MaybeSendEnableTracingAck(); } -void ConsumerHost::OnEnableTracingTimeout() { +void ConsumerHost::TracingSession::RequestDisableTracing( + base::OnceClosure on_disabled_callback) { + DCHECK(!on_disabled_callback_); + on_disabled_callback_ = std::move(on_disabled_callback); + DisableTracing(); +} + +void ConsumerHost::TracingSession::OnEnableTracingTimeout() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!pending_enable_tracing_ack_pids_) { return; } - DCHECK(tracing_session_); - tracing_session_->OnTracingEnabled(); + + std::stringstream error; + error << "Timed out waiting for processes to ack BeginTracing: "; + for (auto pid : *pending_enable_tracing_ack_pids_) { + error << pid << " "; + } + LOG(ERROR) << error.rdbuf(); + + DCHECK(tracing_session_client_); + tracing_session_client_->OnTracingEnabled(); pending_enable_tracing_ack_pids_.reset(); } -void ConsumerHost::MaybeSendEnableTracingAck() { +void ConsumerHost::TracingSession::MaybeSendEnableTracingAck() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!pending_enable_tracing_ack_pids_ || !pending_enable_tracing_ack_pids_->empty() || - !service_->active_service_pids_initialized()) { + !host_->service()->active_service_pids_initialized()) { return; } - DCHECK(tracing_session_); - tracing_session_->OnTracingEnabled(); + DCHECK(tracing_session_client_); + tracing_session_client_->OnTracingEnabled(); pending_enable_tracing_ack_pids_.reset(); enable_tracing_ack_timer_.Stop(); } -bool ConsumerHost::IsExpectedPid(base::ProcessId pid) const { +bool ConsumerHost::TracingSession::IsExpectedPid(base::ProcessId pid) const { return filtered_pids_.empty() || base::ContainsKey(filtered_pids_, pid); } -void ConsumerHost::OnTraceStats(bool success, - const perfetto::TraceStats& stats) { +void ConsumerHost::TracingSession::ChangeTraceConfig( + const perfetto::TraceConfig& trace_config) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + perfetto::TraceConfig trace_config_copy = AdjustTraceConfig(trace_config); + host_->consumer_endpoint()->ChangeTraceConfig(trace_config_copy); +} + +void ConsumerHost::TracingSession::DisableTracing() { + host_->consumer_endpoint()->DisableTracing(); +} + +void ConsumerHost::TracingSession::OnTracingDisabled() { + DCHECK(tracing_session_client_); + + if (enable_tracing_ack_timer_.IsRunning()) { + enable_tracing_ack_timer_.FireNow(); + } + DCHECK(!pending_enable_tracing_ack_pids_); + + tracing_session_client_->OnTracingDisabled(); + + if (json_trace_exporter_) { + host_->consumer_endpoint()->ReadBuffers(); + } + + tracing_enabled_ = false; + + if (on_disabled_callback_) { + std::move(on_disabled_callback_).Run(); + } +} + +void ConsumerHost::TracingSession::OnConsumerClientDisconnected() { + // The TracingSession will be deleted after this point. + host_->DestructTracingSession(); +} + +void ConsumerHost::TracingSession::ReadBuffers( + mojo::ScopedDataPipeProducerHandle stream, + ReadBuffersCallback callback) { + read_buffers_stream_writer_ = base::SequenceBound<StreamWriter>( + StreamWriter::CreateTaskRunner(), std::move(stream), std::move(callback), + base::BindOnce(&TracingSession::OnConsumerClientDisconnected, + weak_factory_.GetWeakPtr()), + base::SequencedTaskRunnerHandle::Get()); + + host_->consumer_endpoint()->ReadBuffers(); +} + +void ConsumerHost::TracingSession::RequestBufferUsage( + RequestBufferUsageCallback callback) { + if (!request_buffer_usage_callback_.is_null()) { + std::move(callback).Run(false, 0); + return; + } + + request_buffer_usage_callback_ = std::move(callback); + host_->consumer_endpoint()->GetTraceStats(); +} + +void ConsumerHost::TracingSession::DisableTracingAndEmitJson( + const std::string& agent_label_filter, + mojo::ScopedDataPipeProducerHandle stream, + DisableTracingAndEmitJsonCallback callback) { + DCHECK(!read_buffers_stream_writer_); + + read_buffers_stream_writer_ = base::SequenceBound<StreamWriter>( + StreamWriter::CreateTaskRunner(), std::move(stream), std::move(callback), + base::BindOnce(&TracingSession::OnConsumerClientDisconnected, + weak_factory_.GetWeakPtr()), + base::SequencedTaskRunnerHandle::Get()); + + // In legacy backend, the trace event agent sets the predicate used by + // TraceLog. For perfetto backend, ensure that predicate is always set + // before creating the exporter. The agent can be created later than this + // point. + if (base::trace_event::TraceLog::GetInstance() + ->GetArgumentFilterPredicate() + .is_null()) { + base::trace_event::TraceLog::GetInstance()->SetArgumentFilterPredicate( + base::BindRepeating(&IsTraceEventArgsWhitelisted)); + base::trace_event::TraceLog::GetInstance()->SetMetadataFilterPredicate( + base::BindRepeating(&IsMetadataWhitelisted)); + } + + JSONTraceExporter::ArgumentFilterPredicate arg_filter_predicate; + JSONTraceExporter::MetadataFilterPredicate metadata_filter_predicate; + if (privacy_filtering_enabled_) { + auto* trace_log = base::trace_event::TraceLog::GetInstance(); + arg_filter_predicate = trace_log->GetArgumentFilterPredicate(); + metadata_filter_predicate = trace_log->GetMetadataFilterPredicate(); + } + 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); + + DisableTracing(); +} + +void ConsumerHost::TracingSession::OnJSONTraceData( + std::string* json, + base::DictionaryValue* metadata, + bool has_more) { + auto slices = std::make_unique<StreamWriter::Slices>(); + slices->push_back(std::string()); + slices->back().swap(*json); + read_buffers_stream_writer_.Post(FROM_HERE, &StreamWriter::WriteToStream, + std::move(slices), has_more); + + if (!has_more) { + read_buffers_stream_writer_.Reset(); + } +} + +void ConsumerHost::TracingSession::OnTraceData( + std::vector<perfetto::TracePacket> packets, + bool has_more) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (json_trace_exporter_) { + json_trace_exporter_->OnTraceData(std::move(packets), has_more); + if (!has_more) { + json_trace_exporter_.reset(); + } + return; + } + + auto copy = std::make_unique<StreamWriter::Slices>(); + for (auto& packet : packets) { + char* data; + size_t size; + std::tie(data, size) = packet.GetProtoPreamble(); + copy->emplace_back(data, size); + auto& slices = packet.slices(); + for (auto& slice : slices) { + copy->emplace_back(static_cast<const char*>(slice.start), slice.size); + } + } + read_buffers_stream_writer_.Post(FROM_HERE, &StreamWriter::WriteToStream, + std::move(copy), has_more); + if (!has_more) { + read_buffers_stream_writer_.Reset(); + } +} + +void ConsumerHost::TracingSession::OnTraceStats( + bool success, + const perfetto::TraceStats& stats) { if (!request_buffer_usage_callback_) { return; } @@ -357,54 +448,116 @@ void ConsumerHost::OnTraceStats(bool success, std::move(request_buffer_usage_callback_).Run(true, percent_full); } -void ConsumerHost::OnJSONTraceData(const std::string& json, - base::DictionaryValue* metadata, - bool has_more) { - WriteToStream(json.data(), json.size()); +void ConsumerHost::TracingSession::Flush( + uint32_t timeout, + base::OnceCallback<void(bool)> callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + flush_callback_ = std::move(callback); + base::WeakPtr<TracingSession> weak_this = weak_factory_.GetWeakPtr(); + host_->consumer_endpoint()->Flush(timeout, [weak_this](bool success) { + if (!weak_this) { + return; + } - if (has_more) { - return; - } + if (weak_this->flush_callback_) { + std::move(weak_this->flush_callback_).Run(success); + } + }); +} - read_buffers_stream_.reset(); - if (read_buffers_callback_) { - std::move(read_buffers_callback_).Run(); - } +// static +void ConsumerHost::BindConsumerRequest( + PerfettoService* service, + mojom::ConsumerHostRequest request, + const service_manager::BindSourceInfo& source_info) { + mojo::MakeStrongBinding(std::make_unique<ConsumerHost>(service), + std::move(request)); +} + +ConsumerHost::ConsumerHost(PerfettoService* service) : service_(service) { + DETACH_FROM_SEQUENCE(sequence_checker_); + consumer_endpoint_ = + service_->GetService()->ConnectConsumer(this, 0 /*uid_t*/); + consumer_endpoint_->ObserveEvents( + perfetto::TracingService::ConsumerEndpoint::ObservableEventType:: + kDataSourceInstances); } -void ConsumerHost::WriteToStream(const void* start, size_t size) { - TRACE_EVENT0("ipc", "ConsumerHost::WriteToStream"); - DCHECK(read_buffers_stream_.is_valid()); - uint32_t write_position = 0; +ConsumerHost::~ConsumerHost() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // Make sure the tracing_session is destroyed first, as it keeps a pointer to + // the ConsumerHost parent and accesses it on destruction. + tracing_session_.reset(); +} - while (write_position < size) { - uint32_t write_bytes = size - write_position; +void ConsumerHost::EnableTracing( + mojom::TracingSessionHostRequest tracing_session_host, + mojom::TracingSessionClientPtr tracing_session_client, + const perfetto::TraceConfig& trace_config, + mojom::TracingClientPriority priority) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!tracing_session_); + + // We create our new TracingSession async, if the PerfettoService allows + // us to, after it's stopped any currently running lower or equal priority + // tracing sessions. + service_->RequestTracingSession( + priority, + base::BindOnce( + [](base::WeakPtr<ConsumerHost> weak_this, + mojom::TracingSessionHostRequest tracing_session_host, + mojom::TracingSessionClientPtr tracing_session_client, + const perfetto::TraceConfig& trace_config, + mojom::TracingClientPriority priority) { + if (!weak_this) { + return; + } + + weak_this->tracing_session_ = std::make_unique<TracingSession>( + weak_this.get(), std::move(tracing_session_host), + std::move(tracing_session_client), trace_config, priority); + }, + weak_factory_.GetWeakPtr(), std::move(tracing_session_host), + std::move(tracing_session_client), trace_config, priority)); +} - MojoResult result = read_buffers_stream_->WriteData( - static_cast<const uint8_t*>(start) + write_position, &write_bytes, - MOJO_WRITE_DATA_FLAG_NONE); +void ConsumerHost::OnConnect() {} - if (result == MOJO_RESULT_OK) { - write_position += write_bytes; - continue; - } +void ConsumerHost::OnDisconnect() {} - if (result == MOJO_RESULT_SHOULD_WAIT) { - // TODO(oysteine): If we end up actually blocking here it means - // the client is consuming data slower than Perfetto is producing - // it. Consider other solutions at that point because it means - // eventually Producers will run out of chunks and will stall waiting - // for new ones. - result = - mojo::Wait(read_buffers_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE); - } +void ConsumerHost::OnTracingDisabled() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (tracing_session_) { + tracing_session_->OnTracingDisabled(); + } +} - if (result != MOJO_RESULT_OK) { - // Bail out; destination handle got closed. - consumer_endpoint_->FreeBuffers(); - return; - } +void ConsumerHost::OnTraceData(std::vector<perfetto::TracePacket> packets, + bool has_more) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (tracing_session_) { + tracing_session_->OnTraceData(std::move(packets), has_more); } } +void ConsumerHost::OnObservableEvents( + const perfetto::ObservableEvents& events) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (tracing_session_) { + tracing_session_->OnPerfettoEvents(events); + } +} + +void ConsumerHost::OnTraceStats(bool success, + const perfetto::TraceStats& stats) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (tracing_session_) { + tracing_session_->OnTraceStats(success, stats); + } +} + +void ConsumerHost::DestructTracingSession() { + tracing_session_.reset(); +} + } // namespace tracing diff --git a/chromium/services/tracing/perfetto/consumer_host.h b/chromium/services/tracing/perfetto/consumer_host.h index d88d8a814ba..afb26770275 100644 --- a/chromium/services/tracing/perfetto/consumer_host.h +++ b/chromium/services/tracing/perfetto/consumer_host.h @@ -12,6 +12,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/sequence_checker.h" +#include "base/threading/sequence_bound.h" #include "base/timer/timer.h" #include "mojo/public/cpp/bindings/binding.h" #include "services/tracing/public/mojom/perfetto_service.mojom.h" @@ -36,29 +37,94 @@ class ConsumerHost : public perfetto::Consumer, public mojom::ConsumerHost { mojom::ConsumerHostRequest request, const service_manager::BindSourceInfo& source_info); - static bool ParsePidFromProducerName(const std::string& producer_name, - base::ProcessId* pid); + class StreamWriter; + class TracingSession : public mojom::TracingSessionHost { + public: + TracingSession(ConsumerHost* host, + mojom::TracingSessionHostRequest tracing_session_host, + mojom::TracingSessionClientPtr tracing_session_client, + const perfetto::TraceConfig& trace_config, + mojom::TracingClientPriority priority); + ~TracingSession() override; + + void OnPerfettoEvents(const perfetto::ObservableEvents&); + void OnTraceData(std::vector<perfetto::TracePacket> packets, bool has_more); + void OnTraceStats(bool success, const perfetto::TraceStats&); + void OnTracingDisabled(); + void OnConsumerClientDisconnected(); + void Flush(uint32_t timeout, base::OnceCallback<void(bool)> callback); + + mojom::TracingClientPriority tracing_priority() const { + return tracing_priority_; + } + bool tracing_enabled() const { return tracing_enabled_; } + ConsumerHost* host() const { return host_; } + + // Called by TracingService. + void OnActiveServicePidAdded(base::ProcessId pid); + void OnActiveServicePidRemoved(base::ProcessId pid); + void OnActiveServicePidsInitialized(); + void RequestDisableTracing(base::OnceClosure on_disabled_callback); + + // mojom::TracingSessionHost implementation. + void ChangeTraceConfig(const perfetto::TraceConfig& config) override; + void DisableTracing() override; + void ReadBuffers(mojo::ScopedDataPipeProducerHandle stream, + ReadBuffersCallback callback) override; + + void RequestBufferUsage(RequestBufferUsageCallback callback) override; + void DisableTracingAndEmitJson( + const std::string& agent_label_filter, + mojo::ScopedDataPipeProducerHandle stream, + DisableTracingAndEmitJsonCallback callback) override; + + private: + void OnJSONTraceData(std::string* json, + base::DictionaryValue* metadata, + bool has_more); + void OnEnableTracingTimeout(); + void MaybeSendEnableTracingAck(); + bool IsExpectedPid(base::ProcessId pid) const; + + ConsumerHost* const host_; + mojom::TracingSessionClientPtr tracing_session_client_; + mojo::Binding<mojom::TracingSessionHost> binding_; + bool privacy_filtering_enabled_ = false; + base::SequenceBound<StreamWriter> read_buffers_stream_writer_; + RequestBufferUsageCallback request_buffer_usage_callback_; + std::unique_ptr<JSONTraceExporter> json_trace_exporter_; + base::OnceCallback<void(bool)> flush_callback_; + const mojom::TracingClientPriority tracing_priority_; + base::OnceClosure on_disabled_callback_; + std::set<base::ProcessId> filtered_pids_; + bool tracing_enabled_ = true; + + // If set, we didn't issue OnTracingEnabled() on the session yet. If set and + // empty, no more pids are pending and we should issue OnTracingEnabled(). + base::Optional<std::set<base::ProcessId>> pending_enable_tracing_ack_pids_; + base::OneShotTimer enable_tracing_ack_timer_; + + SEQUENCE_CHECKER(sequence_checker_); + base::WeakPtrFactory<TracingSession> weak_factory_{this}; + + DISALLOW_COPY_AND_ASSIGN(TracingSession); + }; // The owner of ConsumerHost should make sure to destroy // |service| after destroying this. explicit ConsumerHost(PerfettoService* service); ~ConsumerHost() override; + PerfettoService* service() const { return service_; } + perfetto::TracingService::ConsumerEndpoint* consumer_endpoint() const { + return consumer_endpoint_.get(); + } + // mojom::ConsumerHost implementation. - void EnableTracing(mojom::TracingSessionPtr tracing_session, - const perfetto::TraceConfig& config) override; - void ChangeTraceConfig(const perfetto::TraceConfig& config) override; - void DisableTracing() override; - void ReadBuffers(mojo::ScopedDataPipeProducerHandle stream, - ReadBuffersCallback callback) override; - void DisableTracingAndEmitJson( - const std::string& agent_label_filter, - mojo::ScopedDataPipeProducerHandle stream, - DisableTracingAndEmitJsonCallback callback) override; - void RequestBufferUsage(RequestBufferUsageCallback callback) override; - - void Flush(uint32_t timeout, base::OnceCallback<void(bool)> callback); - void FreeBuffers(); + void EnableTracing(mojom::TracingSessionHostRequest tracing_session_host, + mojom::TracingSessionClientPtr tracing_session_client, + const perfetto::TraceConfig& config, + mojom::TracingClientPriority priority) override; // perfetto::Consumer implementation. // This gets called by the Perfetto service as control signals, @@ -75,34 +141,15 @@ class ConsumerHost : public perfetto::Consumer, public mojom::ConsumerHost { void OnDetach(bool success) override {} void OnAttach(bool success, const perfetto::TraceConfig&) override {} - // Called by TracingService. - void OnActiveServicePidAdded(base::ProcessId pid); - void OnActiveServicePidRemoved(base::ProcessId pid); - void OnActiveServicePidsInitialized(); + TracingSession* tracing_session_for_testing() { + return tracing_session_.get(); + } private: - perfetto::TraceConfig AdjustTraceConfig( - const perfetto::TraceConfig& trace_config); - void OnEnableTracingTimeout(); - void MaybeSendEnableTracingAck(); - bool IsExpectedPid(base::ProcessId pid) const; - void OnJSONTraceData(const std::string& json, - base::DictionaryValue* metadata, - bool has_more); - void WriteToStream(const void* start, size_t size); + void DestructTracingSession(); PerfettoService* const service_; - mojo::ScopedDataPipeProducerHandle read_buffers_stream_; - ReadBuffersCallback read_buffers_callback_; - base::OnceCallback<void(bool)> flush_callback_; - mojom::TracingSessionPtr tracing_session_; - std::set<base::ProcessId> filtered_pids_; - // If set, we didn't issue OnTracingEnabled() on the session yet. If set and - // empty, no more pids are pending and we should issue OnTracingEnabled(). - base::Optional<std::set<base::ProcessId>> pending_enable_tracing_ack_pids_; - base::OneShotTimer enable_tracing_ack_timer_; - RequestBufferUsageCallback request_buffer_usage_callback_; - std::unique_ptr<JSONTraceExporter> json_trace_exporter_; + std::unique_ptr<TracingSession> tracing_session_; SEQUENCE_CHECKER(sequence_checker_); @@ -110,7 +157,7 @@ class ConsumerHost : public perfetto::Consumer, public mojom::ConsumerHost { std::unique_ptr<perfetto::TracingService::ConsumerEndpoint> consumer_endpoint_; - base::WeakPtrFactory<ConsumerHost> weak_factory_; + base::WeakPtrFactory<ConsumerHost> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(ConsumerHost); }; diff --git a/chromium/services/tracing/perfetto/consumer_host_unittest.cc b/chromium/services/tracing/perfetto/consumer_host_unittest.cc index 160f8d8a937..2670a78de8e 100644 --- a/chromium/services/tracing/perfetto/consumer_host_unittest.cc +++ b/chromium/services/tracing/perfetto/consumer_host_unittest.cc @@ -39,7 +39,7 @@ constexpr base::ProcessId kProducerPid = 1234; // different sequences (ProducerClient side, Service side, and // whatever connects via Mojo to the Producer). This is needed // so we don't get into read/write locks. -class ThreadedPerfettoService : public mojom::TracingSession { +class ThreadedPerfettoService : public mojom::TracingSessionClient { public: ThreadedPerfettoService() : task_runner_(base::CreateSequencedTaskRunnerWithTraits( @@ -66,8 +66,6 @@ class ThreadedPerfettoService : public mojom::TracingSession { task_runner_->DeleteSoon(FROM_HERE, std::move(consumer_)); } - task_runner_->DeleteSoon(FROM_HERE, std::move(perfetto_service_)); - { base::RunLoop wait_for_destruction; task_runner_->PostTaskAndReply(FROM_HERE, base::DoNothing(), @@ -77,18 +75,22 @@ class ThreadedPerfettoService : public mojom::TracingSession { { base::RunLoop wait_for_destruction; - ProducerClient::GetTaskRunner()->task_runner()->PostTaskAndReply( - FROM_HERE, base::DoNothing(), wait_for_destruction.QuitClosure()); + PerfettoTracedProcess::GetTaskRunner() + ->GetOrCreateTaskRunner() + ->PostTaskAndReply(FROM_HERE, base::DoNothing(), + wait_for_destruction.QuitClosure()); wait_for_destruction.Run(); } } - // mojom::TracingSession implementation: + // mojom::TracingSessionClient implementation: void OnTracingEnabled() override { EXPECT_FALSE(tracing_enabled_); tracing_enabled_ = true; } + void OnTracingDisabled() override {} + void CreateProducer(const std::string& data_source_name, size_t num_packets, base::OnceClosure on_tracing_started) { @@ -129,38 +131,50 @@ class ThreadedPerfettoService : public mojom::TracingSession { } void EnableTracingOnSequence(const perfetto::TraceConfig& config) { - tracing::mojom::TracingSessionPtr tracing_session; - - binding_ = std::make_unique<mojo::Binding<mojom::TracingSession>>( - this, mojo::MakeRequest(&tracing_session)); - - consumer_->EnableTracing(std::move(tracing_session), std::move(config)); + tracing::mojom::TracingSessionClientPtr tracing_session_client; + binding_ = std::make_unique<mojo::Binding<mojom::TracingSessionClient>>( + this, mojo::MakeRequest(&tracing_session_client)); + + consumer_->EnableTracing( + mojo::MakeRequest(&tracing_session_host_), + std::move(tracing_session_client), std::move(config), + tracing::mojom::TracingClientPriority::kUserInitiated); } void ReadBuffers(mojo::ScopedDataPipeProducerHandle stream, - ConsumerHost::ReadBuffersCallback callback) { + ConsumerHost::TracingSession::ReadBuffersCallback callback) { task_runner_->PostTask( - FROM_HERE, base::BindOnce(&ConsumerHost::ReadBuffers, - base::Unretained(consumer_.get()), - std::move(stream), std::move(callback))); + FROM_HERE, + base::BindOnce( + &ConsumerHost::TracingSession::ReadBuffers, + base::Unretained(consumer_.get()->tracing_session_for_testing()), + std::move(stream), std::move(callback))); } - void FreeBuffers() { + void FreeBuffers() { tracing_session_host_.reset(); } + + void DisableTracing() { base::RunLoop wait_for_call; task_runner_->PostTaskAndReply( FROM_HERE, - base::BindOnce(&ConsumerHost::FreeBuffers, - base::Unretained(consumer_.get())), + base::BindOnce( + &ConsumerHost::TracingSession::DisableTracing, + base::Unretained(consumer_.get()->tracing_session_for_testing())), wait_for_call.QuitClosure()); wait_for_call.Run(); } - void DisableTracing() { + void DisableTracingAndEmitJson( + mojo::ScopedDataPipeProducerHandle stream, + ConsumerHost::TracingSession::DisableTracingAndEmitJsonCallback + callback) { base::RunLoop wait_for_call; task_runner_->PostTaskAndReply( FROM_HERE, - base::BindOnce(&ConsumerHost::DisableTracing, - base::Unretained(consumer_.get())), + base::BindOnce( + &ConsumerHost::TracingSession::DisableTracingAndEmitJson, + base::Unretained(consumer_.get()->tracing_session_for_testing()), + std::string(), std::move(stream), std::move(callback)), wait_for_call.QuitClosure()); wait_for_call.Run(); } @@ -177,14 +191,16 @@ class ThreadedPerfettoService : public mojom::TracingSession { void Flush(base::OnceClosure on_flush_complete) { task_runner_->PostTask( FROM_HERE, - base::BindOnce(&ConsumerHost::Flush, base::Unretained(consumer_.get()), - 10000u, - base::BindOnce( - [](base::OnceClosure callback, bool success) { - EXPECT_TRUE(success); - std::move(callback).Run(); - }, - std::move(on_flush_complete)))); + base::BindOnce( + &ConsumerHost::TracingSession::Flush, + base::Unretained(consumer_.get()->tracing_session_for_testing()), + 10000u, + base::BindOnce( + [](base::OnceClosure callback, bool success) { + EXPECT_TRUE(success); + std::move(callback).Run(); + }, + std::move(on_flush_complete)))); } void ExpectPid(base::ProcessId pid) { @@ -236,21 +252,32 @@ class ThreadedPerfettoService : public mojom::TracingSession { perfetto::DataSourceConfig GetProducerClientConfig() { perfetto::DataSourceConfig config; base::RunLoop wait_loop; + task_runner_->PostTaskAndReply(FROM_HERE, base::BindLambdaForTesting([&]() { + config = + producer_->data_source()->config(); + }), + wait_loop.QuitClosure()); + wait_loop.Run(); + return config; + } + + void ClearConsumer() { + base::RunLoop wait_loop; task_runner_->PostTaskAndReply( - FROM_HERE, base::BindLambdaForTesting([&]() { - config = producer_->producer_client()->data_source()->config(); - }), + FROM_HERE, base::BindLambdaForTesting([&]() { consumer_.reset(); }), wait_loop.QuitClosure()); wait_loop.Run(); - return config; } + PerfettoService* perfetto_service() const { return perfetto_service_.get(); } + private: scoped_refptr<base::SequencedTaskRunner> task_runner_; std::unique_ptr<PerfettoService> perfetto_service_; std::unique_ptr<ConsumerHost> consumer_; std::unique_ptr<MockProducer> producer_; - std::unique_ptr<mojo::Binding<mojom::TracingSession>> binding_; + std::unique_ptr<mojo::Binding<mojom::TracingSessionClient>> binding_; + tracing::mojom::TracingSessionHostPtr tracing_session_host_; bool tracing_enabled_ = false; }; @@ -258,7 +285,7 @@ class TracingConsumerTest : public testing::Test, public mojo::DataPipeDrainer::Client { public: void SetUp() override { - ProducerClient::ResetTaskRunnerForTesting(); + PerfettoTracedProcess::ResetTaskRunnerForTesting(); threaded_service_ = std::make_unique<ThreadedPerfettoService>(); matching_packet_count_ = 0; @@ -277,14 +304,22 @@ class TracingConsumerTest : public testing::Test, // mojo::DataPipeDrainer::Client void OnDataComplete() override { - auto proto = std::make_unique<perfetto::protos::Trace>(); - EXPECT_TRUE( - proto->ParseFromArray(received_data_.data(), received_data_.size())); - - for (int i = 0; i < proto->packet_size(); ++i) { - if (proto->packet(i).for_testing().str() == packet_testing_str_) { + if (expect_json_data_) { + std::string output(reinterpret_cast<const char*>(received_data_.data()), + received_data_.size()); + if (output.find(packet_testing_str_) != std::string::npos) { matching_packet_count_++; } + } else { + auto proto = std::make_unique<perfetto::protos::Trace>(); + EXPECT_TRUE( + proto->ParseFromArray(received_data_.data(), received_data_.size())); + + for (int i = 0; i < proto->packet_size(); ++i) { + if (proto->packet(i).for_testing().str() == packet_testing_str_) { + matching_packet_count_++; + } + } } if (on_data_complete_) { @@ -300,11 +335,27 @@ class TracingConsumerTest : public testing::Test, } void ReadBuffers() { - mojo::DataPipe data_pipe; - threaded_service_->ReadBuffers(std::move(data_pipe.producer_handle), - base::OnceClosure()); - drainer_.reset( - new mojo::DataPipeDrainer(this, std::move(data_pipe.consumer_handle))); + MojoCreateDataPipeOptions options = {sizeof(MojoCreateDataPipeOptions), + MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0}; + mojo::ScopedDataPipeProducerHandle producer; + mojo::ScopedDataPipeConsumerHandle consumer; + MojoResult rv = mojo::CreateDataPipe(&options, &producer, &consumer); + ASSERT_EQ(MOJO_RESULT_OK, rv); + threaded_service_->ReadBuffers(std::move(producer), base::OnceClosure()); + drainer_.reset(new mojo::DataPipeDrainer(this, std::move(consumer))); + } + + void DisableTracingAndEmitJson(base::OnceClosure write_callback) { + expect_json_data_ = true; + MojoCreateDataPipeOptions options = {sizeof(MojoCreateDataPipeOptions), + MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0}; + mojo::ScopedDataPipeProducerHandle producer; + mojo::ScopedDataPipeConsumerHandle consumer; + MojoResult rv = mojo::CreateDataPipe(&options, &producer, &consumer); + ASSERT_EQ(MOJO_RESULT_OK, rv); + threaded_service_->DisableTracingAndEmitJson(std::move(producer), + std::move(write_callback)); + drainer_.reset(new mojo::DataPipeDrainer(this, std::move(consumer))); } perfetto::TraceConfig GetDefaultTraceConfig( @@ -356,6 +407,7 @@ class TracingConsumerTest : public testing::Test, std::string packet_testing_str_; size_t matching_packet_count_ = 0; size_t total_bytes_received_ = 0; + bool expect_json_data_ = false; }; TEST_F(TracingConsumerTest, EnableAndDisableTracing) { @@ -393,6 +445,26 @@ TEST_F(TracingConsumerTest, ReceiveTestPackets) { EXPECT_EQ(10u, matching_packet_count()); } +TEST_F(TracingConsumerTest, DeleteConsumerWhenReceiving) { + EnableTracingWithDataSourceName(mojom::kTraceEventDataSourceName); + + base::RunLoop wait_for_tracing_start; + threaded_perfetto_service()->CreateProducer( + mojom::kTraceEventDataSourceName, 100u, + wait_for_tracing_start.QuitClosure()); + + wait_for_tracing_start.Run(); + + base::RunLoop no_more_data; + ExpectPackets(kPerfettoTestString, no_more_data.QuitClosure()); + + threaded_perfetto_service()->DisableTracing(); + ReadBuffers(); + + threaded_perfetto_service()->ClearConsumer(); + no_more_data.Run(); +} + TEST_F(TracingConsumerTest, FlushProducers) { EnableTracingWithDataSourceName(mojom::kTraceEventDataSourceName); @@ -547,4 +619,128 @@ TEST_F(TracingConsumerTest, PrivacyFilterConfig) { .privacy_filtering_enabled()); } +TEST_F(TracingConsumerTest, PrivacyFilterConfigInJson) { + EnableTracingWithDataSourceName(mojom::kTraceEventDataSourceName, + /* enable_privacy_filtering =*/true); + + base::RunLoop wait_for_tracing_start; + threaded_perfetto_service()->CreateProducer( + mojom::kTraceEventDataSourceName, 10u, + wait_for_tracing_start.QuitClosure()); + + wait_for_tracing_start.Run(); + + EXPECT_TRUE(threaded_perfetto_service() + ->GetProducerClientConfig() + .chrome_config() + .privacy_filtering_enabled()); + + base::RunLoop no_more_data; + ExpectPackets("\"perfetto_trace_stats\":\"__stripped__\"", + no_more_data.QuitClosure()); + + base::RunLoop write_done; + DisableTracingAndEmitJson(write_done.QuitClosure()); + + no_more_data.Run(); + write_done.Run(); + + EXPECT_EQ(1u, matching_packet_count()); +} + +class MockConsumerHost : public mojom::TracingSessionClient { + public: + MockConsumerHost(PerfettoService* service) + : consumer_host_(std::make_unique<ConsumerHost>(service)) {} + + void EnableTracing(const perfetto::TraceConfig& config, + mojom::TracingClientPriority priority) { + tracing::mojom::TracingSessionClientPtr tracing_session_client; + binding_.Bind(mojo::MakeRequest(&tracing_session_client)); + + binding_.set_connection_error_handler(base::BindOnce( + &MockConsumerHost::OnConnectionLost, base::Unretained(this))); + + consumer_host_->EnableTracing(mojo::MakeRequest(&tracing_session_host_), + std::move(tracing_session_client), config, + priority); + tracing_session_host_.set_connection_error_handler(base::BindOnce( + &MockConsumerHost::OnConnectionLost, base::Unretained(this))); + } + + void DisableTracing() { tracing_session_host_->DisableTracing(); } + + void OnConnectionLost() { + CloseTracingSession(); + wait_for_connection_lost_.Quit(); + } + + void CloseTracingSession() { + tracing_session_host_.reset(); + binding_.Close(); + } + + // mojom::TracingSessionClient implementation: + void OnTracingEnabled() override { wait_for_tracing_enabled_.Quit(); } + + void OnTracingDisabled() override { wait_for_tracing_disabled_.Quit(); } + + void WaitForConnectionLost() { wait_for_connection_lost_.Run(); } + + void WaitForTracingEnabled() { wait_for_tracing_enabled_.Run(); } + + void WaitForTracingDisabled() { wait_for_tracing_disabled_.Run(); } + + private: + tracing::mojom::TracingSessionHostPtr tracing_session_host_; + mojo::Binding<mojom::TracingSessionClient> binding_{this}; + std::unique_ptr<ConsumerHost> consumer_host_; + base::RunLoop wait_for_connection_lost_; + base::RunLoop wait_for_tracing_enabled_; + base::RunLoop wait_for_tracing_disabled_; +}; + +TEST_F(TracingConsumerTest, TestConsumerPriority) { + // auto perfetto_service = std::make_unique<PerfettoService>(nullptr); + PerfettoService::GetInstance()->SetActiveServicePidsInitialized(); + auto trace_config = GetDefaultTraceConfig(mojom::kTraceEventDataSourceName); + + MockConsumerHost background_consumer_1(PerfettoService::GetInstance()); + background_consumer_1.EnableTracing( + trace_config, tracing::mojom::TracingClientPriority::kBackground); + background_consumer_1.WaitForTracingEnabled(); + + // Second consumer of the same priority should cause the first one to + // be disabled and the second to start. + MockConsumerHost background_consumer_2(PerfettoService::GetInstance()); + background_consumer_2.EnableTracing( + trace_config, tracing::mojom::TracingClientPriority::kBackground); + background_consumer_1.WaitForTracingDisabled(); + background_consumer_2.WaitForTracingEnabled(); + + // Third consumer will have a higher priority, and should kill the second + // one. + MockConsumerHost user_initiated_consumer(PerfettoService::GetInstance()); + user_initiated_consumer.EnableTracing( + trace_config, tracing::mojom::TracingClientPriority::kUserInitiated); + background_consumer_2.WaitForTracingDisabled(); + user_initiated_consumer.WaitForTracingEnabled(); + + // Fourth consumer will be another background consumer, and should be + // itself killed as the third consumer is still running. + MockConsumerHost background_consumer_3(PerfettoService::GetInstance()); + background_consumer_3.EnableTracing( + trace_config, tracing::mojom::TracingClientPriority::kBackground); + background_consumer_3.WaitForConnectionLost(); + + // If we close the user initiated consumer, the third background consumer + // should now be able to trace. + user_initiated_consumer.DisableTracing(); + user_initiated_consumer.WaitForTracingDisabled(); + user_initiated_consumer.CloseTracingSession(); + background_consumer_3.EnableTracing( + trace_config, tracing::mojom::TracingClientPriority::kBackground); + background_consumer_3.WaitForTracingEnabled(); +} + } // namespace tracing diff --git a/chromium/services/tracing/perfetto/json_exporter_main.cc b/chromium/services/tracing/perfetto/json_exporter_main.cc new file mode 100644 index 00000000000..ff2ba4de1df --- /dev/null +++ b/chromium/services/tracing/perfetto/json_exporter_main.cc @@ -0,0 +1,77 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/at_exit.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "services/tracing/perfetto/json_trace_exporter.h" +#include "services/tracing/perfetto/track_event_json_exporter.h" +#include "third_party/perfetto/include/perfetto/tracing/core/trace_packet.h" +#include "third_party/perfetto/protos/perfetto/trace/trace.pbzero.h" + +// Tool to convert a given proto trace into json. +// +// Usage: +// trace_json_exporter [input_file] [output_file] +// Both the arguments are required. +// +// Parses the given input file which contains a serialized perfetto::Trace +// proto, converts the trace to JSON and writes to output file. + +namespace tracing { + +void OnJsonData(base::File* output_file, + std::string* json, + base::DictionaryValue* metadata, + bool has_more) { + CHECK_EQ(output_file->WriteAtCurrentPos(json->data(), json->size()), + static_cast<int>(json->size())); + LOG(ERROR) << "Finished writing " << json->size() << " bytes to file."; +} + +void WriteJsonTrace(const std::string& data, base::File* output_file) { + TrackEventJSONExporter exporter( + JSONTraceExporter::ArgumentFilterPredicate(), + JSONTraceExporter::MetadataFilterPredicate(), + base::BindRepeating(&OnJsonData, base::Unretained(output_file))); + perfetto::protos::pbzero::Trace::Decoder decoder( + reinterpret_cast<const uint8_t*>(data.data()), data.size()); + std::vector<perfetto::TracePacket> packets; + for (auto it = decoder.packet(); !!it; ++it) { + perfetto::TracePacket trace_packet; + trace_packet.AddSlice(it->data(), it->size()); + packets.emplace_back(std::move(trace_packet)); + } + exporter.OnTraceData(std::move(packets), false); +} + +} // namespace tracing + +int main(int argc, char* argv[]) { + base::AtExitManager at_exit_manager; + base::CommandLine::Init(argc, argv); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + + base::CommandLine::StringVector args = command_line.GetArgs(); + if (args.size() < 2u) { + LOG(ERROR) << "Enter input and output path. Usage:" + "trace_json_exporter [input] [output]"; + return -1; + } + + base::FilePath input_path(args[0]); + base::FilePath output_path(args[1]); + + std::string contents; + CHECK(base::ReadFileToString(input_path, &contents)); + + base::File output_file( + output_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); + tracing::WriteJsonTrace(contents, &output_file); + return 0; +} diff --git a/chromium/services/tracing/perfetto/json_trace_exporter.cc b/chromium/services/tracing/perfetto/json_trace_exporter.cc index f86102cc09d..cf6dc4c209a 100644 --- a/chromium/services/tracing/perfetto/json_trace_exporter.cc +++ b/chromium/services/tracing/perfetto/json_trace_exporter.cc @@ -440,10 +440,11 @@ void JSONTraceExporter::StringBuffer::EscapeJSONAndAppend( void JSONTraceExporter::StringBuffer::Flush(base::DictionaryValue* metadata, bool has_more) { - callback_.Run(out_, metadata, has_more); + callback_.Run(&out_, metadata, has_more); if (has_more) { // We clear |out_| because we've processed all the current data in |out_| - // and we don't want any data to be repeated. We have to protect this by + // and we don't want any data to be repeated. The callback should have moved + // all the contents, but clear it to be safe. We have to protect this by // checking |has_more| because the callback could have deleted |this| in // which cause |out_| is a destroyed as well. out_.clear(); diff --git a/chromium/services/tracing/perfetto/json_trace_exporter.h b/chromium/services/tracing/perfetto/json_trace_exporter.h index 9e96cba5a3e..b682dd2e161 100644 --- a/chromium/services/tracing/perfetto/json_trace_exporter.h +++ b/chromium/services/tracing/perfetto/json_trace_exporter.h @@ -55,10 +55,8 @@ class JSONTraceExporter { using MetadataFilterPredicate = base::RepeatingCallback<bool(const std::string& metadata_name)>; - using OnTraceEventJSONCallback = - base::RepeatingCallback<void(const std::string& json, - base::DictionaryValue* metadata, - bool has_more)>; + using OnTraceEventJSONCallback = base::RepeatingCallback< + void(std::string* json, base::DictionaryValue* metadata, bool has_more)>; JSONTraceExporter(ArgumentFilterPredicate argument_filter_predicate, MetadataFilterPredicate metadata_filter_predicate, diff --git a/chromium/services/tracing/perfetto/json_trace_exporter_unittest.cc b/chromium/services/tracing/perfetto/json_trace_exporter_unittest.cc index 8a55cd296de..9da5bb202c5 100644 --- a/chromium/services/tracing/perfetto/json_trace_exporter_unittest.cc +++ b/chromium/services/tracing/perfetto/json_trace_exporter_unittest.cc @@ -195,11 +195,12 @@ class JsonTraceExporterTest : public testing::Test { base::BindRepeating(&JsonTraceExporterTest::OnTraceEventJSON, base::Unretained(this)))) {} - void OnTraceEventJSON(const std::string& json, + void OnTraceEventJSON(std::string* json, base::DictionaryValue* metadata, bool has_more) { - unparsed_trace_data_ += json; - unparsed_trace_data_sequence_.push_back(json); + unparsed_trace_data_ += *json; + unparsed_trace_data_sequence_.push_back(std::string()); + unparsed_trace_data_sequence_.back().swap(*json); if (has_more) { return; } @@ -207,7 +208,7 @@ class JsonTraceExporterTest : public testing::Test { base::JSONReader::ReadDeprecated(unparsed_trace_data_)); EXPECT_TRUE(parsed_trace_data_); if (!parsed_trace_data_) { - LOG(ERROR) << "Couldn't parse json: \n" << json; + LOG(ERROR) << "Couldn't parse json: \n" << unparsed_trace_data_; } // The TraceAnalyzer expects the raw trace output, without the diff --git a/chromium/services/tracing/perfetto/perfetto_integration_unittest.cc b/chromium/services/tracing/perfetto/perfetto_integration_unittest.cc index bec94c0f452..a7da62c1981 100644 --- a/chromium/services/tracing/perfetto/perfetto_integration_unittest.cc +++ b/chromium/services/tracing/perfetto/perfetto_integration_unittest.cc @@ -4,6 +4,7 @@ #include <memory> #include <string> +#include <thread> #include <utility> #include "base/bind.h" @@ -22,14 +23,17 @@ namespace { const char kPerfettoTestDataSourceName[] = "org.chromium.chrome_integration_unittest"; -const char kPerfettoProducerName[] = "chrome_producer_test"; +const char kPerfettoProducerName[] = "org.chromium.perfetto_producer.123"; class PerfettoIntegrationTest : public testing::Test { public: void SetUp() override { + PerfettoTracedProcess::Get()->ClearDataSourcesForTesting(); + data_source_ = + std::make_unique<TestDataSource>(kPerfettoTestDataSourceName, 0); perfetto_service_ = std::make_unique<PerfettoService>(); RunUntilIdle(); - ProducerClient::ResetTaskRunnerForTesting(); + PerfettoTracedProcess::ResetTaskRunnerForTesting(); } void TearDown() override { perfetto_service_.reset(); } @@ -37,14 +41,14 @@ class PerfettoIntegrationTest : public testing::Test { PerfettoService* perfetto_service() const { return perfetto_service_.get(); } void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); } - private: + protected: + std::unique_ptr<TestDataSource> data_source_; std::unique_ptr<PerfettoService> perfetto_service_; base::test::ScopedTaskEnvironment scoped_task_environment_; }; TEST_F(PerfettoIntegrationTest, ProducerDatasourceInitialized) { - auto dummy_client = - std::make_unique<MockProducerClient>(0 /* send_packet_count */); + auto dummy_client = std::make_unique<MockProducerClient>(); base::RunLoop producer_initialized_runloop; auto new_producer = std::make_unique<MockProducerHost>( @@ -69,12 +73,13 @@ TEST_F(PerfettoIntegrationTest, ClientEnabledAndDisabled) { base::RunLoop client_enabled_callback; base::RunLoop client_disabled_callback; auto client = std::make_unique<MockProducerClient>( - 0 /* send_packet_count */, client_enabled_callback.QuitClosure(), + client_enabled_callback.QuitClosure(), client_disabled_callback.QuitClosure()); auto producer = std::make_unique<MockProducerHost>( kPerfettoProducerName, kPerfettoTestDataSourceName, perfetto_service()->GetService(), client.get()); + client_enabled_callback.Run(); RunUntilIdle(); @@ -91,11 +96,12 @@ TEST_F(PerfettoIntegrationTest, ClientEnabledAndDisabled) { TEST_F(PerfettoIntegrationTest, PacketsEndToEndProducerFirst) { const size_t kNumPackets = 10; + data_source_->set_send_packet_count(kNumPackets); base::RunLoop client_enabled_callback; base::RunLoop client_disabled_callback; auto client = std::make_unique<MockProducerClient>( - kNumPackets, client_enabled_callback.QuitClosure(), + client_enabled_callback.QuitClosure(), client_disabled_callback.QuitClosure()); auto producer = std::make_unique<MockProducerHost>( @@ -127,6 +133,7 @@ TEST_F(PerfettoIntegrationTest, PacketsEndToEndProducerFirst) { TEST_F(PerfettoIntegrationTest, PacketsEndToEndConsumerFirst) { const size_t kNumPackets = 10; + data_source_->set_send_packet_count(kNumPackets); base::RunLoop no_more_packets_runloop; MockConsumer consumer(kPerfettoTestDataSourceName, @@ -139,7 +146,7 @@ TEST_F(PerfettoIntegrationTest, PacketsEndToEndConsumerFirst) { base::RunLoop client_enabled_callback; auto client = std::make_unique<MockProducerClient>( - kNumPackets, client_enabled_callback.QuitClosure()); + client_enabled_callback.QuitClosure()); auto new_producer = std::make_unique<MockProducerHost>( kPerfettoProducerName, kPerfettoTestDataSourceName, @@ -159,6 +166,7 @@ TEST_F(PerfettoIntegrationTest, PacketsEndToEndConsumerFirst) { TEST_F(PerfettoIntegrationTest, CommitDataRequestIsMaybeComplete) { const size_t kNumPackets = 100; + data_source_->set_send_packet_count(kNumPackets); base::RunLoop no_more_packets_runloop; MockConsumer consumer(kPerfettoTestDataSourceName, @@ -171,7 +179,7 @@ TEST_F(PerfettoIntegrationTest, CommitDataRequestIsMaybeComplete) { base::RunLoop client_enabled_callback; auto client = std::make_unique<MockProducerClient>( - kNumPackets, client_enabled_callback.QuitClosure()); + client_enabled_callback.QuitClosure()); auto new_producer = std::make_unique<MockProducerHost>( kPerfettoProducerName, kPerfettoTestDataSourceName, perfetto_service()->GetService(), client.get()); @@ -179,11 +187,13 @@ TEST_F(PerfettoIntegrationTest, CommitDataRequestIsMaybeComplete) { client_enabled_callback.Run(); base::RunLoop wait_for_packet_write; - client->GetTaskRunner()->task_runner()->PostTaskAndReply( - FROM_HERE, - base::BindOnce(&TestDataSource::WritePacketBigly, - base::Unretained(client->data_source())), - wait_for_packet_write.QuitClosure()); + PerfettoTracedProcess::Get() + ->GetTaskRunner() + ->GetOrCreateTaskRunner() + ->PostTaskAndReply(FROM_HERE, + base::BindOnce(&TestDataSource::WritePacketBigly, + base::Unretained(data_source_.get())), + wait_for_packet_write.QuitClosure()); wait_for_packet_write.Run(); RunUntilIdle(); @@ -200,6 +210,7 @@ TEST_F(PerfettoIntegrationTest, CommitDataRequestIsMaybeComplete) { TEST_F(PerfettoIntegrationTest, TracingRestarted) { const size_t kNumPackets = 10; + data_source_->set_send_packet_count(kNumPackets); base::RunLoop no_more_packets_runloop; MockConsumer consumer(kPerfettoTestDataSourceName, @@ -212,7 +223,7 @@ TEST_F(PerfettoIntegrationTest, TracingRestarted) { base::RunLoop client_enabled_callback; auto client = std::make_unique<MockProducerClient>( - kNumPackets, client_enabled_callback.QuitClosure()); + client_enabled_callback.QuitClosure()); auto new_producer = std::make_unique<MockProducerHost>( kPerfettoProducerName, kPerfettoTestDataSourceName, @@ -254,11 +265,12 @@ TEST_F(PerfettoIntegrationTest, TracingRestarted) { TEST_F(PerfettoIntegrationTest, NoPacketsReceivedOnWrongSourceName) { const size_t kNumPackets = 10; + data_source_->set_send_packet_count(kNumPackets); base::RunLoop client_enabled_callback; base::RunLoop client_disabled_callback; auto client = std::make_unique<MockProducerClient>( - kNumPackets, client_enabled_callback.QuitClosure(), + client_enabled_callback.QuitClosure(), client_disabled_callback.QuitClosure()); base::RunLoop producer_initialized_runloop; @@ -267,8 +279,7 @@ TEST_F(PerfettoIntegrationTest, NoPacketsReceivedOnWrongSourceName) { perfetto_service()->GetService(), client.get()); base::RunLoop no_more_packets_runloop; - MockConsumer consumer(kPerfettoTestDataSourceName, - perfetto_service()->GetService(), + MockConsumer consumer("fake_data_source", perfetto_service()->GetService(), [&no_more_packets_runloop](bool has_more) { if (!has_more) { no_more_packets_runloop.Quit(); @@ -290,22 +301,27 @@ TEST_F(PerfettoIntegrationTest, base::RunLoop client1_enabled_callback; base::RunLoop client2_enabled_callback; auto client1 = std::make_unique<MockProducerClient>( - 0 /* send_packet_count */, client1_enabled_callback.QuitClosure()); - auto client2 = std::make_unique<MockProducerClient>( - 0 /* send_packet_count */, client2_enabled_callback.QuitClosure()); - + client1_enabled_callback.QuitClosure()); auto producer1 = std::make_unique<MockProducerHost>( kPerfettoProducerName, kPerfettoTestDataSourceName, perfetto_service()->GetService(), client1.get()); - auto producer2 = std::make_unique<MockProducerHost>( - kPerfettoProducerName, kPerfettoTestDataSourceName, - perfetto_service()->GetService(), client2.get()); - + // Start the trace here, this is because we need to send the EnableTracing + // call to client1, but constructing client2 will override the + // |producer_client_| pointer in PerfettoTracedProcess::Get() so we wait until + // client1 has been enabled before constructing the second producer client. MockConsumer consumer(kPerfettoTestDataSourceName, perfetto_service()->GetService(), nullptr); client1_enabled_callback.Run(); + + auto client2 = std::make_unique<MockProducerClient>( + client2_enabled_callback.QuitClosure()); + + auto producer2 = std::make_unique<MockProducerHost>( + kPerfettoProducerName, kPerfettoTestDataSourceName, + perfetto_service()->GetService(), client2.get()); + client2_enabled_callback.Run(); EXPECT_TRUE(client1->shared_memory()); diff --git a/chromium/services/tracing/perfetto/perfetto_service.cc b/chromium/services/tracing/perfetto/perfetto_service.cc index eb87096932a..980aec90ae9 100644 --- a/chromium/services/tracing/perfetto/perfetto_service.cc +++ b/chromium/services/tracing/perfetto/perfetto_service.cc @@ -19,6 +19,33 @@ namespace tracing { +namespace { + +bool StringToProcessId(const std::string& input, base::ProcessId* output) { + // Pid is encoded as uint in the string. + return base::StringToUint(input, reinterpret_cast<uint32_t*>(output)); +} + +} // namespace + +// static +bool PerfettoService::ParsePidFromProducerName(const std::string& producer_name, + base::ProcessId* pid) { + if (!base::StartsWith(producer_name, mojom::kPerfettoProducerNamePrefix, + base::CompareCase::SENSITIVE)) { + LOG(DFATAL) << "Unexpected producer name: " << producer_name; + return false; + } + + static const size_t kPrefixLength = + strlen(mojom::kPerfettoProducerNamePrefix); + if (!StringToProcessId(producer_name.substr(kPrefixLength), pid)) { + LOG(DFATAL) << "Unexpected producer name: " << producer_name; + return false; + } + return true; +} + // static PerfettoService* PerfettoService::GetInstance() { static base::NoDestructor<PerfettoService> perfetto_service; @@ -63,31 +90,63 @@ void PerfettoService::ConnectToProducerHost( void PerfettoService::AddActiveServicePid(base::ProcessId pid) { active_service_pids_.insert(pid); - for (auto* consumer_host : consumer_hosts_) { - consumer_host->OnActiveServicePidAdded(pid); + for (auto* tracing_session : tracing_sessions_) { + tracing_session->OnActiveServicePidAdded(pid); } } void PerfettoService::RemoveActiveServicePid(base::ProcessId pid) { active_service_pids_.erase(pid); - for (auto* consumer_host : consumer_hosts_) { - consumer_host->OnActiveServicePidRemoved(pid); + for (auto* tracing_session : tracing_sessions_) { + tracing_session->OnActiveServicePidRemoved(pid); } } void PerfettoService::SetActiveServicePidsInitialized() { active_service_pids_initialized_ = true; - for (auto* consumer_host : consumer_hosts_) { - consumer_host->OnActiveServicePidsInitialized(); + for (auto* tracing_session : tracing_sessions_) { + tracing_session->OnActiveServicePidsInitialized(); } } -void PerfettoService::RegisterConsumerHost(ConsumerHost* consumer_host) { - consumer_hosts_.insert(consumer_host); +void PerfettoService::RegisterTracingSession( + ConsumerHost::TracingSession* tracing_session) { + tracing_sessions_.insert(tracing_session); } -void PerfettoService::UnregisterConsumerHost(ConsumerHost* consumer_host) { - consumer_hosts_.erase(consumer_host); +void PerfettoService::UnregisterTracingSession( + ConsumerHost::TracingSession* tracing_session) { + tracing_sessions_.erase(tracing_session); +} + +void PerfettoService::RequestTracingSession( + mojom::TracingClientPriority priority, + base::OnceClosure callback) { + // TODO(oysteine): This currently assumes we only have one concurrent tracing + // session, which is enforced by all ConsumerHost::BeginTracing calls routing + // through RequestTracingSession before creating a new TracingSession. + // Not running the callback means we'll drop any connection requests and deny + // the creation of the tracing session. + for (auto* tracing_session : tracing_sessions_) { + if (!tracing_session->tracing_enabled()) { + continue; + } + + if (tracing_session->tracing_priority() > priority) { + return; + } + + // If the currently active session is the same or lower priority and it's + // tracing, then we'll disable it and re-try the request once it's shut + // down. + tracing_session->RequestDisableTracing( + base::BindOnce(&PerfettoService::RequestTracingSession, + base::Unretained(PerfettoService::GetInstance()), + priority, std::move(callback))); + return; + } + + std::move(callback).Run(); } } // namespace tracing diff --git a/chromium/services/tracing/perfetto/perfetto_service.h b/chromium/services/tracing/perfetto/perfetto_service.h index f611ec309a3..aae5ede388c 100644 --- a/chromium/services/tracing/perfetto/perfetto_service.h +++ b/chromium/services/tracing/perfetto/perfetto_service.h @@ -12,6 +12,7 @@ #include "mojo/public/cpp/bindings/binding_set.h" #include "mojo/public/cpp/bindings/strong_binding_set.h" #include "services/service_manager/public/cpp/identity.h" +#include "services/tracing/perfetto/consumer_host.h" #include "services/tracing/public/cpp/perfetto/task_runner.h" #include "services/tracing/public/mojom/perfetto_service.mojom.h" @@ -21,8 +22,6 @@ class TracingService; namespace tracing { -class ConsumerHost; - // This class serves two purposes: It wraps the use of the system-wide // perfetto::TracingService instance, and serves as the main Mojo interface for // connecting per-process ProducerClient with corresponding service-side @@ -34,6 +33,8 @@ class PerfettoService : public mojom::PerfettoService { ~PerfettoService() override; static PerfettoService* GetInstance(); + static bool ParsePidFromProducerName(const std::string& producer_name, + base::ProcessId* pid); void BindRequest(mojom::PerfettoServiceRequest request, uint32_t pid); @@ -43,10 +44,17 @@ class PerfettoService : public mojom::PerfettoService { perfetto::TracingService* GetService() const; - // Called when a ConsumerHost is created/destroyed (i.e. when a consumer - // connects/disconnects). - void RegisterConsumerHost(ConsumerHost* consumer_host); - void UnregisterConsumerHost(ConsumerHost* consumer_host); + // Called when a ConsumerHost::TracingSession is created/destroyed (i.e. when + // a consumer starts/finishes tracing. + void RegisterTracingSession(ConsumerHost::TracingSession* consumer_host); + void UnregisterTracingSession(ConsumerHost::TracingSession* consumer_host); + + // Make a request of the service for whether or not a TracingSession + // should be allowed to start tracing, in case of pre-existing sessions. + // |callback| will eventually be called once a session is allowed, or it + // will be destroyed. + void RequestTracingSession(mojom::TracingClientPriority priority, + base::OnceClosure callback); // Called by TracingService to notify the perfetto service of the PIDs of // actively running services (whenever a service starts or stops). @@ -70,7 +78,7 @@ class PerfettoService : public mojom::PerfettoService { std::unique_ptr<perfetto::TracingService> service_; mojo::BindingSet<mojom::PerfettoService, uint32_t> bindings_; mojo::StrongBindingSet<mojom::ProducerHost> producer_bindings_; - std::set<ConsumerHost*> consumer_hosts_; // Not owned. + std::set<ConsumerHost::TracingSession*> tracing_sessions_; // Not owned. std::set<base::ProcessId> active_service_pids_; bool active_service_pids_initialized_ = false; diff --git a/chromium/services/tracing/perfetto/perfetto_tracing_coordinator.cc b/chromium/services/tracing/perfetto/perfetto_tracing_coordinator.cc index 591727e5a65..f12f6a13a7d 100644 --- a/chromium/services/tracing/perfetto/perfetto_tracing_coordinator.cc +++ b/chromium/services/tracing/perfetto/perfetto_tracing_coordinator.cc @@ -84,7 +84,7 @@ class PerfettoTracingCoordinator::TracingSession : public perfetto::Consumer { ~TracingSession() override { if (!stop_and_flush_callback_.is_null()) { - base::ResetAndReturn(&stop_and_flush_callback_) + std::move(stop_and_flush_callback_) .Run(base::Value(base::Value::Type::DICTIONARY)); } @@ -124,16 +124,16 @@ class PerfettoTracingCoordinator::TracingSession : public perfetto::Consumer { consumer_endpoint_->DisableTracing(); } - void OnJSONTraceEventCallback(const std::string& json, + void OnJSONTraceEventCallback(std::string* json, base::DictionaryValue* metadata, bool has_more) { if (stream_.is_valid()) { - mojo::BlockingCopyFromString(json, stream_); + mojo::BlockingCopyFromString(*json, stream_); } if (!has_more) { DCHECK(!stop_and_flush_callback_.is_null()); - base::ResetAndReturn(&stop_and_flush_callback_) + std::move(stop_and_flush_callback_) .Run(/*metadata=*/std::move(*metadata)); std::move(tracing_over_callback_).Run(); @@ -208,8 +208,8 @@ class PerfettoTracingCoordinator::TracingSession : public perfetto::Consumer { // Attempt to parse the PID out of the producer name. base::ProcessId pid; - if (!ConsumerHost::ParsePidFromProducerName(state_change.producer_name(), - &pid)) { + if (!PerfettoService::ParsePidFromProducerName( + state_change.producer_name(), &pid)) { continue; } diff --git a/chromium/services/tracing/perfetto/privacy_filtered_fields-inl.h b/chromium/services/tracing/perfetto/privacy_filtered_fields-inl.h new file mode 100644 index 00000000000..9ec02336839 --- /dev/null +++ b/chromium/services/tracing/perfetto/privacy_filtered_fields-inl.h @@ -0,0 +1,98 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_TRACING_PERFETTO_PRIVACY_FILTERED_FIELDS_INL_H_ +#define SERVICES_TRACING_PERFETTO_PRIVACY_FILTERED_FIELDS_INL_H_ + +// This file is auto generated from internal copy of the TracePacket proto, that +// does not contain any privacy sensitive fields. Updates to this file should be +// made by changing internal copy and then running the generator script. Follow +// instructions at: +// https://goto.google.com/chrome-trace-privacy-filtered-fields + +namespace tracing { + +// A MessageInfo node created from a tree of TracePacket proto messages. +struct MessageInfo { + // List of accepted field ids in the output for this message. The end of list + // is marked by a -1. + const int* accepted_field_ids; + + // List of sub messages that correspond to the accepted field ids list. There + // is no end of list marker and the length is this list is equal to length of + // |accepted_field_ids| - 1. + const MessageInfo* const* const sub_messages; +}; + +// Proto Message: TaskExecution +constexpr int kTaskExecutionIndices[] = {1, -1}; +constexpr MessageInfo kTaskExecution = {kTaskExecutionIndices, nullptr}; + +// Proto Message: LegacyEvent +constexpr int kLegacyEventIndices[] = {1, 2, 3, 4, 6, 8, 9, 10, + 11, 12, 13, 14, 18, 19, -1}; +constexpr MessageInfo kLegacyEvent = {kLegacyEventIndices, nullptr}; + +// Proto Message: TrackEvent +constexpr int kTrackEventIndices[] = {1, 2, 3, 5, 6, 16, 17, -1}; +constexpr MessageInfo const* kTrackEventComplexMessages[] = { + nullptr, nullptr, nullptr, &kTaskExecution, + &kLegacyEvent, nullptr, nullptr}; +constexpr MessageInfo kTrackEvent = {kTrackEventIndices, + kTrackEventComplexMessages}; + +// Proto Message: EventCategory +constexpr int kEventCategoryIndices[] = {1, 2, -1}; +constexpr MessageInfo kEventCategory = {kEventCategoryIndices, nullptr}; + +// Proto Message: LegacyEventName +constexpr int kLegacyEventNameIndices[] = {1, 2, -1}; +constexpr MessageInfo kLegacyEventName = {kLegacyEventNameIndices, nullptr}; + +// Proto Message: SourceLocation +constexpr int kSourceLocationIndices[] = {1, 2, 3, -1}; +constexpr MessageInfo kSourceLocation = {kSourceLocationIndices, nullptr}; + +// Proto Message: InternedData +constexpr int kInternedDataIndices[] = {1, 2, 4, -1}; +constexpr MessageInfo const* kInternedDataComplexMessages[] = { + &kEventCategory, &kLegacyEventName, &kSourceLocation}; +constexpr MessageInfo kInternedData = {kInternedDataIndices, + kInternedDataComplexMessages}; + +// Proto Message: BufferStats +constexpr int kBufferStatsIndices[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, -1}; +constexpr MessageInfo kBufferStats = {kBufferStatsIndices, nullptr}; + +// Proto Message: TraceStats +constexpr int kTraceStatsIndices[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, -1}; +constexpr MessageInfo const* kTraceStatsComplexMessages[] = { + &kBufferStats, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr}; +constexpr MessageInfo kTraceStats = {kTraceStatsIndices, + kTraceStatsComplexMessages}; + +// Proto Message: ProcessDescriptor +constexpr int kProcessDescriptorIndices[] = {1, 4, -1}; +constexpr MessageInfo kProcessDescriptor = {kProcessDescriptorIndices, nullptr}; + +// Proto Message: ThreadDescriptor +constexpr int kThreadDescriptorIndices[] = {1, 2, 4, 6, 7, -1}; +constexpr MessageInfo kThreadDescriptor = {kThreadDescriptorIndices, nullptr}; + +// EDIT: Contains field numbers: {3} which are not autogenerated. + +// Proto Message: TracePacket +constexpr int kTracePacketIndices[] = {3, 10, 11, 12, 35, 36, + 41, 42, 43, 44, -1}; +constexpr MessageInfo const* kTracePacketComplexMessages[] = { + nullptr, nullptr, &kTrackEvent, &kInternedData, &kTraceStats, + nullptr, nullptr, nullptr, &kProcessDescriptor, &kThreadDescriptor}; +constexpr MessageInfo kTracePacket = {kTracePacketIndices, + kTracePacketComplexMessages}; + +} // namespace tracing + +#endif // SERVICES_TRACING_PERFETTO_PRIVACY_FILTERED_FIELDS_INL_H_ diff --git a/chromium/services/tracing/perfetto/privacy_filtering_check.cc b/chromium/services/tracing/perfetto/privacy_filtering_check.cc new file mode 100644 index 00000000000..cebb3cd0262 --- /dev/null +++ b/chromium/services/tracing/perfetto/privacy_filtering_check.cc @@ -0,0 +1,101 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/tracing/perfetto/privacy_filtering_check.h" + +#include <sstream> + +#include "base/logging.h" +#include "services/tracing/perfetto/privacy_filtered_fields-inl.h" +#include "third_party/perfetto/protos/perfetto/trace/interned_data/interned_data.pbzero.h" +#include "third_party/perfetto/protos/perfetto/trace/trace.pbzero.h" +#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h" +#include "third_party/perfetto/protos/perfetto/trace/track_event/track_event.pbzero.h" + +namespace tracing { +namespace { + +using perfetto::protos::pbzero::InternedData; +using perfetto::protos::pbzero::TracePacket; +using perfetto::protos::pbzero::TrackEvent; +using protozero::ProtoDecoder; + +int FindIndexOfValue(const int* const arr, uint32_t value) { + for (unsigned i = 0; arr[i] != -1; ++i) { + if (static_cast<int>(value) == arr[i]) + return i; + } + return -1; +} + +// Recursively verifies that the |proto| contains only accepted field IDs +// including all sub messages. Keeps track of |parent_ids| for printing error +// message. +void VerifyProtoRecursive(const MessageInfo* root, + ProtoDecoder* proto, + std::vector<uint32_t>* parent_ids) { + proto->Reset(); + for (auto f = proto->ReadField(); f.valid(); f = proto->ReadField()) { + int index = FindIndexOfValue(root->accepted_field_ids, f.id()); + if (index == -1) { + std::stringstream error; + error << " Unexpected field in TracePacket proto. IDs from root to child"; + for (int a : *parent_ids) { + error << " : " << a; + } + error << " : " << f.id(); + LOG(DFATAL) << error.rdbuf(); + continue; + } + if (root->sub_messages && root->sub_messages[index] != nullptr) { + ProtoDecoder decoder(f.data(), f.size()); + parent_ids->push_back(f.id()); + VerifyProtoRecursive(root->sub_messages[index], &decoder, parent_ids); + parent_ids->pop_back(); + } + } +} + +// Verifies that the |proto| contains only accepted fields. +void VerifyProto(const MessageInfo* root, ProtoDecoder* proto) { + std::vector<uint32_t> parent_ids; + VerifyProtoRecursive(root, proto, &parent_ids); +} + +} // namespace + +PrivacyFilteringCheck::PrivacyFilteringCheck() = default; +PrivacyFilteringCheck::~PrivacyFilteringCheck() = default; + +// static +void PrivacyFilteringCheck::CheckProtoForUnexpectedFields( + const std::string& serialized_trace_proto) { + perfetto::protos::pbzero::Trace::Decoder trace( + reinterpret_cast<const uint8_t*>(serialized_trace_proto.data()), + serialized_trace_proto.size()); + + for (auto it = trace.packet(); !!it; ++it) { + TracePacket::Decoder packet(it->data(), it->size()); + const MessageInfo* root = &kTracePacket; + VerifyProto(root, &packet); + + if (packet.has_track_event()) { + ++stats_.track_event; + } else if (packet.has_process_descriptor()) { + ++stats_.process_desc; + } else if (packet.has_thread_descriptor()) { + ++stats_.thread_desc; + } + if (packet.has_interned_data()) { + InternedData::Decoder interned_data(packet.interned_data().data, + packet.interned_data().size); + stats_.has_interned_names |= interned_data.has_legacy_event_names(); + stats_.has_interned_categories |= interned_data.has_event_categories(); + stats_.has_interned_source_locations |= + interned_data.has_source_locations(); + } + } +} + +} // namespace tracing diff --git a/chromium/services/tracing/perfetto/privacy_filtering_check.h b/chromium/services/tracing/perfetto/privacy_filtering_check.h new file mode 100644 index 00000000000..fc76da7c1ae --- /dev/null +++ b/chromium/services/tracing/perfetto/privacy_filtering_check.h @@ -0,0 +1,41 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_TRACING_PERFETTO_PRIVACY_FILTERING_CHECK_H_ +#define SERVICES_TRACING_PERFETTO_PRIVACY_FILTERING_CHECK_H_ + +#include <string> + +#include "base/macros.h" + +namespace tracing { + +class PrivacyFilteringCheck { + public: + struct TraceStats { + size_t track_event = 0; + size_t process_desc = 0; + size_t thread_desc = 0; + + size_t has_interned_names = 0; + size_t has_interned_categories = 0; + size_t has_interned_source_locations = 0; + }; + + PrivacyFilteringCheck(); + ~PrivacyFilteringCheck(); + + void CheckProtoForUnexpectedFields(const std::string& serialized_trace_proto); + + const TraceStats& stats() const { return stats_; } + + private: + TraceStats stats_; + + DISALLOW_COPY_AND_ASSIGN(PrivacyFilteringCheck); +}; + +} // namespace tracing + +#endif // SERVICES_TRACING_PERFETTO_PRIVACY_FILTERING_CHECK_H_ diff --git a/chromium/services/tracing/perfetto/producer_host.cc b/chromium/services/tracing/perfetto/producer_host.cc index 0cc87619b00..12da7a6296d 100644 --- a/chromium/services/tracing/perfetto/producer_host.cc +++ b/chromium/services/tracing/perfetto/producer_host.cc @@ -7,7 +7,11 @@ #include <utility> #include "base/bind.h" +#include "base/process/process.h" +#include "services/tracing/perfetto/perfetto_service.h" +#include "services/tracing/public/cpp/perfetto/producer_client.h" #include "services/tracing/public/cpp/perfetto/shared_memory.h" +#include "services/tracing/public/cpp/tracing_features.h" #include "third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h" #include "third_party/perfetto/include/perfetto/tracing/core/data_source_descriptor.h" #include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h" @@ -30,11 +34,32 @@ void ProducerHost::Initialize(mojom::ProducerClientPtr producer_client, producer_client_ = std::move(producer_client); + // Attempt to parse the PID out of the producer name. + // If the Producer is in-process, we: + // * Signal Perfetto that it should create and manage its own + // SharedMemoryArbiter + // when we call ConnectProducer. + // * Set the local ProducerClient instance to use this SMA instead of passing + // an SMB handle over Mojo and letting it create its own. + // * After that point, any TraceWriter created by TraceEventDataSource will + // use this in-process SMA, and hence be able to synchronously flush full + // SMB chunks if we're running on the same sequence as the Perfetto service, + // hence we avoid any deadlocking occurring from trace events emitted from + // the Perfetto sequence filling up the SMB and stalling while waiting for + // Perfetto to free the chunks. + if (!base::FeatureList::IsEnabled( + features::kPerfettoForceOutOfProcessProducer)) { + base::ProcessId pid; + if (PerfettoService::ParsePidFromProducerName(name, &pid)) { + is_in_process_ = (pid == base::Process::Current().Pid()); + } + } + // TODO(oysteine): Figure out an uid once we need it. // TODO(oysteine): Figure out a good buffer size. producer_endpoint_ = service->ConnectProducer( this, 0 /* uid */, name, - 4 * 1024 * 1024 /* shared_memory_size_hint_bytes */); + 4 * 1024 * 1024 /* shared_memory_size_hint_bytes */, is_in_process_); DCHECK(producer_endpoint_); } @@ -47,6 +72,14 @@ void ProducerHost::OnDisconnect() { } void ProducerHost::OnTracingSetup() { + if (is_in_process_) { + PerfettoTracedProcess::Get() + ->producer_client() + ->set_in_process_shmem_arbiter( + producer_endpoint_->GetInProcessShmemArbiter()); + return; + } + MojoSharedMemory* shared_memory = static_cast<MojoSharedMemory*>(producer_endpoint_->shared_memory()); DCHECK(shared_memory); @@ -98,6 +131,9 @@ void ProducerHost::Flush( producer_client_->Flush(id, data_source_ids); } +void ProducerHost::ClearIncrementalState(const perfetto::DataSourceInstanceID*, + size_t) {} + // This data can come from a malicious child process. We don't do any // sanitization here because ProducerEndpoint::CommitData() (And any other // ProducerEndpoint methods) are designed to deal with malformed / malicious diff --git a/chromium/services/tracing/perfetto/producer_host.h b/chromium/services/tracing/perfetto/producer_host.h index 1637a7bde5a..9fe46160602 100644 --- a/chromium/services/tracing/perfetto/producer_host.h +++ b/chromium/services/tracing/perfetto/producer_host.h @@ -61,6 +61,9 @@ class ProducerHost : public tracing::mojom::ProducerHost, void Flush(perfetto::FlushRequestID, const perfetto::DataSourceInstanceID* raw_data_source_ids, size_t num_data_sources) override; + void ClearIncrementalState( + const perfetto::DataSourceInstanceID* data_source_ids, + size_t num_data_sources) override; // mojom::ProducerHost implementation. // This interface gets called by the per-process ProducerClients @@ -85,6 +88,7 @@ class ProducerHost : public tracing::mojom::ProducerHost, private: mojom::ProducerClientPtr producer_client_; + bool is_in_process_ = false; protected: // Perfetto guarantees that no OnXX callbacks are invoked on |this| diff --git a/chromium/services/tracing/perfetto/test_utils.cc b/chromium/services/tracing/perfetto/test_utils.cc index c5bc6fbd68b..9ba987d9c8d 100644 --- a/chromium/services/tracing/perfetto/test_utils.cc +++ b/chromium/services/tracing/perfetto/test_utils.cc @@ -1,11 +1,12 @@ // Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "services/tracing/perfetto/test_utils.h" + #include <utility> #include "base/bind.h" #include "base/run_loop.h" -#include "services/tracing/perfetto/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h" #include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h" @@ -20,7 +21,9 @@ namespace tracing { TestDataSource::TestDataSource(const std::string& data_source_name, size_t send_packet_count) - : DataSourceBase(data_source_name), send_packet_count_(send_packet_count) {} + : DataSourceBase(data_source_name), send_packet_count_(send_packet_count) { + PerfettoTracedProcess::Get()->AddDataSource(this); +} TestDataSource::~TestDataSource() = default; @@ -30,7 +33,7 @@ void TestDataSource::WritePacketBigly() { payload.get()[kLargeMessageSize - 1] = 0; std::unique_ptr<perfetto::TraceWriter> writer = - producer_client_->CreateTraceWriter(config_.target_buffer()); + producer_->CreateTraceWriter(config_.target_buffer()); CHECK(writer); writer->NewTracePacket()->set_for_testing()->set_str(payload.get(), @@ -38,14 +41,14 @@ void TestDataSource::WritePacketBigly() { } void TestDataSource::StartTracing( - ProducerClient* producer_client, + PerfettoProducer* producer, const perfetto::DataSourceConfig& data_source_config) { - producer_client_ = producer_client; + producer_ = producer; config_ = data_source_config; if (send_packet_count_ > 0) { std::unique_ptr<perfetto::TraceWriter> writer = - producer_client_->CreateTraceWriter(config_.target_buffer()); + producer_->CreateTraceWriter(config_.target_buffer()); CHECK(writer); for (size_t i = 0; i < send_packet_count_; i++) { @@ -65,21 +68,30 @@ void TestDataSource::Flush(base::RepeatingClosure flush_complete_callback) { } MockProducerClient::MockProducerClient( - size_t send_packet_count, base::OnceClosure client_enabled_callback, base::OnceClosure client_disabled_callback) - : client_enabled_callback_(std::move(client_enabled_callback)), - client_disabled_callback_(std::move(client_disabled_callback)), - send_packet_count_(send_packet_count) {} - -MockProducerClient::~MockProducerClient() = default; + : ProducerClient(PerfettoTracedProcess::Get()->GetTaskRunner()), + client_enabled_callback_(std::move(client_enabled_callback)), + client_disabled_callback_(std::move(client_disabled_callback)) { + // We want to set the ProducerClient to this mock, but that 'requires' passing + // ownership of ourselves to PerfettoTracedProcess. Since someone else manages + // our deletion we need to be careful in the deconstructor to not double free + // ourselves. + std::unique_ptr<MockProducerClient> client; + client.reset(this); + old_producer_ = PerfettoTracedProcess::Get()->SetProducerClientForTesting( + std::move(client)); +} -void MockProducerClient::SetupDataSource(const std::string& data_source_name) { - enabled_data_source_ = - std::make_unique<TestDataSource>(data_source_name, send_packet_count_); - AddDataSource(enabled_data_source_.get()); +MockProducerClient::~MockProducerClient() { + // See comment in the constructor. This prevents a double free. + auto client = PerfettoTracedProcess::Get()->SetProducerClientForTesting( + std::move(old_producer_)); + client.release(); } +void MockProducerClient::SetupDataSource(const std::string& data_source_name) {} + void MockProducerClient::StartDataSource( uint64_t id, const perfetto::DataSourceConfig& data_source_config, @@ -192,21 +204,14 @@ MockProducerHost::MockProducerHost( : producer_name_(producer_name), datasource_registered_callback_( std::move(datasource_registered_callback)) { - auto on_mojo_connected_callback = - [](MockProducerClient* producer_client, - const std::string& data_source_name, MockProducerHost* producer_host, - perfetto::TracingService* service, - mojom::ProducerClientPtr producer_client_pipe, - mojom::ProducerHostRequest producer_host_pipe) { - producer_host->OnMessagepipesReadyCallback( - service, std::move(producer_client_pipe), - std::move(producer_host_pipe)); - producer_client->SetupDataSource(data_source_name); - }; - - producer_client->CreateMojoMessagepipes(base::BindOnce( - on_mojo_connected_callback, base::Unretained(producer_client), - data_source_name, base::Unretained(this), base::Unretained(service))); + mojom::ProducerClientPtr client; + mojom::ProducerHostPtrInfo host_info; + auto client_request = mojo::MakeRequest(&client); + Initialize(std::move(client), service, producer_name_); + binding_.Bind(mojo::MakeRequest(&host_info)); + producer_client->BindClientAndHostPipesForTesting(std::move(client_request), + std::move(host_info)); + producer_client->SetupDataSource(data_source_name); } MockProducerHost::~MockProducerHost() = default; @@ -239,23 +244,16 @@ void MockProducerHost::OnCommit( all_host_commit_data_requests_ += proto_string; } -void MockProducerHost::OnMessagepipesReadyCallback( - perfetto::TracingService* perfetto_service, - mojom::ProducerClientPtr producer_client_pipe, - mojom::ProducerHostRequest producer_host_pipe) { - Initialize(std::move(producer_client_pipe), perfetto_service, producer_name_); - binding_ = std::make_unique<mojo::Binding<mojom::ProducerHost>>( - this, std::move(producer_host_pipe)); -} - MockProducer::MockProducer(const std::string& producer_name, const std::string& data_source_name, perfetto::TracingService* service, base::OnceClosure on_datasource_registered, base::OnceClosure on_tracing_started, size_t num_packets) { - producer_client_ = std::make_unique<MockProducerClient>( - num_packets, std::move(on_tracing_started)); + data_source_ = + std::make_unique<TestDataSource>(data_source_name, num_packets); + producer_client_ = + std::make_unique<MockProducerClient>(std::move(on_tracing_started)); producer_host_ = std::make_unique<MockProducerHost>( producer_name, data_source_name, service, producer_client_.get(), @@ -267,11 +265,13 @@ MockProducer::~MockProducer() { } void MockProducer::WritePacketBigly(base::OnceClosure on_write_complete) { - producer_client_->GetTaskRunner()->task_runner()->PostTaskAndReply( - FROM_HERE, - base::BindOnce(&TestDataSource::WritePacketBigly, - base::Unretained(producer_client_->data_source())), - std::move(on_write_complete)); + PerfettoTracedProcess::Get() + ->GetTaskRunner() + ->GetOrCreateTaskRunner() + ->PostTaskAndReply(FROM_HERE, + base::BindOnce(&TestDataSource::WritePacketBigly, + base::Unretained(data_source())), + std::move(on_write_complete)); } } // namespace tracing diff --git a/chromium/services/tracing/perfetto/test_utils.h b/chromium/services/tracing/perfetto/test_utils.h index 2e4486ef726..48385073f67 100644 --- a/chromium/services/tracing/perfetto/test_utils.h +++ b/chromium/services/tracing/perfetto/test_utils.h @@ -9,7 +9,9 @@ #include <string> #include <vector> +#include "mojo/public/cpp/bindings/binding.h" #include "services/tracing/perfetto/producer_host.h" +#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h" #include "services/tracing/public/cpp/perfetto/producer_client.h" #include "third_party/perfetto/include/perfetto/tracing/core/consumer.h" @@ -18,7 +20,7 @@ namespace tracing { const char kPerfettoTestString[] = "d00df00d"; const size_t kLargeMessageSize = 1 * 1024 * 1024; -class TestDataSource : public ProducerClient::DataSourceBase { +class TestDataSource : public PerfettoTracedProcess::DataSourceBase { public: TestDataSource(const std::string& data_source_name, size_t send_packet_count); ~TestDataSource() override; @@ -27,7 +29,7 @@ class TestDataSource : public ProducerClient::DataSourceBase { // DataSourceBase implementation void StartTracing( - ProducerClient* producer_client, + PerfettoProducer* producer, const perfetto::DataSourceConfig& data_source_config) override; void StopTracing( base::OnceClosure stop_complete_callback = base::OnceClosure()) override; @@ -35,16 +37,17 @@ class TestDataSource : public ProducerClient::DataSourceBase { const perfetto::DataSourceConfig& config() { return config_; } + void set_send_packet_count(size_t count) { send_packet_count_ = count; } + private: - ProducerClient* producer_client_ = nullptr; - const size_t send_packet_count_; + PerfettoProducer* producer_ = nullptr; + size_t send_packet_count_; perfetto::DataSourceConfig config_; }; class MockProducerClient : public ProducerClient { public: MockProducerClient( - size_t send_packet_count, base::OnceClosure client_enabled_callback = base::OnceClosure(), base::OnceClosure client_disabled_callback = base::OnceClosure()); ~MockProducerClient() override; @@ -68,14 +71,12 @@ class MockProducerClient : public ProducerClient { return all_client_commit_data_requests_; } - TestDataSource* data_source() { return enabled_data_source_.get(); } private: base::OnceClosure client_enabled_callback_; base::OnceClosure client_disabled_callback_; - size_t send_packet_count_; std::string all_client_commit_data_requests_; - std::unique_ptr<TestDataSource> enabled_data_source_; + std::unique_ptr<ProducerClient> old_producer_; }; class MockConsumer : public perfetto::Consumer { @@ -133,11 +134,6 @@ class MockProducerHost : public ProducerHost { void OnCommit(const perfetto::CommitDataRequest& commit_data_request); - void OnMessagepipesReadyCallback( - perfetto::TracingService* perfetto_service, - mojom::ProducerClientPtr producer_client_pipe, - mojom::ProducerHostRequest producer_host_pipe); - const std::string& all_host_commit_data_requests() const { return all_host_commit_data_requests_; } @@ -146,7 +142,7 @@ class MockProducerHost : public ProducerHost { const std::string producer_name_; base::OnceClosure datasource_registered_callback_; std::string all_host_commit_data_requests_; - std::unique_ptr<mojo::Binding<mojom::ProducerHost>> binding_; + mojo::Binding<mojom::ProducerHost> binding_{this}; }; class MockProducer { @@ -163,7 +159,10 @@ class MockProducer { MockProducerClient* producer_client() { return producer_client_.get(); } + TestDataSource* data_source() { return data_source_.get(); } + private: + std::unique_ptr<TestDataSource> data_source_; std::unique_ptr<MockProducerClient> producer_client_; std::unique_ptr<MockProducerHost> producer_host_; }; diff --git a/chromium/services/tracing/perfetto/track_event_json_exporter_unittest.cc b/chromium/services/tracing/perfetto/track_event_json_exporter_unittest.cc index 704867fad4d..6524eecd79f 100644 --- a/chromium/services/tracing/perfetto/track_event_json_exporter_unittest.cc +++ b/chromium/services/tracing/perfetto/track_event_json_exporter_unittest.cc @@ -54,15 +54,16 @@ class TrackEventJsonExporterTest : public testing::Test { void TearDown() override { json_trace_exporter_.reset(); } - void OnTraceEventJson(const std::string& json, + void OnTraceEventJson(std::string* json, base::DictionaryValue* metadata, bool has_more) { CHECK(!has_more); - unparsed_trace_data_ = json; - parsed_trace_data_ = - base::DictionaryValue::From(base::JSONReader::ReadDeprecated(json)); - ASSERT_TRUE(parsed_trace_data_) << "Couldn't parse json: \n" << json; + unparsed_trace_data_.swap(*json); + parsed_trace_data_ = base::DictionaryValue::From( + base::JSONReader::ReadDeprecated(unparsed_trace_data_)); + ASSERT_TRUE(parsed_trace_data_) << "Couldn't parse json: \n" + << unparsed_trace_data_; // The TraceAnalyzer expects the raw trace output, without the // wrapping root-node. diff --git a/chromium/services/tracing/public/cpp/BUILD.gn b/chromium/services/tracing/public/cpp/BUILD.gn index 3094241434c..90ed03cf395 100644 --- a/chromium/services/tracing/public/cpp/BUILD.gn +++ b/chromium/services/tracing/public/cpp/BUILD.gn @@ -26,13 +26,21 @@ if (!is_nacl && !is_ios) { sources = [ "base_agent.cc", "base_agent.h", + "perfetto/dummy_producer.cc", + "perfetto/dummy_producer.h", "perfetto/interning_index.h", "perfetto/perfetto_config.cc", "perfetto/perfetto_config.h", + "perfetto/perfetto_producer.cc", + "perfetto/perfetto_producer.h", + "perfetto/perfetto_traced_process.cc", + "perfetto/perfetto_traced_process.h", "perfetto/producer_client.cc", "perfetto/producer_client.h", "perfetto/shared_memory.cc", "perfetto/shared_memory.h", + "perfetto/system_producer.cc", + "perfetto/system_producer.h", "perfetto/task_runner.cc", "perfetto/task_runner.h", "perfetto/thread_local_event_sink.cc", diff --git a/chromium/services/tracing/public/cpp/perfetto/dummy_producer.cc b/chromium/services/tracing/public/cpp/perfetto/dummy_producer.cc new file mode 100644 index 00000000000..6558054f589 --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/dummy_producer.cc @@ -0,0 +1,65 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/tracing/public/cpp/perfetto/dummy_producer.h" + +namespace tracing { + +DummyProducer::DummyProducer(PerfettoTaskRunner* task_runner) + : SystemProducer(task_runner) {} +DummyProducer::~DummyProducer() {} + +// perfetto::Producer functions. +void DummyProducer::OnConnect() {} +void DummyProducer::OnDisconnect() {} +void DummyProducer::OnTracingSetup() {} +void DummyProducer::SetupDataSource(perfetto::DataSourceInstanceID, + const perfetto::DataSourceConfig&) {} +void DummyProducer::StartDataSource(perfetto::DataSourceInstanceID, + const perfetto::DataSourceConfig&) {} +void DummyProducer::StopDataSource(perfetto::DataSourceInstanceID) {} +void DummyProducer::Flush(perfetto::FlushRequestID, + const perfetto::DataSourceInstanceID* data_source_ids, + size_t num_data_sources) {} +void DummyProducer::ClearIncrementalState( + const perfetto::DataSourceInstanceID* data_source_ids, + size_t num_data_sources) {} + +// perfetto::TracingService::ProducerEndpoint functions. +void DummyProducer::RegisterDataSource(const perfetto::DataSourceDescriptor&) {} +void DummyProducer::UnregisterDataSource(const std::string& name) {} + +void DummyProducer::RegisterTraceWriter(uint32_t writer_id, + uint32_t target_buffer) {} +void DummyProducer::UnregisterTraceWriter(uint32_t writer_id) {} + +void DummyProducer::CommitData(const perfetto::CommitDataRequest& commit, + CommitDataCallback callback) {} +perfetto::SharedMemory* DummyProducer::shared_memory() const { + return nullptr; +} +size_t DummyProducer::shared_buffer_page_size_kb() const { + return 0; +} +perfetto::SharedMemoryArbiter* DummyProducer::GetSharedMemoryArbiter() { + return nullptr; +} +perfetto::SharedMemoryArbiter* DummyProducer::GetInProcessShmemArbiter() { + return nullptr; +} +void DummyProducer::NotifyFlushComplete(perfetto::FlushRequestID) {} + +void DummyProducer::NotifyDataSourceStarted(perfetto::DataSourceInstanceID) {} +void DummyProducer::NotifyDataSourceStopped(perfetto::DataSourceInstanceID) {} + +void DummyProducer::ActivateTriggers(const std::vector<std::string>&) {} + +// tracing::PerfettoProducer functions. +void DummyProducer::NewDataSourceAdded( + const PerfettoTracedProcess::DataSourceBase* const data_source) {} + +// Functions expected for SystemProducer +void DummyProducer::Disconnect() {} + +} // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/dummy_producer.h b/chromium/services/tracing/public/cpp/perfetto/dummy_producer.h new file mode 100644 index 00000000000..a79383f465c --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/dummy_producer.h @@ -0,0 +1,59 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_TRACING_PUBLIC_CPP_PERFETTO_DUMMY_PRODUCER_H_ +#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_DUMMY_PRODUCER_H_ + +#include "services/tracing/public/cpp/perfetto/system_producer.h" +#include "third_party/perfetto/include/perfetto/tracing/core/producer.h" + +namespace tracing { + +class COMPONENT_EXPORT(TRACING_CPP) DummyProducer : public SystemProducer { + public: + DummyProducer(PerfettoTaskRunner* task_runner); + ~DummyProducer() override; + + // perfetto::Producer functions. + void OnConnect() override; + void OnDisconnect() override; + void OnTracingSetup() override; + void SetupDataSource(perfetto::DataSourceInstanceID, + const perfetto::DataSourceConfig&) override; + void StartDataSource(perfetto::DataSourceInstanceID, + const perfetto::DataSourceConfig&) override; + void StopDataSource(perfetto::DataSourceInstanceID) override; + void Flush(perfetto::FlushRequestID, + const perfetto::DataSourceInstanceID* data_source_ids, + size_t num_data_sources) override; + void ClearIncrementalState( + const perfetto::DataSourceInstanceID* data_source_ids, + size_t num_data_sources) override; + + // perfetto::TracingService::ProducerEndpoint functions. + void RegisterDataSource(const perfetto::DataSourceDescriptor&) override; + void UnregisterDataSource(const std::string& name) override; + void RegisterTraceWriter(uint32_t writer_id, uint32_t target_buffer) override; + void UnregisterTraceWriter(uint32_t writer_id) override; + void CommitData(const perfetto::CommitDataRequest& commit, + CommitDataCallback callback) override; + perfetto::SharedMemory* shared_memory() const override; + size_t shared_buffer_page_size_kb() const override; + perfetto::SharedMemoryArbiter* GetSharedMemoryArbiter() override; + perfetto::SharedMemoryArbiter* GetInProcessShmemArbiter() override; + void NotifyFlushComplete(perfetto::FlushRequestID) override; + void NotifyDataSourceStarted(perfetto::DataSourceInstanceID) override; + void NotifyDataSourceStopped(perfetto::DataSourceInstanceID) override; + void ActivateTriggers(const std::vector<std::string>&) override; + + // tracing::PerfettoProducer functions. + void NewDataSourceAdded( + const PerfettoTracedProcess::DataSourceBase* const data_source) override; + + // Functions expected for SystemProducer + void Disconnect() override; +}; +} // namespace tracing + +#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_DUMMY_PRODUCER_H_ diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_config.cc b/chromium/services/tracing/public/cpp/perfetto/perfetto_config.cc index b21ba02a792..84fbef75bd0 100644 --- a/chromium/services/tracing/public/cpp/perfetto/perfetto_config.cc +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_config.cc @@ -14,8 +14,28 @@ namespace tracing { +namespace { + +perfetto::TraceConfig::DataSource* AddDataSourceConfig( + perfetto::TraceConfig* perfetto_config, + const char* name, + const std::string& chrome_config_string, + bool privacy_filtering_enabled) { + auto* data_source = perfetto_config->add_data_sources(); + auto* source_config = data_source->mutable_config(); + source_config->set_name(name); + source_config->set_target_buffer(0); + auto* chrome_config = source_config->mutable_chrome_config(); + chrome_config->set_trace_config(chrome_config_string); + chrome_config->set_privacy_filtering_enabled(privacy_filtering_enabled); + return data_source; +} + +} // namespace + perfetto::TraceConfig GetDefaultPerfettoConfig( - const base::trace_event::TraceConfig& chrome_config) { + const base::trace_event::TraceConfig& chrome_config, + bool privacy_filtering_enabled) { perfetto::TraceConfig perfetto_config; size_t size_limit = chrome_config.GetTraceBufferSizeInKb(); @@ -26,16 +46,10 @@ perfetto::TraceConfig GetDefaultPerfettoConfig( // Perfetto uses clock_gettime for its internal snapshotting, which gets // blocked by the sandboxed and isn't needed for Chrome regardless. - perfetto_config.set_disable_clock_snapshotting(true); - - // Capture actual trace events. - auto* trace_event_data_source = perfetto_config.add_data_sources(); - for (auto& enabled_pid : - chrome_config.process_filter_config().included_process_ids()) { - *trace_event_data_source->add_producer_name_filter() = base::StrCat( - {mojom::kPerfettoProducerNamePrefix, - base::NumberToString(static_cast<uint32_t>(enabled_pid))}); - } + auto* builtin_data_sources = perfetto_config.mutable_builtin_data_sources(); + builtin_data_sources->set_disable_clock_snapshotting(true); + builtin_data_sources->set_disable_trace_config(privacy_filtering_enabled); + builtin_data_sources->set_disable_system_info(privacy_filtering_enabled); // We strip the process filter from the config string we send to Perfetto, // so perfetto doesn't reject it from a future @@ -46,39 +60,33 @@ perfetto::TraceConfig GetDefaultPerfettoConfig( base::trace_event::TraceConfig::ProcessFilterConfig()); std::string chrome_config_string = processfilter_stripped_config.ToString(); - auto* trace_event_config = trace_event_data_source->mutable_config(); - trace_event_config->set_name(tracing::mojom::kTraceEventDataSourceName); - trace_event_config->set_target_buffer(0); - auto* chrome_proto_config = trace_event_config->mutable_chrome_config(); - chrome_proto_config->set_trace_config(chrome_config_string); + // Capture actual trace events. + auto* trace_event_data_source = AddDataSourceConfig( + &perfetto_config, tracing::mojom::kTraceEventDataSourceName, + chrome_config_string, privacy_filtering_enabled); + for (auto& enabled_pid : + chrome_config.process_filter_config().included_process_ids()) { + *trace_event_data_source->add_producer_name_filter() = base::StrCat( + {mojom::kPerfettoProducerNamePrefix, + base::NumberToString(static_cast<uint32_t>(enabled_pid))}); + } // Capture system trace events if supported and enabled. The datasources will // only emit events if system tracing is enabled in |chrome_config|. #if defined(OS_CHROMEOS) || (defined(IS_CHROMECAST) && defined(OS_LINUX)) - auto* system_trace_config = - perfetto_config.add_data_sources()->mutable_config(); - system_trace_config->set_name(tracing::mojom::kSystemTraceDataSourceName); - system_trace_config->set_target_buffer(0); - auto* system_chrome_config = system_trace_config->mutable_chrome_config(); - system_chrome_config->set_trace_config(chrome_config_string); + AddDataSourceConfig(&perfetto_config, + tracing::mojom::kSystemTraceDataSourceName, + chrome_config_string, privacy_filtering_enabled); #endif #if defined(OS_CHROMEOS) - auto* arc_trace_config = perfetto_config.add_data_sources()->mutable_config(); - arc_trace_config->set_name(tracing::mojom::kArcTraceDataSourceName); - arc_trace_config->set_target_buffer(0); - auto* arc_chrome_config = arc_trace_config->mutable_chrome_config(); - arc_chrome_config->set_trace_config(chrome_config_string); + AddDataSourceConfig(&perfetto_config, tracing::mojom::kArcTraceDataSourceName, + chrome_config_string, privacy_filtering_enabled); #endif // Also capture global metadata. - auto* trace_metadata_config = - perfetto_config.add_data_sources()->mutable_config(); - trace_metadata_config->set_name(tracing::mojom::kMetaDataSourceName); - trace_metadata_config->set_target_buffer(0); - auto* metadata_chrome_config = trace_metadata_config->mutable_chrome_config(); - metadata_chrome_config->set_trace_config(chrome_config_string); - // TODO(ssid): Also set privacy_filtering_enabled here. + AddDataSourceConfig(&perfetto_config, tracing::mojom::kMetaDataSourceName, + chrome_config_string, privacy_filtering_enabled); return perfetto_config; } diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_config.h b/chromium/services/tracing/public/cpp/perfetto/perfetto_config.h index 84fef0f9065..b4616787278 100644 --- a/chromium/services/tracing/public/cpp/perfetto/perfetto_config.h +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_config.h @@ -17,7 +17,8 @@ class TraceConfig; namespace tracing { perfetto::TraceConfig COMPONENT_EXPORT(TRACING_CPP) GetDefaultPerfettoConfig( - const base::trace_event::TraceConfig& chrome_config); + const base::trace_event::TraceConfig& chrome_config, + bool privacy_filtering_enabled = false); } // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_producer.cc b/chromium/services/tracing/public/cpp/perfetto/perfetto_producer.cc new file mode 100644 index 00000000000..f5a59521a6d --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_producer.cc @@ -0,0 +1,44 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/tracing/public/cpp/perfetto/perfetto_producer.h" + +#include "third_party/perfetto/include/perfetto/tracing/core/shared_memory_arbiter.h" +#include "third_party/perfetto/include/perfetto/tracing/core/startup_trace_writer_registry.h" +#include "third_party/perfetto/include/perfetto/tracing/core/trace_writer.h" + +namespace tracing { + +PerfettoProducer::PerfettoProducer(PerfettoTaskRunner* task_runner) + : task_runner_(task_runner) { + DCHECK(task_runner_); +} + +PerfettoProducer::~PerfettoProducer() {} + +void PerfettoProducer::BindStartupTraceWriterRegistry( + std::unique_ptr<perfetto::StartupTraceWriterRegistry> registry, + perfetto::BufferID target_buffer) { + DCHECK(GetSharedMemoryArbiter()); + return GetSharedMemoryArbiter()->BindStartupTraceWriterRegistry( + std::move(registry), target_buffer); +} + +std::unique_ptr<perfetto::TraceWriter> PerfettoProducer::CreateTraceWriter( + perfetto::BufferID target_buffer) { + DCHECK(GetSharedMemoryArbiter()); + return GetSharedMemoryArbiter()->CreateTraceWriter(target_buffer); +} + +PerfettoTaskRunner* PerfettoProducer::task_runner() { + return task_runner_; +} + +void PerfettoProducer::DeleteSoonForTesting( + std::unique_ptr<PerfettoProducer> perfetto_producer) { + PerfettoTracedProcess::GetTaskRunner()->GetOrCreateTaskRunner()->DeleteSoon( + FROM_HERE, std::move(perfetto_producer)); +} + +} // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_producer.h b/chromium/services/tracing/public/cpp/perfetto/perfetto_producer.h new file mode 100644 index 00000000000..d5a18aafd5f --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_producer.h @@ -0,0 +1,73 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_PRODUCER_H_ +#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_PRODUCER_H_ + +#include <memory> + +#include "base/component_export.h" +#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h" +#include "third_party/perfetto/include/perfetto/tracing/core/tracing_service.h" + +namespace perfetto { +class SharedMemoryArbiter; +class StartupTraceWriterRegistry; +} // namespace perfetto + +namespace tracing { + +// This class represents the perfetto producer endpoint which is used for +// producers to talk to the Perfetto service. It also provides methods to +// interact with the shared memory buffer by binding and creating TraceWriters. +// +// In addition to the PerfettoProducers' pure virtual methods, subclasses must +// implement the remaining methods of the ProducerEndpoint interface. +class COMPONENT_EXPORT(TRACING_CPP) PerfettoProducer + : public perfetto::TracingService::ProducerEndpoint { + public: + PerfettoProducer(PerfettoTaskRunner* task_runner); + + ~PerfettoProducer() override; + + // Binds the registry and its trace writers to the ProducerClient's SMB, to + // write into the given target buffer. The ownership of |registry| is + // transferred to PerfettoProducer (and its SharedMemoryArbiter). + // + // Should only be called while a tracing session is active and a + // SharedMemoryArbiter exists. + void BindStartupTraceWriterRegistry( + std::unique_ptr<perfetto::StartupTraceWriterRegistry> registry, + perfetto::BufferID target_buffer); + + // Used by the DataSource implementations to create TraceWriters + // for writing their protobufs, and respond to flushes. + // + // Should only be called while a tracing session is active and a + // SharedMemoryArbiter exists. + std::unique_ptr<perfetto::TraceWriter> CreateTraceWriter( + perfetto::BufferID target_buffer) override; + + // Informs the PerfettoProducer a new Data Source was added. This instance + // will also be found in |data_sources| having just be inserted before this + // method is called by PerfettoTracedProcess. This enables the + // PerfettoProducer to perform initialization on new data sources. + virtual void NewDataSourceAdded( + const PerfettoTracedProcess::DataSourceBase* const data_source) = 0; + + static void DeleteSoonForTesting( + std::unique_ptr<PerfettoProducer> perfetto_producer); + + protected: + // Returns the SMA of the SharedMemory from the perfetto service or nullptr if + // not initialized (no trace has ever been started). + virtual perfetto::SharedMemoryArbiter* GetSharedMemoryArbiter() = 0; + + PerfettoTaskRunner* task_runner(); + + private: + PerfettoTaskRunner* const task_runner_; +}; +} // namespace tracing +#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_PRODUCER_H_ diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc b/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc new file mode 100644 index 00000000000..96cdf91440e --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.cc @@ -0,0 +1,105 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h" + +#include "base/no_destructor.h" +#include "base/task/post_task.h" +#include "services/tracing/public/cpp/perfetto/producer_client.h" + +#include "services/tracing/public/cpp/perfetto/dummy_producer.h" + +namespace tracing { +namespace { +std::unique_ptr<SystemProducer> NewSystemProducer(PerfettoTaskRunner* runner) { + return std::make_unique<DummyProducer>(runner); +} +} // namespace + +PerfettoTracedProcess::DataSourceBase::DataSourceBase(const std::string& name) + : name_(name) { + DCHECK(!name.empty()); +} + +PerfettoTracedProcess::DataSourceBase::~DataSourceBase() = default; + +void PerfettoTracedProcess::DataSourceBase::StartTracingWithID( + uint64_t data_source_id, + PerfettoProducer* producer_client, + const perfetto::DataSourceConfig& data_source_config) { + data_source_id_ = data_source_id; + StartTracing(producer_client, data_source_config); +} + +// static +PerfettoTracedProcess* PerfettoTracedProcess::Get() { + static base::NoDestructor<PerfettoTracedProcess> traced_process; + return traced_process.get(); +} + +PerfettoTracedProcess::PerfettoTracedProcess() + : producer_client_(std::make_unique<ProducerClient>(GetTaskRunner())), + system_producer_endpoint_(NewSystemProducer(GetTaskRunner())), + weak_ptr_factory_(this) { + DETACH_FROM_SEQUENCE(sequence_checker_); +} + +PerfettoTracedProcess::~PerfettoTracedProcess() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + +void PerfettoTracedProcess::ClearDataSourcesForTesting() { + data_sources_.clear(); +} + +std::unique_ptr<ProducerClient> +PerfettoTracedProcess::SetProducerClientForTesting( + std::unique_ptr<ProducerClient> client) { + auto old_producer_client_for_testing = std::move(producer_client_); + producer_client_ = std::move(client); + return old_producer_client_for_testing; +} + +// static +void PerfettoTracedProcess::DeleteSoonForTesting( + std::unique_ptr<PerfettoTracedProcess> perfetto_traced_process) { + GetTaskRunner()->GetOrCreateTaskRunner()->DeleteSoon( + FROM_HERE, std::move(perfetto_traced_process)); +} + +// We never destroy the taskrunner as we may need it for cleanup +// of TraceWriters in TLS, which could happen after the PerfettoTracedProcess +// is deleted. +// static +PerfettoTaskRunner* PerfettoTracedProcess::GetTaskRunner() { + static base::NoDestructor<PerfettoTaskRunner> task_runner(nullptr); + return task_runner.get(); +} + +// static +void PerfettoTracedProcess::ResetTaskRunnerForTesting() { + DETACH_FROM_SEQUENCE(PerfettoTracedProcess::Get()->sequence_checker_); + GetTaskRunner()->ResetTaskRunnerForTesting(nullptr); +} + +void PerfettoTracedProcess::AddDataSource(DataSourceBase* data_source) { + GetTaskRunner()->GetOrCreateTaskRunner()->PostTask( + FROM_HERE, base::BindOnce(&PerfettoTracedProcess::AddDataSourceOnSequence, + base::Unretained(this), data_source)); +} + +ProducerClient* PerfettoTracedProcess::producer_client() { + return producer_client_.get(); +} + +void PerfettoTracedProcess::AddDataSourceOnSequence( + DataSourceBase* data_source) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (data_sources_.insert(data_source).second) { + producer_client_->NewDataSourceAdded(data_source); + system_producer_endpoint_->NewDataSourceAdded(data_source); + } +} +} // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.h b/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.h new file mode 100644 index 00000000000..abd7b7f5dcf --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/perfetto_traced_process.h @@ -0,0 +1,112 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_TRACED_PROCESS_H_ +#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_TRACED_PROCESS_H_ + +#include "base/component_export.h" +#include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" +#include "services/tracing/public/cpp/perfetto/task_runner.h" + +namespace tracing { + +class PerfettoProducer; +class ProducerClient; +class SystemProducer; + +// This represents global process level state that the Perfetto tracing system +// expects to exist. This includes a single base implementation of DataSources +// all implementors should use and the perfetto task runner that should be used +// when talking to the tracing system to prevent deadlocks. +// +// Implementations of new DataSources should: +// * Implement PerfettoTracedProcess::DataSourceBase. +// * Add a new data source name in perfetto_service.mojom. +// * Register the data source with Perfetto in ProducerHost::OnConnect. +// * Construct the new implementation when requested to +// in PerfettoProducer::StartDataSource. +class COMPONENT_EXPORT(TRACING_CPP) PerfettoTracedProcess final { + public: + class DataSourceBase { + public: + explicit DataSourceBase(const std::string& name); + virtual ~DataSourceBase(); + + void StartTracingWithID( + uint64_t data_source_id, + PerfettoProducer* producer_client, + const perfetto::DataSourceConfig& data_source_config); + + virtual void StartTracing( + PerfettoProducer* producer_client, + const perfetto::DataSourceConfig& data_source_config) = 0; + virtual void StopTracing( + base::OnceClosure stop_complete_callback = base::OnceClosure()) = 0; + + // Flush the data source. + virtual void Flush(base::RepeatingClosure flush_complete_callback) = 0; + + const std::string& name() const { return name_; } + uint64_t data_source_id() const { return data_source_id_; } + + private: + uint64_t data_source_id_ = 0; + std::string name_; + }; + + // Returns the process-wide instance of the PerfettoTracedProcess. + static PerfettoTracedProcess* Get(); + + ProducerClient* producer_client(); + + ~PerfettoTracedProcess(); + + // Sets the ProducerClient and returns the old pointer. If tests want to + // restore the state of the world they should store the pointer and call this + // method again with it as the parameter. + std::unique_ptr<ProducerClient> SetProducerClientForTesting( + std::unique_ptr<ProducerClient> client); + void ClearDataSourcesForTesting(); + static void DeleteSoonForTesting(std::unique_ptr<PerfettoTracedProcess>); + + // Returns the taskrunner used by any Perfetto service. + static PerfettoTaskRunner* GetTaskRunner(); + + // Add a new data source to the PerfettoTracedProcess; the caller retains + // ownership and is responsible for making sure the data source outlives the + // PerfettoTracedProcess. + void AddDataSource(DataSourceBase*); + const std::set<DataSourceBase*>& data_sources() { return data_sources_; } + + static void ResetTaskRunnerForTesting(); + + protected: + // protected for testing. + PerfettoTracedProcess(); + + private: + friend class base::NoDestructor<PerfettoTracedProcess>; + + void AddDataSourceOnSequence(DataSourceBase* data_source); + + // The canonical set of DataSourceBases alive in this process. These will be + // registered with the tracing service. + std::set<DataSourceBase*> data_sources_; + // A PerfettoProducer that connects to the chrome Perfetto service through + // mojo. + std::unique_ptr<ProducerClient> producer_client_; + // A PerfettoProducer that connects to the system Perfetto service. If there + // is no system Perfetto service this pointer will be valid, but all function + // calls will be noops. + std::unique_ptr<SystemProducer> system_producer_endpoint_; + + SEQUENCE_CHECKER(sequence_checker_); + // NOTE: Weak pointers must be invalidated before all other member + // variables. + base::WeakPtrFactory<PerfettoTracedProcess> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(PerfettoTracedProcess); +}; +} // namespace tracing +#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_PERFETTO_TRACED_PROCESS_H_ diff --git a/chromium/services/tracing/public/cpp/perfetto/producer_client.cc b/chromium/services/tracing/public/cpp/perfetto/producer_client.cc index e416f855402..245624cfff6 100644 --- a/chromium/services/tracing/public/cpp/perfetto/producer_client.cc +++ b/chromium/services/tracing/public/cpp/perfetto/producer_client.cc @@ -8,9 +8,10 @@ #include "base/bind.h" #include "base/no_destructor.h" +#include "base/process/process.h" #include "base/task/post_task.h" -#include "base/task/thread_pool/scheduler_lock_impl.h" #include "services/tracing/public/cpp/perfetto/shared_memory.h" +#include "services/tracing/public/cpp/perfetto/trace_event_data_source.h" #include "services/tracing/public/mojom/constants.mojom.h" #include "third_party/perfetto/include/perfetto/tracing/core/commit_data_request.h" #include "third_party/perfetto/include/perfetto/tracing/core/shared_memory_arbiter.h" @@ -19,52 +20,8 @@ namespace tracing { -namespace { - -scoped_refptr<base::SequencedTaskRunner> CreateTaskRunner() { - return base::CreateSequencedTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); -} - -} // namespace - -ScopedPerfettoPostTaskBlocker::ScopedPerfettoPostTaskBlocker(bool enable) - : enabled_(enable) { - if (enabled_) { - ProducerClient::GetTaskRunner()->BlockPostTaskForThread(); - } else { - base::internal::SchedulerLockImpl::AssertNoLockHeldOnCurrentThread(); - } -} - -ScopedPerfettoPostTaskBlocker::~ScopedPerfettoPostTaskBlocker() { - if (enabled_) { - ProducerClient::GetTaskRunner()->UnblockPostTaskForThread(); - } -} - -ProducerClient::DataSourceBase::DataSourceBase(const std::string& name) - : name_(name) { - DCHECK(!name.empty()); -} - -ProducerClient::DataSourceBase::~DataSourceBase() = default; - -void ProducerClient::DataSourceBase::StartTracingWithID( - uint64_t data_source_id, - ProducerClient* producer_client, - const perfetto::DataSourceConfig& data_source_config) { - data_source_id_ = data_source_id; - StartTracing(producer_client, data_source_config); -} - -// static -ProducerClient* ProducerClient::Get() { - static base::NoDestructor<ProducerClient> producer_client; - return producer_client.get(); -} - -ProducerClient::ProducerClient() : weak_ptr_factory_(this) { +ProducerClient::ProducerClient(PerfettoTaskRunner* task_runner) + : PerfettoProducer(task_runner), weak_ptr_factory_(this) { DETACH_FROM_SEQUENCE(sequence_checker_); } @@ -72,68 +29,36 @@ ProducerClient::~ProducerClient() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -// static -void ProducerClient::DeleteSoonForTesting( - std::unique_ptr<ProducerClient> producer_client) { - GetTaskRunner()->task_runner()->DeleteSoon(FROM_HERE, - std::move(producer_client)); -} - -// We never destroy the taskrunner as we may need it for cleanup -// of TraceWriters in TLS, which could happen after the ProducerClient -// is deleted. -// static -PerfettoTaskRunner* ProducerClient::GetTaskRunner() { - static base::NoDestructor<PerfettoTaskRunner> task_runner(CreateTaskRunner()); - return task_runner.get(); -} - -// static -void ProducerClient::ResetTaskRunnerForTesting() { - DETACH_FROM_SEQUENCE(ProducerClient::Get()->sequence_checker_); - GetTaskRunner()->ResetTaskRunnerForTesting(CreateTaskRunner()); -} - void ProducerClient::Connect(mojom::PerfettoServicePtr perfetto_service) { - CreateMojoMessagepipes(base::BindOnce( - [](mojom::PerfettoServicePtr perfetto_service, - mojom::ProducerClientPtr producer_client_pipe, - mojom::ProducerHostRequest producer_host_pipe) { - perfetto_service->ConnectToProducerHost(std::move(producer_client_pipe), - std::move(producer_host_pipe)); - }, - std::move(perfetto_service))); -} - -void ProducerClient::CreateMojoMessagepipes( - MessagepipesReadyCallback callback) { - auto origin_task_runner = base::SequencedTaskRunnerHandle::Get(); - DCHECK(origin_task_runner); - mojom::ProducerClientPtr producer_client; - GetTaskRunner()->task_runner()->PostTask( + mojom::ProducerClientPtr client; + auto client_request = mojo::MakeRequest(&client); + mojom::ProducerHostPtrInfo host_info; + perfetto_service->ConnectToProducerHost(std::move(client), + mojo::MakeRequest(&host_info)); + task_runner()->GetOrCreateTaskRunner()->PostTask( FROM_HERE, - base::BindOnce(&ProducerClient::CreateMojoMessagepipesOnSequence, - base::Unretained(this), origin_task_runner, - std::move(callback), mojo::MakeRequest(&producer_client), - std::move(producer_client))); + base::BindOnce(&ProducerClient::BindClientAndHostPipesOnSequence, + base::Unretained(this), std::move(client_request), + std::move(host_info))); } -void ProducerClient::BindStartupTraceWriterRegistry( - std::unique_ptr<perfetto::StartupTraceWriterRegistry> registry, - perfetto::BufferID target_buffer) { - shared_memory_arbiter_->BindStartupTraceWriterRegistry(std::move(registry), - target_buffer); +void ProducerClient::BindClientAndHostPipesForTesting( + mojom::ProducerClientRequest producer_client_request, + mojom::ProducerHostPtrInfo producer_host_info) { + task_runner()->GetOrCreateTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(&ProducerClient::BindClientAndHostPipesOnSequence, + base::Unretained(this), std::move(producer_client_request), + std::move(producer_host_info))); } // The Mojo binding should run on the same sequence as the one we get // callbacks from Perfetto on, to avoid additional PostTasks. -void ProducerClient::CreateMojoMessagepipesOnSequence( - scoped_refptr<base::SequencedTaskRunner> origin_task_runner, - MessagepipesReadyCallback callback, +void ProducerClient::BindClientAndHostPipesOnSequence( mojom::ProducerClientRequest producer_client_request, - mojom::ProducerClientPtr producer_client) { + mojom::ProducerHostPtrInfo producer_host_info) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - DCHECK(!binding_ || !binding_->is_bound()); + CHECK(!binding_ || !binding_->is_bound()); binding_ = std::make_unique<mojo::Binding<mojom::ProducerClient>>( this, std::move(producer_client_request)); @@ -143,9 +68,7 @@ void ProducerClient::CreateMojoMessagepipesOnSequence( }, base::Unretained(this))); - origin_task_runner->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), std::move(producer_client), - mojo::MakeRequest(&producer_host_))); + producer_host_.Bind(std::move(producer_host_info)); // TODO(oysteine) We register the data sources in reverse as a temporary // workaround to make sure that the TraceEventDataSource is registered @@ -154,28 +77,17 @@ void ProducerClient::CreateMojoMessagepipesOnSequence( // be enabled, which is done by the TraceEventDataSource. We need to register // the MetadataSource first to ensure that it's also ready. Once the // Perfetto Observer interface is ready, we can remove this. - for (auto it = data_sources_.rbegin(); it != data_sources_.rend(); ++it) { - RegisterDataSourceWithHost(*it); + const auto& data_sources = PerfettoTracedProcess::Get()->data_sources(); + for (auto it = data_sources.crbegin(); it != data_sources.crend(); ++it) { + NewDataSourceAdded(*it); } } -void ProducerClient::AddDataSource(DataSourceBase* data_source) { - GetTaskRunner()->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&ProducerClient::AddDataSourceOnSequence, - base::Unretained(this), data_source)); -} - -void ProducerClient::AddDataSourceOnSequence(DataSourceBase* data_source) { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - - if (data_sources_.insert(data_source).second) { - if (producer_host_) { - RegisterDataSourceWithHost(data_source); - } +void ProducerClient::NewDataSourceAdded( + const PerfettoTracedProcess::DataSourceBase* const data_source) { + if (!producer_host_) { + return; } -} - -void ProducerClient::RegisterDataSourceWithHost(DataSourceBase* data_source) { perfetto::DataSourceDescriptor new_registration; new_registration.set_name(data_source->name()); new_registration.set_will_notify_on_start(true); @@ -183,8 +95,16 @@ void ProducerClient::RegisterDataSourceWithHost(DataSourceBase* data_source) { producer_host_->RegisterDataSource(std::move(new_registration)); } +perfetto::SharedMemoryArbiter* ProducerClient::GetSharedMemoryArbiter() { + return in_process_arbiter_ ? in_process_arbiter_ + : shared_memory_arbiter_.get(); +} + void ProducerClient::OnTracingStart( mojo::ScopedSharedBufferHandle shared_memory) { + // If we're using in-process mode, we don't need to set up our + // own SharedMemoryArbiter. + DCHECK(!in_process_arbiter_); // TODO(oysteine): In next CLs plumb this through the service. const size_t kShmemBufferPageSize = 4096; @@ -195,7 +115,8 @@ void ProducerClient::OnTracingStart( std::make_unique<MojoSharedMemory>(std::move(shared_memory)); shared_memory_arbiter_ = perfetto::SharedMemoryArbiter::CreateInstance( - shared_memory_.get(), kShmemBufferPageSize, this, GetTaskRunner()); + shared_memory_.get(), kShmemBufferPageSize, this, + PerfettoTracedProcess::GetTaskRunner()); } else { // TODO(oysteine): This is assuming the SMB is the same, currently. Swapping // out SharedMemoryBuffers would require more thread synchronization. @@ -210,7 +131,7 @@ void ProducerClient::StartDataSource( DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // TODO(oysteine): Support concurrent tracing sessions. - for (auto* data_source : data_sources_) { + for (auto* data_source : PerfettoTracedProcess::Get()->data_sources()) { if (data_source->name() == data_source_config.name()) { data_source->StartTracingWithID(id, this, data_source_config); // TODO(eseckler): Consider plumbing this callback through |data_source|. @@ -224,7 +145,7 @@ void ProducerClient::StopDataSource(uint64_t id, StopDataSourceCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - for (auto* data_source : data_sources_) { + for (auto* data_source : PerfettoTracedProcess::Get()->data_sources()) { if (data_source->data_source_id() == id) { data_source->StopTracing(std::move(callback)); return; @@ -240,7 +161,7 @@ void ProducerClient::Flush(uint64_t flush_request_id, data_source_ids.size()}; // N^2, optimize once there's more than a couple of possible data sources. - for (auto* data_source : data_sources_) { + for (auto* data_source : PerfettoTracedProcess::Get()->data_sources()) { if (std::find(data_source_ids.begin(), data_source_ids.end(), data_source->data_source_id()) != data_source_ids.end()) { data_source->Flush(base::BindRepeating( @@ -278,6 +199,17 @@ void ProducerClient::ActivateTriggers(const std::vector<std::string>&) { void ProducerClient::CommitData(const perfetto::CommitDataRequest& commit, CommitDataCallback callback) { + // We need to make sure the CommitData IPC is sent off without triggering any + // trace events, as that could stall waiting for SMB chunks to be freed up + // which requires the tracing service to receive the IPC. + if (!TraceEventDataSource::GetThreadIsInTraceEventTLS()->Get()) { + AutoThreadLocalBoolean thread_is_in_trace_event( + TraceEventDataSource::GetThreadIsInTraceEventTLS()); + + producer_host_->CommitData(commit); + return; + } + producer_host_->CommitData(commit); } @@ -310,15 +242,10 @@ void ProducerClient::NotifyFlushComplete(perfetto::FlushRequestID id) { DCHECK_NE(pending_replies_for_latest_flush_.second, 0u); if (--pending_replies_for_latest_flush_.second == 0) { - shared_memory_arbiter_->NotifyFlushComplete(id); + GetSharedMemoryArbiter()->NotifyFlushComplete(id); } } -std::unique_ptr<perfetto::TraceWriter> ProducerClient::CreateTraceWriter( - perfetto::BufferID target_buffer) { - return shared_memory_arbiter_->CreateTraceWriter(target_buffer); -} - void ProducerClient::RegisterTraceWriter(uint32_t writer_id, uint32_t target_buffer) { producer_host_->RegisterTraceWriter(writer_id, target_buffer); diff --git a/chromium/services/tracing/public/cpp/perfetto/producer_client.h b/chromium/services/tracing/public/cpp/perfetto/producer_client.h index 8370a219346..2322307f3ea 100644 --- a/chromium/services/tracing/public/cpp/perfetto/producer_client.h +++ b/chromium/services/tracing/public/cpp/perfetto/producer_client.h @@ -17,102 +17,38 @@ #include "base/memory/weak_ptr.h" #include "base/sequence_checker.h" #include "mojo/public/cpp/bindings/binding.h" +#include "services/tracing/public/cpp/perfetto/perfetto_producer.h" #include "services/tracing/public/cpp/perfetto/task_runner.h" #include "services/tracing/public/mojom/perfetto_service.mojom.h" -#include "third_party/perfetto/include/perfetto/tracing/core/tracing_service.h" namespace perfetto { class SharedMemoryArbiter; -class StartupTraceWriterRegistry; } // namespace perfetto namespace tracing { class MojoSharedMemory; -class ScopedPerfettoPostTaskBlocker { - public: - explicit ScopedPerfettoPostTaskBlocker(bool enable); - ~ScopedPerfettoPostTaskBlocker(); - - private: - const bool enabled_; -}; - // This class is the per-process client side of the Perfetto // producer, and is responsible for creating specific kinds // of DataSources (like ChromeTracing) on demand, and provide // them with TraceWriters and a configuration to start logging. - -// Implementations of new DataSources should: -// * Implement ProducerClient::DataSourceBase. -// * Add a new data source name in perfetto_service.mojom. -// * Register the data source with Perfetto in ProducerHost::OnConnect. -// * Construct the new implementation when requested to -// in ProducerClient::StartDataSource. class COMPONENT_EXPORT(TRACING_CPP) ProducerClient - : public mojom::ProducerClient, - public perfetto::TracingService::ProducerEndpoint { + : public PerfettoProducer, + public mojom::ProducerClient { public: - class DataSourceBase { - public: - explicit DataSourceBase(const std::string& name); - virtual ~DataSourceBase(); - - void StartTracingWithID( - uint64_t data_source_id, - ProducerClient* producer_client, - const perfetto::DataSourceConfig& data_source_config); - - virtual void StartTracing( - ProducerClient* producer_client, - const perfetto::DataSourceConfig& data_source_config) = 0; - virtual void StopTracing( - base::OnceClosure stop_complete_callback = base::OnceClosure()) = 0; - - // Flush the data source. - virtual void Flush(base::RepeatingClosure flush_complete_callback) = 0; - - const std::string& name() const { return name_; } - uint64_t data_source_id() const { return data_source_id_; } - - private: - uint64_t data_source_id_ = 0; - std::string name_; - }; - - // Returns the process-wide instance of the ProducerClient. - static ProducerClient* Get(); - + ProducerClient(PerfettoTaskRunner* task_runner); ~ProducerClient() override; - static void DeleteSoonForTesting(std::unique_ptr<ProducerClient>); - - // Returns the taskrunner used by Perfetto. - static PerfettoTaskRunner* GetTaskRunner(); + void NewDataSourceAdded( + const PerfettoTracedProcess::DataSourceBase* const data_source) override; void Connect(mojom::PerfettoServicePtr perfetto_service); - // Create the messagepipes that'll be used to connect - // to the service-side ProducerHost, on the correct - // sequence. The callback will be called on same sequence - // as CreateMojoMessagepipes() got called on. - using MessagepipesReadyCallback = - base::OnceCallback<void(mojom::ProducerClientPtr, - mojom::ProducerHostRequest)>; - void CreateMojoMessagepipes(MessagepipesReadyCallback); - - // Binds the registry and its trace writers to the ProducerClient's SMB, to - // write into the given target buffer. The ownership of |registry| is - // transferred to ProducerClient (and its SharedMemoryArbiter). - void BindStartupTraceWriterRegistry( - std::unique_ptr<perfetto::StartupTraceWriterRegistry> registry, - perfetto::BufferID target_buffer); - - // Add a new data source to the ProducerClient; the caller - // retains ownership and is responsible for making sure - // the data source outlives the ProducerClient. - void AddDataSource(DataSourceBase*); + void set_in_process_shmem_arbiter(perfetto::SharedMemoryArbiter* arbiter) { + DCHECK(!in_process_arbiter_); + in_process_arbiter_ = arbiter; + } // mojom::ProducerClient implementation. // Called through Mojo by the ProducerHost on the service-side to control @@ -134,8 +70,6 @@ class COMPONENT_EXPORT(TRACING_CPP) ProducerClient CommitDataCallback callback) override; // Used by the DataSource implementations to create TraceWriters // for writing their protobufs, and respond to flushes. - std::unique_ptr<perfetto::TraceWriter> CreateTraceWriter( - perfetto::BufferID target_buffer) override; void NotifyFlushComplete(perfetto::FlushRequestID) override; perfetto::SharedMemory* shared_memory() const override; void RegisterTraceWriter(uint32_t writer_id, uint32_t target_buffer) override; @@ -151,32 +85,24 @@ class COMPONENT_EXPORT(TRACING_CPP) ProducerClient size_t shared_buffer_page_size_kb() const override; perfetto::SharedMemoryArbiter* GetInProcessShmemArbiter() override; - static void ResetTaskRunnerForTesting(); + void BindClientAndHostPipesForTesting(mojom::ProducerClientRequest, + mojom::ProducerHostPtrInfo); protected: - // protected for testing. - ProducerClient(); + perfetto::SharedMemoryArbiter* GetSharedMemoryArbiter() override; private: friend class base::NoDestructor<ProducerClient>; void CommitDataOnSequence(const perfetto::CommitDataRequest& request); - void AddDataSourceOnSequence(DataSourceBase*); - void RegisterDataSourceWithHost(DataSourceBase* data_source); - - // The callback will be run on the |origin_task_runner|, meaning - // the same sequence as CreateMojoMessagePipes() got called on. - void CreateMojoMessagepipesOnSequence( - scoped_refptr<base::SequencedTaskRunner> origin_task_runner, - MessagepipesReadyCallback, - mojom::ProducerClientRequest, - mojom::ProducerClientPtr); + void BindClientAndHostPipesOnSequence(mojom::ProducerClientRequest, + mojom::ProducerHostPtrInfo); std::unique_ptr<mojo::Binding<mojom::ProducerClient>> binding_; - std::unique_ptr<perfetto::SharedMemoryArbiter> shared_memory_arbiter_; mojom::ProducerHostPtr producer_host_; std::unique_ptr<MojoSharedMemory> shared_memory_; - std::set<DataSourceBase*> data_sources_; + std::unique_ptr<perfetto::SharedMemoryArbiter> shared_memory_arbiter_; + perfetto::SharedMemoryArbiter* in_process_arbiter_ = nullptr; // First value is the flush ID, the second is the number of // replies we're still waiting for. std::pair<uint64_t, size_t> pending_replies_for_latest_flush_; diff --git a/chromium/services/tracing/public/cpp/perfetto/system_producer.cc b/chromium/services/tracing/public/cpp/perfetto/system_producer.cc new file mode 100644 index 00000000000..f1ffbf294c0 --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/system_producer.cc @@ -0,0 +1,10 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "services/tracing/public/cpp/perfetto/system_producer.h" + +namespace tracing { +SystemProducer::SystemProducer(PerfettoTaskRunner* task_runner) + : PerfettoProducer(task_runner) {} +} // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/system_producer.h b/chromium/services/tracing/public/cpp/perfetto/system_producer.h new file mode 100644 index 00000000000..e37274c42d2 --- /dev/null +++ b/chromium/services/tracing/public/cpp/perfetto/system_producer.h @@ -0,0 +1,23 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef SERVICES_TRACING_PUBLIC_CPP_PERFETTO_SYSTEM_PRODUCER_H_ +#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_SYSTEM_PRODUCER_H_ + +#include "services/tracing/public/cpp/perfetto/perfetto_producer.h" +#include "third_party/perfetto/include/perfetto/tracing/core/producer.h" + +namespace tracing { + +class SystemProducer : public PerfettoProducer, public perfetto::Producer { + public: + SystemProducer(PerfettoTaskRunner* task_runner); + // Since Chrome does not support concurrent tracing sessions, and system + // tracing is always lower priority than human or DevTools initiated tracing, + // all system producers must be able to disconnect and stop tracing. + virtual void Disconnect() = 0; +}; +} // namespace tracing + +#endif // SERVICES_TRACING_PUBLIC_CPP_PERFETTO_SYSTEM_PRODUCER_H_ diff --git a/chromium/services/tracing/public/cpp/perfetto/task_runner.cc b/chromium/services/tracing/public/cpp/perfetto/task_runner.cc index 7c7838f34f4..1eecec0c9bc 100644 --- a/chromium/services/tracing/public/cpp/perfetto/task_runner.cc +++ b/chromium/services/tracing/public/cpp/perfetto/task_runner.cc @@ -8,8 +8,15 @@ #include <utility> #include "base/bind.h" +#include "base/no_destructor.h" +#include "base/task/common/checked_lock_impl.h" +#include "base/task/common/scoped_defer_task_posting.h" +#include "base/task/post_task.h" +#include "base/task/thread_pool/thread_pool.h" #include "base/threading/sequenced_task_runner_handle.h" +#include "base/threading/thread_local.h" #include "base/threading/thread_local_storage.h" +#include "services/tracing/public/cpp/perfetto/trace_event_data_source.h" namespace tracing { @@ -20,29 +27,25 @@ PerfettoTaskRunner::PerfettoTaskRunner( PerfettoTaskRunner::~PerfettoTaskRunner() = default; void PerfettoTaskRunner::PostTask(std::function<void()> task) { - // If we're blocked from PostTasking, we defer the task until - // later. If we're not blocked, but there's tasks that have previously been - // deferred, we PostTask them now; this is important to preserve ordering, - // in case the previously deferred tasks have been posted from the same - // sequence as we're now posting a new task from. - { - base::AutoLock lock(lock_); - if (posttask_is_blocked_for_thread_.Get()) { - deferred_tasks_.emplace_back(std::move(task)); - return; - } - - while (!deferred_tasks_.empty()) { - task_runner_->PostTask( - FROM_HERE, base::BindOnce([](std::function<void()> task) { task(); }, - deferred_tasks_.front())); - deferred_tasks_.pop_front(); - } - } - - task_runner_->PostTask( - FROM_HERE, - base::BindOnce([](std::function<void()> task) { task(); }, task)); + base::ScopedDeferTaskPosting::PostOrDefer( + GetOrCreateTaskRunner(), FROM_HERE, + base::BindOnce( + [](std::function<void()> task) { + // We block any trace events that happens while any + // Perfetto task is running, or we'll get deadlocks in + // situations where the StartupTraceWriterRegistry tries + // to bind a writer which in turn causes a PostTask where + // a trace event can be emitted, which then deadlocks as + // it needs a new chunk from the same StartupTraceWriter + // which we're trying to bind and are keeping the lock + // to. + // TODO(oysteine): Try to see if we can be more selective + // about this. + AutoThreadLocalBoolean thread_is_in_trace_event( + TraceEventDataSource::GetThreadIsInTraceEventTLS()); + task(); + }, + task)); } void PerfettoTaskRunner::PostDelayedTask(std::function<void()> task, @@ -55,14 +58,15 @@ void PerfettoTaskRunner::PostDelayedTask(std::function<void()> task, // There's currently nothing which uses PostDelayedTask on the ProducerClient // side, where PostTask sometimes requires blocking. If this DCHECK ever // triggers, support for deferring delayed tasks need to be added. - DCHECK(!posttask_is_blocked_for_thread_.Get()); - task_runner_->PostDelayedTask( + DCHECK(!base::ScopedDeferTaskPosting::IsPresent()); + GetOrCreateTaskRunner()->PostDelayedTask( FROM_HERE, base::BindOnce([](std::function<void()> task) { task(); }, task), base::TimeDelta::FromMilliseconds(delay_ms)); } bool PerfettoTaskRunner::RunsTasksOnCurrentThread() const { + DCHECK(task_runner_); return task_runner_->RunsTasksInCurrentSequence(); } @@ -79,44 +83,21 @@ void PerfettoTaskRunner::ResetTaskRunnerForTesting( task_runner_ = std::move(task_runner); } -void PerfettoTaskRunner::BlockPostTaskForThread() { - DCHECK(!posttask_is_blocked_for_thread_.Get()); - posttask_is_blocked_for_thread_.Set(true); -} - -void PerfettoTaskRunner::UnblockPostTaskForThread() { - DCHECK(posttask_is_blocked_for_thread_.Get()); - posttask_is_blocked_for_thread_.Set(false); -} - -void PerfettoTaskRunner::StartDeferredTasksDrainTimer() { - DCHECK(!posttask_is_blocked_for_thread_.Get()); - // The deferred tasks will generally be posted by another task being - // posted when PostTask isn't blocked; this timer is a fallback for the - // rare case where we're *only* getting trace events when PostTask is - // blocked, and hence doesn't need to run very often (just often enough so - // the SMB doesn't get filled up with uncommitted chunks). - deferred_tasks_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(1), this, - &PerfettoTaskRunner::OnDeferredTasksDrainTimer); -} - -void PerfettoTaskRunner::StopDeferredTasksDrainTimer() { - DCHECK(!posttask_is_blocked_for_thread_.Get()); - - deferred_tasks_timer_.Stop(); - OnDeferredTasksDrainTimer(); +void PerfettoTaskRunner::SetTaskRunner( + scoped_refptr<base::SequencedTaskRunner> task_runner) { + DCHECK(!task_runner_); + task_runner_ = std::move(task_runner); } -void PerfettoTaskRunner::OnDeferredTasksDrainTimer() { - DCHECK(!posttask_is_blocked_for_thread_.Get()); - - base::AutoLock lock(lock_); - while (!deferred_tasks_.empty()) { - task_runner_->PostTask( - FROM_HERE, base::BindOnce([](std::function<void()> task) { task(); }, - deferred_tasks_.front())); - deferred_tasks_.pop_front(); +scoped_refptr<base::SequencedTaskRunner> +PerfettoTaskRunner::GetOrCreateTaskRunner() { + if (!task_runner_) { + DCHECK(base::ThreadPoolInstance::Get()); + task_runner_ = base::CreateSequencedTaskRunnerWithTraits( + {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); } + + return task_runner_; } } // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/task_runner.h b/chromium/services/tracing/public/cpp/perfetto/task_runner.h index d8193e461d0..1221bc68670 100644 --- a/chromium/services/tracing/public/cpp/perfetto/task_runner.h +++ b/chromium/services/tracing/public/cpp/perfetto/task_runner.h @@ -11,7 +11,6 @@ #include "base/macros.h" #include "base/sequenced_task_runner.h" #include "base/synchronization/lock.h" -#include "base/threading/thread_local.h" #include "base/timer/timer.h" #include "services/tracing/public/mojom/perfetto_service.mojom.h" #include "third_party/perfetto/include/perfetto/base/task_runner.h" @@ -37,36 +36,24 @@ class COMPONENT_EXPORT(TRACING_CPP) PerfettoTaskRunner // use case. bool RunsTasksOnCurrentThread() const override; + void SetTaskRunner(scoped_refptr<base::SequencedTaskRunner> task_runner); + scoped_refptr<base::SequencedTaskRunner> GetOrCreateTaskRunner(); + bool HasTaskRunner() const { return !!task_runner_; } + // Not used in Chrome. void AddFileDescriptorWatch(int fd, std::function<void()>) override; void RemoveFileDescriptorWatch(int fd) override; - base::SequencedTaskRunner* task_runner() { return task_runner_.get(); } // Tests will shut down all task runners in between runs, so we need // to re-create any static instances on each SetUp(); void ResetTaskRunnerForTesting( scoped_refptr<base::SequencedTaskRunner> task_runner); - // Sometimes we have to temporarily defer any posted tasks, like - // when trace events are added when the taskqueue is locked. For this purpose - // we keep a timer running when tracing is enabled, which will periodically - // drain these posted tasks. - void StartDeferredTasksDrainTimer(); - void StopDeferredTasksDrainTimer(); - - void BlockPostTaskForThread(); - void UnblockPostTaskForThread(); - private: void OnDeferredTasksDrainTimer(); scoped_refptr<base::SequencedTaskRunner> task_runner_; - base::ThreadLocalBoolean posttask_is_blocked_for_thread_; - - base::Lock lock_; // Protects deferred_tasks_; - std::list<std::function<void()>> deferred_tasks_; - base::RepeatingTimer deferred_tasks_timer_; DISALLOW_COPY_AND_ASSIGN(PerfettoTaskRunner); }; diff --git a/chromium/services/tracing/public/cpp/perfetto/task_runner_unittest.cc b/chromium/services/tracing/public/cpp/perfetto/task_runner_unittest.cc index 9739ca7fb2d..d0af6fea754 100644 --- a/chromium/services/tracing/public/cpp/perfetto/task_runner_unittest.cc +++ b/chromium/services/tracing/public/cpp/perfetto/task_runner_unittest.cc @@ -72,8 +72,6 @@ class PosterThread : public base::SimpleThread { void BeforeJoin() override {} void Run() override { - task_runner_->BlockPostTaskForThread(); - for (int i = 0; i < n_; ++i) { auto weak_ptr = weak_ptr_; auto sequence_number = sequence_number_; @@ -81,8 +79,6 @@ class PosterThread : public base::SimpleThread { weak_ptr->TestTask(i, sequence_number); }); } - - task_runner_->UnblockPostTaskForThread(); } private: @@ -132,70 +128,6 @@ TEST_F(PerfettoTaskRunnerTest, SequentialTasks) { wait_for_tasks.Run(); } -TEST_F(PerfettoTaskRunnerTest, SequentialDeferredTasks) { - base::RunLoop wait_for_tasks; - SetTaskExpectations(wait_for_tasks.QuitClosure(), 3); - - task_runner()->BlockPostTaskForThread(); - auto weak_ptr = destination()->GetWeakPtr(); - task_runner()->PostTask([weak_ptr]() { weak_ptr->TestTask(1); }); - task_runner()->PostTask([weak_ptr]() { weak_ptr->TestTask(2); }); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(0u, destination()->tasks_run()); - task_runner()->UnblockPostTaskForThread(); - // Posting an unblocked task should post the earlier deferred ones, - // in the right order. - task_runner()->PostTask([weak_ptr]() { weak_ptr->TestTask(3); }); - - wait_for_tasks.Run(); -} - -TEST_F(PerfettoTaskRunnerTest, SequentialDeferredTasksByTimer) { - base::RunLoop wait_for_tasks; - SetTaskExpectations(wait_for_tasks.QuitClosure(), 3); - - task_runner()->BlockPostTaskForThread(); - auto weak_ptr = destination()->GetWeakPtr(); - task_runner()->PostTask([weak_ptr]() { weak_ptr->TestTask(1); }); - task_runner()->PostTask([weak_ptr]() { weak_ptr->TestTask(2); }); - task_runner()->PostTask([weak_ptr]() { weak_ptr->TestTask(3); }); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(0u, destination()->tasks_run()); - - // Start the timer which eventually will tick and post the previously - // deferred tasks. Note that this is posted directly to the taskqueue - // rather than the Perfetto wrapper, so it won't be deferred. - task_runner()->task_runner()->PostTask( - FROM_HERE, - base::BindOnce(&PerfettoTaskRunner::StartDeferredTasksDrainTimer, - base::Unretained(task_runner()))); - - wait_for_tasks.Run(); -} - -TEST_F(PerfettoTaskRunnerTest, SequentialByMultipleSequences) { - base::RunLoop wait_for_tasks; - SetTaskExpectations(wait_for_tasks.QuitClosure(), 2001, 3); - - auto weak_ptr = destination()->GetWeakPtr(); - - PosterThread first_thread(task_runner(), weak_ptr, 1000, 1); - PosterThread second_thread(task_runner(), weak_ptr, 1000, 2); - first_thread.Start(); - second_thread.Start(); - first_thread.Join(); - second_thread.Join(); - - // Both threads set the taskrunner to defer new tasks, so none - // should have run at this point. - EXPECT_EQ(0u, destination()->tasks_run()); - - // Posting an unblocked task should post the earlier deferred ones, - // in the right order. - task_runner()->PostTask([weak_ptr]() { weak_ptr->TestTask(1, 0); }); - wait_for_tasks.Run(); -} - } // namespace } // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.cc index e388ed6d321..2a8f262dfdf 100644 --- a/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.cc +++ b/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.cc @@ -83,6 +83,9 @@ TraceEventMetadataSource::GenerateTraceConfigMetadataDict() { void TraceEventMetadataSource::GenerateMetadata( std::unique_ptr<perfetto::TraceWriter> trace_writer) { + if (privacy_filtering_enabled_) { + return; + } DCHECK(origin_task_runner_->RunsTasksInCurrentSequence()); auto trace_packet = trace_writer->NewTracePacket(); @@ -114,7 +117,7 @@ void TraceEventMetadataSource::GenerateMetadata( } void TraceEventMetadataSource::StartTracing( - ProducerClient* producer_client, + PerfettoProducer* producer, const perfetto::DataSourceConfig& data_source_config) { // TODO(eseckler): Once we support streaming of trace data, it would make // sense to emit the metadata on startup, so the UI can display it right away. @@ -122,7 +125,7 @@ void TraceEventMetadataSource::StartTracing( data_source_config.chrome_config().privacy_filtering_enabled(); chrome_config_ = data_source_config.chrome_config().trace_config(); trace_writer_ = - producer_client->CreateTraceWriter(data_source_config.target_buffer()); + producer->CreateTraceWriter(data_source_config.target_buffer()); } void TraceEventMetadataSource::StopTracing( @@ -150,31 +153,11 @@ void TraceEventMetadataSource::Flush( namespace { -class AutoThreadLocalBoolean { - public: - explicit AutoThreadLocalBoolean( - base::ThreadLocalBoolean* thread_local_boolean) - : thread_local_boolean_(thread_local_boolean) { - DCHECK(!thread_local_boolean_->Get()); - thread_local_boolean_->Set(true); - } - ~AutoThreadLocalBoolean() { thread_local_boolean_->Set(false); } - - private: - base::ThreadLocalBoolean* thread_local_boolean_; - DISALLOW_COPY_AND_ASSIGN(AutoThreadLocalBoolean); -}; - -base::ThreadLocalBoolean* GetThreadIsInTraceEventTLS() { - static base::NoDestructor<base::ThreadLocalBoolean> thread_is_in_trace_event; - return thread_is_in_trace_event.get(); -} - base::ThreadLocalStorage::Slot* ThreadLocalEventSinkSlot() { static base::NoDestructor<base::ThreadLocalStorage::Slot> thread_local_event_sink_tls([](void* event_sink) { AutoThreadLocalBoolean thread_is_in_trace_event( - GetThreadIsInTraceEventTLS()); + TraceEventDataSource::GetThreadIsInTraceEventTLS()); delete static_cast<ThreadLocalEventSink*>(event_sink); }); @@ -192,6 +175,12 @@ TraceEventDataSource* TraceEventDataSource::GetInstance() { } // static +base::ThreadLocalBoolean* TraceEventDataSource::GetThreadIsInTraceEventTLS() { + static base::NoDestructor<base::ThreadLocalBoolean> thread_is_in_trace_event; + return thread_is_in_trace_event.get(); +} + +// static void TraceEventDataSource::ResetForTesting() { if (!g_trace_event_data_source_for_testing) return; @@ -221,15 +210,17 @@ void TraceEventDataSource::UnregisterFromTraceLog() { TraceLog::GetInstance()->SetAddTraceEventOverrides(nullptr, nullptr, nullptr); } -void TraceEventDataSource::SetupStartupTracing() { +void TraceEventDataSource::SetupStartupTracing(bool privacy_filtering_enabled) { { base::AutoLock lock(lock_); // No need to do anything if startup tracing has already been set, // or we know Perfetto has already been setup. if (startup_writer_registry_ || producer_client_) { + DCHECK(!privacy_filtering_enabled || privacy_filtering_enabled_); return; } + privacy_filtering_enabled_ = privacy_filtering_enabled; startup_writer_registry_ = std::make_unique<perfetto::StartupTraceWriterRegistry>(); } @@ -237,34 +228,36 @@ void TraceEventDataSource::SetupStartupTracing() { } void TraceEventDataSource::StartTracing( - ProducerClient* producer_client, + PerfettoProducer* producer, const perfetto::DataSourceConfig& data_source_config) { - privacy_filtering_enabled_ = - data_source_config.chrome_config().privacy_filtering_enabled(); - std::unique_ptr<perfetto::StartupTraceWriterRegistry> unbound_writer_registry; { base::AutoLock lock(lock_); + bool should_enable_filtering = + data_source_config.chrome_config().privacy_filtering_enabled(); + if (should_enable_filtering) { + CHECK(!startup_writer_registry_ || privacy_filtering_enabled_) + << "Unexpected StartTracing received when startup tracing is " + "running."; + } + privacy_filtering_enabled_ = should_enable_filtering; + DCHECK(!producer_client_); - producer_client_ = producer_client; + producer_client_ = producer; target_buffer_ = data_source_config.target_buffer(); // Reduce lock contention by binding the registry without holding the lock. unbound_writer_registry = std::move(startup_writer_registry_); - trace_writers_from_registry_.clear(); } session_id_.fetch_add(1u, std::memory_order_relaxed); if (unbound_writer_registry) { - // TODO(ssid): Startup tracing should know about filtering output. - CHECK(!privacy_filtering_enabled_); - // TODO(oysteine): Investigate why trace events emitted by something in // BindStartupTraceWriterRegistry() causes deadlocks. AutoThreadLocalBoolean thread_is_in_trace_event( GetThreadIsInTraceEventTLS()); - producer_client->BindStartupTraceWriterRegistry( + producer->BindStartupTraceWriterRegistry( std::move(unbound_writer_registry), data_source_config.target_buffer()); } else { RegisterWithTraceLog(); @@ -274,7 +267,6 @@ void TraceEventDataSource::StartTracing( TraceConfig(data_source_config.chrome_config().trace_config()); TraceLog::GetInstance()->SetEnabled(trace_config, TraceLog::RECORDING_MODE); ResetHistograms(trace_config); - ProducerClient::GetTaskRunner()->StartDeferredTasksDrainTimer(); } void TraceEventDataSource::StopTracing( @@ -289,12 +281,6 @@ void TraceEventDataSource::StopTracing( return; } - // It's extremely unlikely any threads are still in mid-trace-event - // at this point and end up posting new tasks to the PerfettoTaskRunner - // which end up not getting run until the next tracing session; worst - // case is we lose some chunk commit messages and Perfetto will - // scrape the chunks. - ProducerClient::GetTaskRunner()->StopDeferredTasksDrainTimer(); data_source->UnregisterFromTraceLog(); if (data_source->stop_complete_callback_) { @@ -399,7 +385,6 @@ ThreadLocalEventSink* TraceEventDataSource::CreateThreadLocalEventSink( uint32_t session_id = session_id_.load(std::memory_order_relaxed); if (startup_writer_registry_) { trace_writer = startup_writer_registry_->CreateUnboundTraceWriter(); - trace_writers_from_registry_.insert(trace_writer.get()); } else if (producer_client_) { trace_writer = std::make_unique<perfetto::StartupTraceWriter>( producer_client_->CreateTraceWriter(target_buffer_)); @@ -456,20 +441,13 @@ void TraceEventDataSource::OnAddTraceEvent( // events emitted while the taskqueue is locked), we can't reset the // sink as the TraceWriter deletion is done through PostTask. if (new_session_id > kFirstSessionID && - new_session_id != thread_local_event_sink->session_id() && - !(trace_event->flags() & TRACE_EVENT_FLAG_DISALLOW_POSTTASK)) { + new_session_id != thread_local_event_sink->session_id()) { delete thread_local_event_sink; thread_local_event_sink = nullptr; } } if (!thread_local_event_sink) { - // Trace events emitted by the task queue itself can happen while the task - // queue is locked, posting to it reentrantly would deadlock so these events - // need to be flagged so we can avoid PostTasks while they're being emitted. - ScopedPerfettoPostTaskBlocker post_task_blocker( - !!(trace_event->flags() & TRACE_EVENT_FLAG_DISALLOW_POSTTASK)); - thread_local_event_sink = GetInstance()->CreateThreadLocalEventSink(thread_will_flush); ThreadLocalEventSinkSlot()->Set(thread_local_event_sink); @@ -518,28 +496,42 @@ void TraceEventDataSource::FlushCurrentThread() { void TraceEventDataSource::ReturnTraceWriter( std::unique_ptr<perfetto::StartupTraceWriter> trace_writer) { + // Prevent concurrent binding of the registry. base::AutoLock lock(lock_); - // It's possible that the returned trace writer was created by a former - // StartupTraceWriterRegistry. In this case, we should not attempt to return - // it to the current registry, so we need to verify first that it was indeed - // created by the current registry. - if (startup_writer_registry_ && - trace_writers_from_registry_.find(trace_writer.get()) != - trace_writers_from_registry_.end()) { - // If the writer is still unbound, the registry will keep it alive until it - // was bound and its buffered data was copied. This ensures that we don't - // lose data from threads that are shut down during startup. - trace_writers_from_registry_.erase(trace_writer.get()); - startup_writer_registry_->ReturnUnboundTraceWriter(std::move(trace_writer)); - } else { - // Delete the TraceWriter on the sequence that Perfetto runs on, needed - // as the ThreadLocalEventSink gets deleted on thread - // shutdown and we can't safely call TaskRunnerHandle::Get() at that point - // (which can happen as the TraceWriter destructor might make a Mojo call - // and trigger it). - ProducerClient::GetTaskRunner()->task_runner()->DeleteSoon( - FROM_HERE, std::move(trace_writer)); + + // If we don't have a task runner yet, we must be attempting to return a + // writer before the (very first) registry was bound. We cannot create the + // task runner safely in this case, because the thread pool may not have been + // brought up yet. + if (!PerfettoTracedProcess::GetTaskRunner()->HasTaskRunner()) { + DCHECK(startup_writer_registry_); + // It's safe to call ReturnToRegistry on the current sequence, as it won't + // destroy the writer since the registry was not bound yet. Will keep + // |trace_writer| alive until the registry is bound later. + perfetto::StartupTraceWriter::ReturnToRegistry(std::move(trace_writer)); + return; } + + // Return the TraceWriter on the sequence that Perfetto runs on. Needed as the + // ThreadLocalEventSink gets deleted on thread shutdown and we can't safely + // call TaskRunnerHandle::Get() at that point (which can happen as the + // TraceWriter destructor might make a Mojo call and trigger it). + PerfettoTracedProcess::GetTaskRunner()->GetOrCreateTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce( + // Pass writer as raw pointer so that we leak it if task posting fails + // (during shutdown). + [](perfetto::StartupTraceWriter* raw_trace_writer) { + std::unique_ptr<perfetto::StartupTraceWriter> trace_writer( + raw_trace_writer); + // May destroy |trace_writer|. If the writer is still unbound, the + // registry will keep it alive until it was bound and its buffered + // data was copied. This ensures that we don't lose data from + // threads that are shut down during startup. + perfetto::StartupTraceWriter::ReturnToRegistry( + std::move(trace_writer)); + }, + trace_writer.release())); } } // namespace tracing diff --git a/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.h b/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.h index 4550cb0283c..b350726f91a 100644 --- a/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.h +++ b/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source.h @@ -16,6 +16,7 @@ #include "base/threading/thread_local.h" #include "base/time/time.h" #include "base/trace_event/trace_config.h" +#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h" #include "services/tracing/public/cpp/perfetto/producer_client.h" namespace perfetto { @@ -28,10 +29,25 @@ namespace tracing { class ThreadLocalEventSink; +class AutoThreadLocalBoolean { + public: + explicit AutoThreadLocalBoolean( + base::ThreadLocalBoolean* thread_local_boolean) + : thread_local_boolean_(thread_local_boolean) { + DCHECK(!thread_local_boolean_->Get()); + thread_local_boolean_->Set(true); + } + ~AutoThreadLocalBoolean() { thread_local_boolean_->Set(false); } + + private: + base::ThreadLocalBoolean* thread_local_boolean_; + DISALLOW_COPY_AND_ASSIGN(AutoThreadLocalBoolean); +}; + // This class is a data source that clients can use to provide // global metadata in dictionary form, by registering callbacks. class COMPONENT_EXPORT(TRACING_CPP) TraceEventMetadataSource - : public ProducerClient::DataSourceBase { + : public PerfettoTracedProcess::DataSourceBase { public: TraceEventMetadataSource(); ~TraceEventMetadataSource() override; @@ -41,10 +57,10 @@ class COMPONENT_EXPORT(TRACING_CPP) TraceEventMetadataSource // Any callbacks passed here will be called when tracing starts. void AddGeneratorFunction(MetadataGeneratorFunction generator); - // ProducerClient::DataSourceBase implementation, called by + // PerfettoTracedProcess::DataSourceBase implementation, called by // ProducerClent. void StartTracing( - ProducerClient* producer_client, + PerfettoProducer* producer_client, const perfetto::DataSourceConfig& data_source_config) override; void StopTracing(base::OnceClosure stop_complete_callback) override; void Flush(base::RepeatingClosure flush_complete_callback) override; @@ -63,11 +79,11 @@ class COMPONENT_EXPORT(TRACING_CPP) TraceEventMetadataSource }; // This class acts as a bridge between the TraceLog and -// the Perfetto ProducerClient. It converts incoming +// the PerfettoProducer. It converts incoming // trace events to ChromeTraceEvent protos and writes // them into the Perfetto shared memory. class COMPONENT_EXPORT(TRACING_CPP) TraceEventDataSource - : public ProducerClient::DataSourceBase { + : public PerfettoTracedProcess::DataSourceBase { public: static TraceEventDataSource* GetInstance(); @@ -77,21 +93,23 @@ class COMPONENT_EXPORT(TRACING_CPP) TraceEventDataSource // Flushes and deletes the TraceWriter for the current thread, if any. static void FlushCurrentThread(); + static base::ThreadLocalBoolean* GetThreadIsInTraceEventTLS(); + // Installs TraceLog overrides for tracing during Chrome startup. Trace data // is locally buffered until connection to the perfetto service is // established. Expects a later call to StartTracing() to bind to the perfetto // service. Should only be called once. - void SetupStartupTracing(); + void SetupStartupTracing(bool privacy_filtering_enabled); - // The ProducerClient is responsible for calling StopTracing + // The PerfettoProducer is responsible for calling StopTracing // which will clear the stored pointer to it, before it - // gets destroyed. ProducerClient::CreateTraceWriter can be + // gets destroyed. PerfettoProducer::CreateTraceWriter can be // called by the TraceEventDataSource on any thread. void StartTracing( - ProducerClient* producer_client, + PerfettoProducer* producer_client, const perfetto::DataSourceConfig& data_source_config) override; - // Called from the ProducerClient. + // Called from the PerfettoProducer. void StopTracing(base::OnceClosure stop_complete_callback) override; void Flush(base::RepeatingClosure flush_complete_callback) override; @@ -132,7 +150,6 @@ class COMPONENT_EXPORT(TRACING_CPP) TraceEventDataSource void LogHistogram(base::HistogramBase* histogram); bool disable_interning_ = false; - bool privacy_filtering_enabled_ = false; base::OnceClosure stop_complete_callback_; // Incremented and accessed atomically but without memory order guarantees. @@ -143,17 +160,14 @@ class COMPONENT_EXPORT(TRACING_CPP) TraceEventDataSource base::Lock lock_; // Protects subsequent members. uint32_t target_buffer_ = 0; - ProducerClient* producer_client_ = nullptr; + PerfettoProducer* producer_client_ = nullptr; // We own the registry during startup, but transfer its ownership to the - // ProducerClient once the perfetto service is available. Only set if + // PerfettoProducer once the perfetto service is available. Only set if // SetupStartupTracing() is called. std::unique_ptr<perfetto::StartupTraceWriterRegistry> startup_writer_registry_; - // Unbound writers created by the current |startup_writer_registry_|. We track - // these writers to ensure that we only return the correct ones back to the - // registry. - std::set<perfetto::StartupTraceWriter*> trace_writers_from_registry_; std::vector<std::string> histograms_; + bool privacy_filtering_enabled_ = false; DISALLOW_COPY_AND_ASSIGN(TraceEventDataSource); }; diff --git a/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc b/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc index 4f1bf3c3b5c..02ce6c010d1 100644 --- a/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc +++ b/chromium/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc @@ -37,8 +37,9 @@ constexpr const char kCategoryGroup[] = "foo"; class MockProducerClient : public ProducerClient { public: explicit MockProducerClient( - scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner) - : delegate_(perfetto::base::kPageSize), + std::unique_ptr<PerfettoTaskRunner> main_thread_task_runner) + : ProducerClient(main_thread_task_runner.get()), + delegate_(perfetto::base::kPageSize), stream_(&delegate_), main_thread_task_runner_(std::move(main_thread_task_runner)) { trace_packet_.Reset(&stream_); @@ -109,7 +110,7 @@ class MockProducerClient : public ProducerClient { perfetto::protos::pbzero::TracePacket trace_packet_; protozero::ScatteredStreamWriterNullDelegate delegate_; protozero::ScatteredStreamWriter stream_; - scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner_; + std::unique_ptr<PerfettoTaskRunner> main_thread_task_runner_; }; // For sequences/threads other than our own, we just want to ignore @@ -169,7 +170,8 @@ std::unique_ptr<perfetto::TraceWriter> MockProducerClient::CreateTraceWriter( // but there's no guarantee that this will succeed if that taskrunner is also // shut down. ANNOTATE_SCOPED_MEMORY_LEAK; - if (main_thread_task_runner_->RunsTasksInCurrentSequence()) { + if (main_thread_task_runner_->GetOrCreateTaskRunner() + ->RunsTasksInCurrentSequence()) { return std::make_unique<MockTraceWriter>(this); } else { return std::make_unique<DummyTraceWriter>(); @@ -179,9 +181,12 @@ std::unique_ptr<perfetto::TraceWriter> MockProducerClient::CreateTraceWriter( class TraceEventDataSourceTest : public testing::Test { public: void SetUp() override { - ProducerClient::ResetTaskRunnerForTesting(); - producer_client_ = std::make_unique<MockProducerClient>( + PerfettoTracedProcess::ResetTaskRunnerForTesting(); + PerfettoTracedProcess::GetTaskRunner()->GetOrCreateTaskRunner(); + auto perfetto_wrapper = std::make_unique<PerfettoTaskRunner>( scoped_task_environment_.GetMainThreadTaskRunner()); + producer_client_ = + std::make_unique<MockProducerClient>(std::move(perfetto_wrapper)); } void TearDown() override { diff --git a/chromium/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc b/chromium/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc index 7b97f5ceabe..dd0a5971e29 100644 --- a/chromium/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc +++ b/chromium/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.cc @@ -9,6 +9,7 @@ #include "base/trace_event/trace_buffer.h" #include "base/trace_event/trace_log.h" #include "build/build_config.h" +#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h" #include "services/tracing/public/cpp/perfetto/producer_client.h" #include "services/tracing/public/cpp/perfetto/traced_value_proto_writer.h" #include "third_party/perfetto/include/perfetto/tracing/core/startup_trace_writer.h" @@ -175,9 +176,6 @@ void TrackEventThreadLocalEventSink::AddTraceEvent( bool copy_strings = flags & TRACE_EVENT_FLAG_COPY; bool explicit_timestamp = flags & TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP; - ScopedPerfettoPostTaskBlocker post_task_blocker( - !!(flags & TRACE_EVENT_FLAG_DISALLOW_POSTTASK)); - if (reset_incremental_state_) { interned_event_categories_.ResetEmittedState(); interned_event_names_.ResetEmittedState(); diff --git a/chromium/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h b/chromium/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h index 54d4f042064..80d122738ee 100644 --- a/chromium/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h +++ b/chromium/services/tracing/public/cpp/perfetto/track_event_thread_local_event_sink.h @@ -64,7 +64,7 @@ class COMPONENT_EXPORT(TRACING_CPP) TrackEventThreadLocalEventSink base::trace_event::TraceEvent complete_event_stack_[kMaxCompleteEventDepth]; uint32_t current_stack_depth_ = 0; - bool privacy_filtering_enabled_; + const bool privacy_filtering_enabled_; }; } // namespace tracing diff --git a/chromium/services/tracing/public/cpp/trace_event_agent.cc b/chromium/services/tracing/public/cpp/trace_event_agent.cc index 510cb9d3a8d..684355de1f9 100644 --- a/chromium/services/tracing/public/cpp/trace_event_agent.cc +++ b/chromium/services/tracing/public/cpp/trace_event_agent.cc @@ -18,7 +18,7 @@ #include "base/trace_event/trace_log.h" #include "base/values.h" #include "build/build_config.h" -#include "services/tracing/public/cpp/perfetto/producer_client.h" +#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h" #include "services/tracing/public/cpp/perfetto/trace_event_data_source.h" #include "services/tracing/public/cpp/trace_event_args_whitelist.h" #include "services/tracing/public/cpp/tracing_features.h" @@ -56,7 +56,8 @@ TraceEventAgent::TraceEventAgent() base::BindRepeating(&IsMetadataWhitelisted)); } - ProducerClient::Get()->AddDataSource(TraceEventDataSource::GetInstance()); + PerfettoTracedProcess::Get()->AddDataSource( + TraceEventDataSource::GetInstance()); } TraceEventAgent::~TraceEventAgent() = default; @@ -77,7 +78,7 @@ void TraceEventAgent::AddMetadataGeneratorFunction( // call. static TraceEventMetadataSource* metadata_source = []() { static base::NoDestructor<TraceEventMetadataSource> instance; - ProducerClient::Get()->AddDataSource(instance.get()); + PerfettoTracedProcess::Get()->AddDataSource(instance.get()); return instance.get(); }(); diff --git a/chromium/services/tracing/public/cpp/trace_event_agent_unittest.cc b/chromium/services/tracing/public/cpp/trace_event_agent_unittest.cc index 8578b723266..27ddafefc4c 100644 --- a/chromium/services/tracing/public/cpp/trace_event_agent_unittest.cc +++ b/chromium/services/tracing/public/cpp/trace_event_agent_unittest.cc @@ -83,7 +83,7 @@ class MockRecorder : public mojom::Recorder { class TraceEventAgentTest : public testing::Test { public: - void SetUp() override { ProducerClient::ResetTaskRunnerForTesting(); } + void SetUp() override { PerfettoTracedProcess::ResetTaskRunnerForTesting(); } void TearDown() override { base::trace_event::TraceLog::GetInstance()->SetDisabled(); diff --git a/chromium/services/tracing/public/cpp/trace_event_args_whitelist.cc b/chromium/services/tracing/public/cpp/trace_event_args_whitelist.cc index 2e622d8222e..3d1de85a037 100644 --- a/chromium/services/tracing/public/cpp/trace_event_args_whitelist.cc +++ b/chromium/services/tracing/public/cpp/trace_event_args_whitelist.cc @@ -29,12 +29,14 @@ const char* const kScopedBlockingCallAllowedArgs[] = {"file_name", const char* const kGetFallbackFontsAllowedArgs[] = {"script", nullptr}; const char* const kGPUAllowedArgs[] = {nullptr}; const char* const kInputLatencyAllowedArgs[] = {"data", nullptr}; -const char* const kMemoryDumpAllowedArgs[] = {"dumps", nullptr}; +const char* const kMemoryDumpAllowedArgs[] = {"dumps", "top_queued_message_tag", + "count", nullptr}; const char* const kRendererHostAllowedArgs[] = { "class", "line", "should_background", "has_pending_views", "bytes_allocated", nullptr}; const char* const kV8GCAllowedArgs[] = {"num_items", "num_tasks", nullptr}; const char* const kTopLevelFlowAllowedArgs[] = {"task_queue_name", nullptr}; +const char* const kTopLevelIpcRunTaskAllowedArgs[] = {"ipc_hash", nullptr}; const WhitelistEntry kEventArgsWhitelist[] = { {"__metadata", "thread_name", nullptr}, @@ -56,6 +58,7 @@ const WhitelistEntry kEventArgsWhitelist[] = { {"startup", "PrefProvider::PrefProvider", nullptr}, {"task_scheduler", "*", nullptr}, {"toplevel", "*", nullptr}, + {"toplevel.ipc", "TaskAnnotator::RunTask", kTopLevelIpcRunTaskAllowedArgs}, {TRACE_DISABLED_BY_DEFAULT("cpu_profiler"), "*", nullptr}, // Redefined the string since MemoryDumpManager::kTraceCategory causes // static initialization of this struct. diff --git a/chromium/services/tracing/public/cpp/trace_startup.cc b/chromium/services/tracing/public/cpp/trace_startup.cc index 13a382f4f2e..f11df229dca 100644 --- a/chromium/services/tracing/public/cpp/trace_startup.cc +++ b/chromium/services/tracing/public/cpp/trace_startup.cc @@ -15,6 +15,11 @@ namespace tracing { +namespace { +using base::trace_event::TraceConfig; +using base::trace_event::TraceLog; +} // namespace + void EnableStartupTracingIfNeeded() { const base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); @@ -27,29 +32,31 @@ void EnableStartupTracingIfNeeded() { // Ensure TraceLog is initialized first. // https://crbug.com/764357 - base::trace_event::TraceLog::GetInstance(); + auto* trace_log = TraceLog::GetInstance(); + auto* startup_config = TraceStartupConfig::GetInstance(); + + if (startup_config->IsEnabled()) { + if (TracingUsesPerfettoBackend()) { + TraceEventDataSource::GetInstance()->SetupStartupTracing( + startup_config->GetBackgroundStartupTracingEnabled()); + } - if (TraceStartupConfig::GetInstance()->IsEnabled()) { - const base::trace_event::TraceConfig& trace_config = - TraceStartupConfig::GetInstance()->GetTraceConfig(); - uint8_t modes = base::trace_event::TraceLog::RECORDING_MODE; + const TraceConfig& trace_config = startup_config->GetTraceConfig(); + uint8_t modes = TraceLog::RECORDING_MODE; if (!trace_config.event_filters().empty()) - modes |= base::trace_event::TraceLog::FILTERING_MODE; - if (TracingUsesPerfettoBackend()) - TraceEventDataSource::GetInstance()->SetupStartupTracing(); - base::trace_event::TraceLog::GetInstance()->SetEnabled( - TraceStartupConfig::GetInstance()->GetTraceConfig(), modes); + modes |= TraceLog::FILTERING_MODE; + trace_log->SetEnabled(startup_config->GetTraceConfig(), modes); } else if (command_line.HasSwitch(switches::kTraceToConsole)) { // TODO(eseckler): Remove ability to trace to the console, perfetto doesn't // support this and noone seems to use it. - base::trace_event::TraceConfig trace_config = GetConfigForTraceToConsole(); + TraceConfig trace_config = GetConfigForTraceToConsole(); LOG(ERROR) << "Start " << switches::kTraceToConsole << " with CategoryFilter '" << trace_config.ToCategoryFilterString() << "'."; if (TracingUsesPerfettoBackend()) - TraceEventDataSource::GetInstance()->SetupStartupTracing(); - base::trace_event::TraceLog::GetInstance()->SetEnabled( - trace_config, base::trace_event::TraceLog::RECORDING_MODE); + TraceEventDataSource::GetInstance()->SetupStartupTracing( + /*privacy_filtering_enabled=*/false); + trace_log->SetEnabled(trace_config, TraceLog::RECORDING_MODE); } } diff --git a/chromium/services/tracing/public/cpp/traced_process_impl.cc b/chromium/services/tracing/public/cpp/traced_process_impl.cc index 1824f7c5adb..ba11a2cdd24 100644 --- a/chromium/services/tracing/public/cpp/traced_process_impl.cc +++ b/chromium/services/tracing/public/cpp/traced_process_impl.cc @@ -45,6 +45,7 @@ void TracedProcessImpl::OnTracedProcessRequest( return; } + DETACH_FROM_SEQUENCE(sequence_checker_); binding_.Bind(std::move(request)); } @@ -73,12 +74,17 @@ void TracedProcessImpl::UnregisterAgent(BaseAgent* agent) { } void TracedProcessImpl::ConnectToTracingService( - mojom::ConnectToTracingRequestPtr request) { + mojom::ConnectToTracingRequestPtr request, + ConnectToTracingServiceCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // Acknowledge this message so the tracing service knows it was dispatched in + // this process. + std::move(callback).Run(); + // Tracing requires a running ThreadPool; disable tracing // for processes without it. - if (!base::ThreadPool::GetInstance()) { + if (!base::ThreadPoolInstance::Get()) { return; } @@ -102,7 +108,7 @@ void TracedProcessImpl::ConnectToTracingService( agent->Connect(agent_registry_.get()); } - ProducerClient::Get()->Connect( + PerfettoTracedProcess::Get()->producer_client()->Connect( tracing::mojom::PerfettoServicePtr(std::move(request->perfetto_service))); } diff --git a/chromium/services/tracing/public/cpp/traced_process_impl.h b/chromium/services/tracing/public/cpp/traced_process_impl.h index efd5f313710..a67e2ca61cd 100644 --- a/chromium/services/tracing/public/cpp/traced_process_impl.h +++ b/chromium/services/tracing/public/cpp/traced_process_impl.h @@ -46,7 +46,8 @@ class COMPONENT_EXPORT(TRACING_CPP) TracedProcessImpl // tracing::mojom::TracedProcess: void ConnectToTracingService( - mojom::ConnectToTracingRequestPtr request) override; + mojom::ConnectToTracingRequestPtr request, + ConnectToTracingServiceCallback callback) override; // Lock protecting binding_. base::Lock lock_; diff --git a/chromium/services/tracing/public/cpp/tracing_features.cc b/chromium/services/tracing/public/cpp/tracing_features.cc index ee6a39798d8..9a20fc7860c 100644 --- a/chromium/services/tracing/public/cpp/tracing_features.cc +++ b/chromium/services/tracing/public/cpp/tracing_features.cc @@ -19,10 +19,19 @@ namespace features { const base::Feature kTracingPerfettoBackend{"TracingPerfettoBackend", base::FEATURE_ENABLED_BY_DEFAULT}; +// Causes the BackgroundTracingManager to upload proto messages via UMA, +// rather than JSON via the crash frontend. +const base::Feature kBackgroundTracingProtoOutput{ + "BackgroundTracingProtoOutput", base::FEATURE_DISABLED_BY_DEFAULT}; + +// Causes Perfetto to run in-process mode for in-process tracing producers. +const base::Feature kPerfettoForceOutOfProcessProducer{ + "PerfettoForceOutOfProcessProducer", base::FEATURE_ENABLED_BY_DEFAULT}; + // Runs the tracing service as an in-process browser service. const base::Feature kTracingServiceInProcess { "TracingServiceInProcess", -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(IS_CHROMECAST) base::FEATURE_ENABLED_BY_DEFAULT #else base::FEATURE_DISABLED_BY_DEFAULT diff --git a/chromium/services/tracing/public/cpp/tracing_features.h b/chromium/services/tracing/public/cpp/tracing_features.h index fca060e4ab8..ef0bbe85b3c 100644 --- a/chromium/services/tracing/public/cpp/tracing_features.h +++ b/chromium/services/tracing/public/cpp/tracing_features.h @@ -21,6 +21,12 @@ extern const COMPONENT_EXPORT(TRACING_CPP) base::Feature extern const COMPONENT_EXPORT(TRACING_CPP) base::Feature kTracingServiceInProcess; +extern const COMPONENT_EXPORT(TRACING_CPP) base::Feature + kBackgroundTracingProtoOutput; + +extern const COMPONENT_EXPORT(TRACING_CPP) base::Feature + kPerfettoForceOutOfProcessProducer; + } // namespace features namespace tracing { diff --git a/chromium/services/tracing/public/mojom/perfetto_service.mojom b/chromium/services/tracing/public/mojom/perfetto_service.mojom index 97b707f7a92..799b2c4997c 100644 --- a/chromium/services/tracing/public/mojom/perfetto_service.mojom +++ b/chromium/services/tracing/public/mojom/perfetto_service.mojom @@ -149,42 +149,77 @@ struct DataSource { array<string> producer_name_filter; }; +struct PerfettoBuiltinDataSource { + bool disable_clock_snapshotting; + bool disable_trace_config; + bool disable_system_info; +}; + // The configuration provided by a Consumer to the Perfetto service which // primarily configures which named data sources it would like to enable and // receive tracing data from, and how large the destination buffers should be. struct TraceConfig { array<DataSource> data_sources; + PerfettoBuiltinDataSource perfetto_builtin_data_source; array<BufferConfig> buffers; uint32 duration_ms; }; +// The priority of the incoming EnableTracing request, used to determine +// whether the session should take precedence over another existing session, +// and/or whether a later session should abort the current one. Ordered in +// ascending priority. +enum TracingClientPriority { + kUnknown, + kBackground, + kUserInitiated +}; + // The ConsumerHost interface is a privileged interface which is implemented // within the tracing service, and connected to by privileged services // (i.e. content_browser) to receive tracing data. interface ConsumerHost { - // Enable Perfetto tracing with the given TracingSession interface for + // Enable Perfetto tracing with the given TracingSessionClient interface for // signaling lifespan of the tracing session, and any future callbacks. - EnableTracing(TracingSession tracing_session, TraceConfig config); + // The given TracingSessionHost is used to control the tracing session and + // closing it will disable tracing and free the session's trace buffers. + // Note: Right now only a single concurrent tracing session is supported, + // as there's no support for multiplexing enabled trace events to multiple + // consumers. If a new tracing session is attempted while there's an existing + // one in progress, the relative priorities will be used to figure out which + // one to be able to (keep) tracing; if the priorities are the same, the new + // session will take precedence. + EnableTracing(TracingSessionHost& tracing_session_host, + TracingSessionClient tracing_session_client, + TraceConfig config, + TracingClientPriority priority); +}; +// Represents the host side of an active tracing session. Closing this +// will disable tracing. +interface TracingSessionHost { // Update the trace config for the active tracing session. Currently, only // (additive) updates to the |producer_name_filter| of a data source are // supported. ChangeTraceConfig(TraceConfig config); - // Stop tracing for the active tracing session. The host will disconnect the - // TracingSession once tracing was disabled. Note that tracing may also stop - // without an explicit call to DisableTracing(), e.g. when a tracing duration - // is specified in the TraceConfig. + // Stop tracing for the active tracing session. Note that tracing may also + // stop without an explicit call to DisableTracing(), e.g. when a tracing + // duration is specified in the TraceConfig. DisableTracing(); // Tell Perfetto we're ready to receive data, over the given data pipe. // The result callback will be called when there's no more data currently - // available. If the TracingSession is still active after the callback, + // available. If the TracingSessionClient is still active after the callback, // another call to ReadBuffers() needs to be made to receive any new // tracing data. ReadBuffers(handle<data_pipe_producer> stream) => (); + // Request current trace buffer usage of the active session. Will be returned + // as percentage value between 0.0f and 1.0f. + RequestBufferUsage() => (bool success, float percent_full); + // Disables tracing and converts the collected trace data converted into the // legacy JSON format before returning it via the data pipe. If // |agent_label_filter| is not empty, only data pertaining to the specified @@ -194,20 +229,17 @@ interface ConsumerHost { // streaming proto format via ReadBuffers. DisableTracingAndEmitJson(string agent_label_filter, handle<data_pipe_producer> stream) => (); - - // Request current trace buffer usage of the active session. Will be returned - // as percentage value between 0.0f and 1.0f. - RequestBufferUsage() => (bool success, float percent_full); }; -// Any client connecting to ConsumerHost should implement this -// interface which represents the lifetime of an active tracing -// session. The ConsumerHost will disconnect it when tracing -// is stopped, at which point the client can know that one -// more ReadBuffers() call will receive any remaining tracing -// data from the session (in addition to any calls it may have -// made while the session is active, to stream out data). -interface TracingSession { +// Any client connecting to ConsumerHost should implement this interface which +// represents the lifetime of an active tracing session, i.e. from the point +// where tracing is enabled, to the point where all tracing data has been +// received by the client. +interface TracingSessionClient { // Called when all processes have begun tracing. OnTracingEnabled(); + // Called when tracing is disabled; initiated either by a call to + // TracingSessionHost::DisableTracing or by the service itself if a timeout is + // specified in the tracing config. + OnTracingDisabled(); }; diff --git a/chromium/services/tracing/public/mojom/perfetto_service.typemap b/chromium/services/tracing/public/mojom/perfetto_service.typemap index 5bec6e49054..2671fac9069 100644 --- a/chromium/services/tracing/public/mojom/perfetto_service.typemap +++ b/chromium/services/tracing/public/mojom/perfetto_service.typemap @@ -36,5 +36,6 @@ type_mappings = [ "tracing.mojom.DataSourceConfig=perfetto::DataSourceConfig", "tracing.mojom.ChromeConfig=perfetto::ChromeConfig", "tracing.mojom.DataSourceRegistration=perfetto::DataSourceDescriptor", + "tracing.mojom.PerfettoBuiltinDataSource=perfetto::TraceConfig::BuiltinDataSource", "tracing.mojom.TraceConfig=perfetto::TraceConfig", ] diff --git a/chromium/services/tracing/public/mojom/trace_config_mojom_traits.cc b/chromium/services/tracing/public/mojom/trace_config_mojom_traits.cc index 649fbe92b05..9e78d913d50 100644 --- a/chromium/services/tracing/public/mojom/trace_config_mojom_traits.cc +++ b/chromium/services/tracing/public/mojom/trace_config_mojom_traits.cc @@ -45,11 +45,24 @@ bool StructTraits<tracing::mojom::DataSourceDataView, } // static +bool StructTraits<tracing::mojom::PerfettoBuiltinDataSourceDataView, + perfetto::TraceConfig::BuiltinDataSource>:: + Read(tracing::mojom::PerfettoBuiltinDataSourceDataView data, + perfetto::TraceConfig::BuiltinDataSource* out) { + out->set_disable_clock_snapshotting(data.disable_clock_snapshotting()); + out->set_disable_trace_config(data.disable_trace_config()); + out->set_disable_system_info(data.disable_system_info()); + return true; +} + +// static bool StructTraits<tracing::mojom::TraceConfigDataView, perfetto::TraceConfig>:: Read(tracing::mojom::TraceConfigDataView data, perfetto::TraceConfig* out) { std::vector<perfetto::TraceConfig::DataSource> data_sources; std::vector<perfetto::TraceConfig::BufferConfig> buffers; - if (!data.ReadDataSources(&data_sources) || !data.ReadBuffers(&buffers)) { + if (!data.ReadDataSources(&data_sources) || !data.ReadBuffers(&buffers) || + !data.ReadPerfettoBuiltinDataSource( + out->mutable_builtin_data_sources())) { return false; } diff --git a/chromium/services/tracing/public/mojom/trace_config_mojom_traits.h b/chromium/services/tracing/public/mojom/trace_config_mojom_traits.h index 018d1bd1896..bb274ef4057 100644 --- a/chromium/services/tracing/public/mojom/trace_config_mojom_traits.h +++ b/chromium/services/tracing/public/mojom/trace_config_mojom_traits.h @@ -49,6 +49,30 @@ class StructTraits<tracing::mojom::DataSourceDataView, perfetto::TraceConfig::DataSource* out); }; +// perfetto::TraceConfig::BuiltinDataSource +template <> +class StructTraits<tracing::mojom::PerfettoBuiltinDataSourceDataView, + perfetto::TraceConfig::BuiltinDataSource> { + public: + static bool disable_clock_snapshotting( + const perfetto::TraceConfig::BuiltinDataSource& src) { + return src.disable_clock_snapshotting(); + } + + static bool disable_trace_config( + const perfetto::TraceConfig::BuiltinDataSource& src) { + return src.disable_trace_config(); + } + + static bool disable_system_info( + const perfetto::TraceConfig::BuiltinDataSource& src) { + return src.disable_system_info(); + } + + static bool Read(tracing::mojom::PerfettoBuiltinDataSourceDataView data, + perfetto::TraceConfig::BuiltinDataSource* out); +}; + // perfetto::TraceConfig template <> class StructTraits<tracing::mojom::TraceConfigDataView, perfetto::TraceConfig> { @@ -58,6 +82,11 @@ class StructTraits<tracing::mojom::TraceConfigDataView, perfetto::TraceConfig> { return src.data_sources(); } + static const perfetto::TraceConfig::BuiltinDataSource& + perfetto_builtin_data_source(const perfetto::TraceConfig& src) { + return src.builtin_data_sources(); + } + static const std::vector<perfetto::TraceConfig::BufferConfig>& buffers( const perfetto::TraceConfig& src) { return src.buffers(); diff --git a/chromium/services/tracing/public/mojom/traced_process.mojom b/chromium/services/tracing/public/mojom/traced_process.mojom index b1d8040907e..7b443cee6ac 100644 --- a/chromium/services/tracing/public/mojom/traced_process.mojom +++ b/chromium/services/tracing/public/mojom/traced_process.mojom @@ -20,7 +20,7 @@ struct ConnectToTracingRequest { // and pass it pointers to the interfaces within the tracing service // that the other services should register themselves with. interface TracedProcess { - ConnectToTracingService(ConnectToTracingRequest request); + ConnectToTracingService(ConnectToTracingRequest request) => (); }; diff --git a/chromium/services/tracing/tracing_service.cc b/chromium/services/tracing/tracing_service.cc index 6d04bca0b47..4ab07c9645d 100644 --- a/chromium/services/tracing/tracing_service.cc +++ b/chromium/services/tracing/tracing_service.cc @@ -9,6 +9,7 @@ #include <vector> #include "base/bind.h" +#include "base/stl_util.h" #include "base/timer/timer.h" #include "services/service_manager/public/mojom/service_manager.mojom.h" #include "services/tracing/agent_registry.h" @@ -29,8 +30,7 @@ class ServiceListener : public service_manager::mojom::ServiceManagerListener { ServiceListener(service_manager::Connector* connector, AgentRegistry* agent_registry, Coordinator* coordinator) - : binding_(this), - connector_(connector), + : connector_(connector), agent_registry_(agent_registry), coordinator_(coordinator) { service_manager::mojom::ServiceManagerPtr service_manager; @@ -45,25 +45,31 @@ class ServiceListener : public service_manager::mojom::ServiceManagerListener { size_t CountServicesWithPID(uint32_t pid) { return std::count_if(service_pid_map_.begin(), service_pid_map_.end(), - [pid](decltype(service_pid_map_)::value_type p) { - return p.second == pid; - }); + [pid](const auto& p) { return p.second == pid; }); } void ServiceAddedWithPID(const service_manager::Identity& identity, uint32_t pid) { service_pid_map_[identity] = pid; - // Not the first service added, so we're already sent it a connection - // request. - if (CountServicesWithPID(pid) > 1) { + + // Not the first service added for this PID, and the process has already + // accepted a connection request. + if (base::ContainsKey(connected_pids_, pid)) return; - } // Let the Coordinator and the perfetto service know it should be expecting // a connection from this process. coordinator_->AddExpectedPID(pid); PerfettoService::GetInstance()->AddActiveServicePid(pid); + // NOTE: If multiple service instances are running in the same process, we + // may send multiple ConnectToTracingService calls to the same process in + // the time it takes the first call to be received and acknowledged. This is + // OK, because any given client process will only bind a single + // TracedProcess endpoint as long as this instance of the tracing service + // remains alive. Subsequent TracedProcess endpoints will be dropped and + // their calls will never be processed. + mojom::TracedProcessPtr traced_process; connector_->BindInterface( service_manager::ServiceFilter::ForExactIdentity(identity), @@ -71,14 +77,17 @@ class ServiceListener : public service_manager::mojom::ServiceManagerListener { service_manager::mojom::BindInterfacePriority::kBestEffort); auto new_connection_request = mojom::ConnectToTracingRequest::New(); - - PerfettoService::GetInstance()->BindRequest( - mojo::MakeRequest(&new_connection_request->perfetto_service), pid); - - agent_registry_->BindAgentRegistryRequest( - mojo::MakeRequest(&new_connection_request->agent_registry)); - - traced_process->ConnectToTracingService(std::move(new_connection_request)); + auto service_request = + mojo::MakeRequest(&new_connection_request->perfetto_service); + auto registry_request = + mojo::MakeRequest(&new_connection_request->agent_registry); + mojom::TracedProcess* raw_traced_process = traced_process.get(); + raw_traced_process->ConnectToTracingService( + std::move(new_connection_request), + base::BindOnce(&ServiceListener::OnProcessConnected, + base::Unretained(this), std::move(traced_process), pid, + std::move(service_request), + std::move(registry_request))); } void ServiceRemoved(const service_manager::Identity& identity) { @@ -91,6 +100,7 @@ class ServiceListener : public service_manager::mojom::ServiceManagerListener { if (CountServicesWithPID(pid) == 0) { coordinator_->RemoveExpectedPID(pid); PerfettoService::GetInstance()->RemoveActiveServicePid(pid); + connected_pids_.erase(pid); } } } @@ -130,11 +140,30 @@ class ServiceListener : public service_manager::mojom::ServiceManagerListener { service_manager::mojom::RunningServiceInfoPtr service) override {} private: - mojo::Binding<service_manager::mojom::ServiceManagerListener> binding_; - service_manager::Connector* connector_; - AgentRegistry* agent_registry_; - Coordinator* coordinator_; + void OnProcessConnected(mojom::TracedProcessPtr traced_process, + uint32_t pid, + mojom::PerfettoServiceRequest service_request, + mojom::AgentRegistryRequest registry_request) { + auto result = connected_pids_.insert(pid); + if (!result.second) { + // The PID was already connected. Nothing more to do. + return; + } + + connected_pids_.insert(pid); + PerfettoService::GetInstance()->BindRequest(std::move(service_request), + pid); + agent_registry_->BindAgentRegistryRequest(std::move(registry_request)); + } + + mojo::Binding<service_manager::mojom::ServiceManagerListener> binding_{this}; + service_manager::Connector* const connector_; + AgentRegistry* const agent_registry_; + Coordinator* const coordinator_; std::map<service_manager::Identity, uint32_t> service_pid_map_; + std::set<uint32_t> connected_pids_; + + DISALLOW_COPY_AND_ASSIGN(ServiceListener); }; TracingService::TracingService(service_manager::mojom::ServiceRequest request) |