blob: 009dc151fad40dd75bdfed43ba937f82766ad936 [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/perfetto/perfetto_tracing_coordinator.h"
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "mojo/public/cpp/system/data_pipe_utils.h"
#include "services/tracing/perfetto/json_trace_exporter.h"
#include "services/tracing/perfetto/perfetto_service.h"
#include "third_party/perfetto/include/perfetto/tracing/core/trace_stats.h"
namespace tracing {
// A TracingSession is used to wrap all the associated
// state of an on-going tracing session, for easy setup
// and cleanup.
class PerfettoTracingCoordinator::TracingSession {
public:
TracingSession(const std::string& config,
base::OnceClosure tracing_over_callback)
: tracing_over_callback_(std::move(tracing_over_callback)) {
json_trace_exporter_ = std::make_unique<JSONTraceExporter>(
config, PerfettoService::GetInstance()->GetService());
}
~TracingSession() {
if (!stop_and_flush_callback_.is_null()) {
base::ResetAndReturn(&stop_and_flush_callback_)
.Run(base::Value(base::Value::Type::DICTIONARY));
}
stream_.reset();
}
void StopAndFlush(mojo::ScopedDataPipeProducerHandle stream,
StopAndFlushCallback callback) {
stream_ = std::move(stream);
stop_and_flush_callback_ = std::move(callback);
json_trace_exporter_->StopAndFlush(base::BindRepeating(
&TracingSession::OnJSONTraceEventCallback, base::Unretained(this)));
}
void OnJSONTraceEventCallback(const std::string& json,
base::DictionaryValue* metadata,
bool has_more) {
if (stream_.is_valid()) {
mojo::BlockingCopyFromString(json, stream_);
}
if (!has_more) {
DCHECK(!stop_and_flush_callback_.is_null());
base::ResetAndReturn(&stop_and_flush_callback_)
.Run(/*metadata=*/std::move(*metadata));
std::move(tracing_over_callback_).Run();
// |this| is now destroyed.
}
}
void RequestBufferUsage(RequestBufferUsageCallback callback) {
if (!request_buffer_usage_callback_.is_null()) {
// TODO(eseckler): Reconsider whether we can DCHECK here after refactoring
// of content's TracingController.
std::move(callback).Run(false, 0, 0);
return;
}
request_buffer_usage_callback_ = std::move(callback);
json_trace_exporter_->GetTraceStats(
base::BindOnce(&TracingSession::OnTraceStats, base::Unretained(this)));
}
void OnTraceStats(bool success, const perfetto::TraceStats& stats) {
DCHECK(request_buffer_usage_callback_);
if (!success) {
std::move(request_buffer_usage_callback_).Run(false, 0.0f, 0);
return;
}
DCHECK_EQ(1, stats.buffer_stats_size());
const perfetto::TraceStats::BufferStats& buf_stats =
stats.buffer_stats()[0];
size_t bytes_in_buffer =
buf_stats.bytes_written() - buf_stats.bytes_read() -
buf_stats.bytes_overwritten() + buf_stats.padding_bytes_written() -
buf_stats.padding_bytes_cleared();
double percent_full =
bytes_in_buffer / static_cast<double>(buf_stats.buffer_size());
percent_full = std::min(std::max(0.0, percent_full), 1.0);
// We know the size of data in the buffer, but not the number of events.
std::move(request_buffer_usage_callback_)
.Run(true, percent_full, 0 /*approx_event_count*/);
}
private:
mojo::ScopedDataPipeProducerHandle stream_;
std::unique_ptr<JSONTraceExporter> json_trace_exporter_;
StopAndFlushCallback stop_and_flush_callback_;
base::OnceClosure tracing_over_callback_;
RequestBufferUsageCallback request_buffer_usage_callback_;
DISALLOW_COPY_AND_ASSIGN(TracingSession);
};
PerfettoTracingCoordinator::PerfettoTracingCoordinator(
AgentRegistry* agent_registry,
base::RepeatingClosure on_disconnect_callback)
: Coordinator(agent_registry, std::move(on_disconnect_callback)),
binding_(this),
weak_factory_(this) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
PerfettoTracingCoordinator::~PerfettoTracingCoordinator() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void PerfettoTracingCoordinator::OnClientConnectionError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
tracing_session_.reset();
binding_.Close();
Coordinator::OnClientConnectionError();
}
void PerfettoTracingCoordinator::BindCoordinatorRequest(
mojom::CoordinatorRequest request,
const service_manager::BindSourceInfo& source_info) {
PerfettoService::GetInstance()->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&PerfettoTracingCoordinator::BindOnSequence,
base::Unretained(this), std::move(request)));
}
void PerfettoTracingCoordinator::BindOnSequence(
mojom::CoordinatorRequest request) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
binding_.Bind(std::move(request));
binding_.set_connection_error_handler(
base::BindOnce(&PerfettoTracingCoordinator::OnClientConnectionError,
base::Unretained(this)));
}
void PerfettoTracingCoordinator::StartTracing(const std::string& config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
tracing_session_ = std::make_unique<TracingSession>(
config, base::BindOnce(&PerfettoTracingCoordinator::OnTracingOverCallback,
weak_factory_.GetWeakPtr()));
}
void PerfettoTracingCoordinator::OnTracingOverCallback() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(tracing_session_);
tracing_session_.reset();
}
void PerfettoTracingCoordinator::StopAndFlush(
mojo::ScopedDataPipeProducerHandle stream,
StopAndFlushCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(tracing_session_);
tracing_session_->StopAndFlush(std::move(stream), std::move(callback));
}
void PerfettoTracingCoordinator::StopAndFlushAgent(
mojo::ScopedDataPipeProducerHandle stream,
const std::string& agent_label,
StopAndFlushCallback callback) {
NOTREACHED();
}
void PerfettoTracingCoordinator::IsTracing(IsTracingCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(callback).Run(tracing_session_ != nullptr);
}
void PerfettoTracingCoordinator::RequestBufferUsage(
RequestBufferUsageCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!tracing_session_) {
std::move(callback).Run(false, 0, 0);
return;
}
tracing_session_->RequestBufferUsage(std::move(callback));
}
} // namespace tracing