blob: 35610843ed1e1b046eab09027a09006d79e316ee [file] [log] [blame]
// 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/public/cpp/perfetto/producer_client.h"
#include <utility>
#include "base/bind.h"
#include "base/no_destructor.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/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"
#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 {
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) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
ProducerClient::~ProducerClient() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
// static
void ProducerClient::DeleteSoonForTesting(
std::unique_ptr<ProducerClient> producer_client) {
GetTaskRunner()->GetOrCreateTaskRunner()->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(nullptr);
return task_runner.get();
}
// static
void ProducerClient::ResetTaskRunnerForTesting() {
DETACH_FROM_SEQUENCE(ProducerClient::Get()->sequence_checker_);
GetTaskRunner()->ResetTaskRunnerForTesting(nullptr);
}
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()->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)));
}
void ProducerClient::BindStartupTraceWriterRegistry(
std::unique_ptr<perfetto::StartupTraceWriterRegistry> registry,
perfetto::BufferID target_buffer) {
shared_memory_arbiter_->BindStartupTraceWriterRegistry(std::move(registry),
target_buffer);
}
// 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,
mojom::ProducerClientRequest producer_client_request,
mojom::ProducerClientPtr producer_client) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!binding_ || !binding_->is_bound());
binding_ = std::make_unique<mojo::Binding<mojom::ProducerClient>>(
this, std::move(producer_client_request));
binding_->set_connection_error_handler(base::BindOnce(
[](ProducerClient* producer_client) {
producer_client->binding_->Close();
},
base::Unretained(this)));
origin_task_runner->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(producer_client),
mojo::MakeRequest(&producer_host_)));
// TODO(oysteine) We register the data sources in reverse as a temporary
// workaround to make sure that the TraceEventDataSource is registered
// *after* the MetadataSource, as the logic which waits for trace clients
// to be "ready" (in the tracing coordinator) waits for the TraceLog to
// 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);
}
}
void ProducerClient::AddDataSource(DataSourceBase* data_source) {
GetTaskRunner()->GetOrCreateTaskRunner()->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::RegisterDataSourceWithHost(DataSourceBase* data_source) {
perfetto::DataSourceDescriptor new_registration;
new_registration.set_name(data_source->name());
new_registration.set_will_notify_on_start(true);
new_registration.set_will_notify_on_stop(true);
producer_host_->RegisterDataSource(std::move(new_registration));
}
void ProducerClient::OnTracingStart(
mojo::ScopedSharedBufferHandle shared_memory) {
// TODO(oysteine): In next CLs plumb this through the service.
const size_t kShmemBufferPageSize = 4096;
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(producer_host_);
if (!shared_memory_) {
shared_memory_ =
std::make_unique<MojoSharedMemory>(std::move(shared_memory));
shared_memory_arbiter_ = perfetto::SharedMemoryArbiter::CreateInstance(
shared_memory_.get(), kShmemBufferPageSize, this, GetTaskRunner());
} else {
// TODO(oysteine): This is assuming the SMB is the same, currently. Swapping
// out SharedMemoryBuffers would require more thread synchronization.
DCHECK_EQ(shared_memory_->shared_buffer()->value(), shared_memory->value());
}
}
void ProducerClient::StartDataSource(
uint64_t id,
const perfetto::DataSourceConfig& data_source_config,
StartDataSourceCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// TODO(oysteine): Support concurrent tracing sessions.
for (auto* data_source : 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|.
std::move(callback).Run();
return;
}
}
}
void ProducerClient::StopDataSource(uint64_t id,
StopDataSourceCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto* data_source : data_sources_) {
if (data_source->data_source_id() == id) {
data_source->StopTracing(std::move(callback));
return;
}
}
LOG(DFATAL) << "Invalid data source ID.";
}
void ProducerClient::Flush(uint64_t flush_request_id,
const std::vector<uint64_t>& data_source_ids) {
pending_replies_for_latest_flush_ = {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_) {
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(
[](base::WeakPtr<ProducerClient> weak_ptr, uint64_t id) {
if (weak_ptr) {
weak_ptr->NotifyFlushComplete(id);
}
},
weak_ptr_factory_.GetWeakPtr(), flush_request_id));
}
}
}
void ProducerClient::RegisterDataSource(const perfetto::DataSourceDescriptor&) {
NOTREACHED();
}
void ProducerClient::UnregisterDataSource(const std::string& name) {
NOTREACHED();
}
void ProducerClient::NotifyDataSourceStopped(
perfetto::DataSourceInstanceID id) {
NOTREACHED();
}
void ProducerClient::NotifyDataSourceStarted(
perfetto::DataSourceInstanceID id) {
NOTREACHED();
}
void ProducerClient::ActivateTriggers(const std::vector<std::string>&) {
NOTREACHED();
}
void ProducerClient::CommitData(const perfetto::CommitDataRequest& commit,
CommitDataCallback callback) {
producer_host_->CommitData(commit);
}
void ProducerClient::CommitDataOnSequence(
const perfetto::CommitDataRequest& request) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
producer_host_->CommitData(request);
}
perfetto::SharedMemory* ProducerClient::shared_memory() const {
return shared_memory_.get();
}
size_t ProducerClient::shared_buffer_page_size_kb() const {
NOTREACHED();
return 0;
}
perfetto::SharedMemoryArbiter* ProducerClient::GetInProcessShmemArbiter() {
NOTREACHED();
return nullptr;
}
void ProducerClient::NotifyFlushComplete(perfetto::FlushRequestID id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (pending_replies_for_latest_flush_.first != id) {
// Ignore; completed flush was for an earlier request.
return;
}
DCHECK_NE(pending_replies_for_latest_flush_.second, 0u);
if (--pending_replies_for_latest_flush_.second == 0) {
shared_memory_arbiter_->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);
}
void ProducerClient::UnregisterTraceWriter(uint32_t writer_id) {
producer_host_->UnregisterTraceWriter(writer_id);
}
} // namespace tracing