blob: c6ff5b1b091f0676bdf8ee44cc187dab5e850625 [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This tool records a Perfetto trace in feedback reports, for example to assist
// diagnosing tasks with high CPU usage.
//
// Feedback reports contain perf stack samples, which can be useful for figuring
// out aggregate behavior (e.g., what code is hot). In contrast, Perfetto
// records a timeline of events, which can help show how different system
// components are interacting (e.g., what's blocking the processing user input
// events, or how well CPU core scheduling is working).
//
// Running the Perfetto profiler adds a small amount of CPU overhead, but
// this can be easily filtered out of the perf stack samples by ignoring
// samples from the traced and traced_probes processes. Including this data
// also increases the uploaded size of feedback reports by about 10-30%,
// depending on system activity.
//
// TODO(skyostil): Enable stack sampling support in Perfetto so that we no
// longer need to run the perf tool in parallel.
#include "debugd/src/perfetto_tool.h"
#include <fcntl.h>
#include <unistd.h>
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
namespace debugd {
PerfettoTool::PerfettoTool() = default;
PerfettoTool::~PerfettoTool() = default;
bool PerfettoTool::Init() {
int pipe_fds[2];
if (HANDLE_EINTR(pipe2(pipe_fds, O_CLOEXEC)) != 0)
return false;
pipe_[0].reset(pipe_fds[0]);
pipe_[1].reset(pipe_fds[1]);
perfetto_.SandboxAs("debugd", "traced-consumer");
perfetto_.AllowAccessRootMountNamespace();
if (!perfetto_.Init())
return false;
perfetto_.AddArg("/usr/bin/perfetto");
perfetto_.AddArg("-c");
perfetto_.AddArg("/usr/share/debugd/perfetto_feedback_config.textproto");
perfetto_.AddArg("--txt");
perfetto_.AddArg("-o");
perfetto_.AddArg("-");
perfetto_.set_separate_stderr(true);
perfetto_.BindFd(pipe_[1].get(), STDOUT_FILENO);
if (!compressor_.Init())
return false;
compressor_.AddArg("/usr/bin/zstd");
compressor_.AddArg("-qc");
compressor_.set_separate_stderr(true);
compressor_.BindFd(pipe_[0].get(), STDIN_FILENO);
return true;
}
bool PerfettoTool::StartImpl() {
bool ok = perfetto_.Start() && compressor_.Start();
pipe_[0].reset();
pipe_[1].reset();
return ok;
}
// static
std::unique_ptr<PerfettoTool> PerfettoTool::Start() {
auto tool = std::make_unique<PerfettoTool>();
if (!tool->Init() || !tool->StartImpl()) {
LOG(ERROR) << "Failed to start Perfetto";
return nullptr;
}
return tool;
}
std::optional<std::string> PerfettoTool::Stop() {
std::string trace;
bool ok = perfetto_.Wait() == EXIT_SUCCESS;
ok &= compressor_.Wait() == EXIT_SUCCESS;
if (ok && compressor_.GetOutput(&trace)) {
return trace;
}
std::string error;
if (!perfetto_.GetError(&error) || error.empty()) {
compressor_.GetError(&error);
}
LOG(ERROR) << "Failed to record Perfetto trace: " << error;
return std::nullopt;
}
} // namespace debugd