| // 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/producer_host.h" |
| |
| #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 "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" |
| |
| namespace tracing { |
| |
| ProducerHost::ProducerHost() = default; |
| |
| ProducerHost::~ProducerHost() { |
| // Manually reset to prevent any callbacks from the ProducerEndpoint |
| // when we're in a half-destructed state. |
| producer_endpoint_.reset(); |
| } |
| |
| void ProducerHost::Initialize(mojom::ProducerClientPtr producer_client, |
| perfetto::TracingService* service, |
| const std::string& name) { |
| DCHECK(service); |
| DCHECK(!producer_endpoint_); |
| |
| 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. |
| 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 */, is_in_process_); |
| DCHECK(producer_endpoint_); |
| } |
| |
| void ProducerHost::OnConnect() { |
| } |
| |
| void ProducerHost::OnDisconnect() { |
| // Deliberately empty, this is invoked by the |service_| business logic after |
| // we destroy the |producer_endpoint|. |
| } |
| |
| 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); |
| DCHECK(producer_client_); |
| mojo::ScopedSharedBufferHandle shm = shared_memory->Clone(); |
| DCHECK(shm.is_valid()); |
| |
| producer_client_->OnTracingStart(std::move(shm)); |
| } |
| |
| void ProducerHost::SetupDataSource(perfetto::DataSourceInstanceID, |
| const perfetto::DataSourceConfig&) { |
| // TODO(primiano): plumb call through mojo. |
| } |
| |
| void ProducerHost::StartDataSource(perfetto::DataSourceInstanceID id, |
| const perfetto::DataSourceConfig& config) { |
| // The type traits will send the base fields in the DataSourceConfig and also |
| // the ChromeConfig other configs are dropped. |
| producer_client_->StartDataSource( |
| id, config, |
| base::BindOnce( |
| [](ProducerHost* producer_host, perfetto::DataSourceInstanceID id) { |
| producer_host->producer_endpoint_->NotifyDataSourceStarted(id); |
| }, |
| base::Unretained(this), id)); |
| } |
| |
| void ProducerHost::StopDataSource(perfetto::DataSourceInstanceID id) { |
| if (producer_client_) { |
| producer_client_->StopDataSource( |
| id, |
| base::BindOnce( |
| [](ProducerHost* producer_host, perfetto::DataSourceInstanceID id) { |
| producer_host->producer_endpoint_->NotifyDataSourceStopped(id); |
| }, |
| base::Unretained(this), id)); |
| } |
| } |
| |
| void ProducerHost::Flush( |
| perfetto::FlushRequestID id, |
| const perfetto::DataSourceInstanceID* raw_data_source_ids, |
| size_t num_data_sources) { |
| DCHECK(producer_client_); |
| std::vector<uint64_t> data_source_ids(raw_data_source_ids, |
| raw_data_source_ids + num_data_sources); |
| DCHECK_EQ(data_source_ids.size(), num_data_sources); |
| 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 |
| // inputs. |
| void ProducerHost::CommitData(const perfetto::CommitDataRequest& data_request) { |
| if (on_commit_callback_for_testing_) { |
| on_commit_callback_for_testing_.Run(data_request); |
| } |
| producer_endpoint_->CommitData(data_request); |
| } |
| |
| void ProducerHost::RegisterDataSource( |
| const perfetto::DataSourceDescriptor& registration_info) { |
| producer_endpoint_->RegisterDataSource(registration_info); |
| } |
| |
| void ProducerHost::RegisterTraceWriter(uint32_t writer_id, |
| uint32_t target_buffer) { |
| producer_endpoint_->RegisterTraceWriter(writer_id, target_buffer); |
| } |
| |
| void ProducerHost::UnregisterTraceWriter(uint32_t writer_id) { |
| producer_endpoint_->UnregisterTraceWriter(writer_id); |
| } |
| |
| } // namespace tracing |