blob: 80150ff0d18072cf9efe3a6bc1b41f90d8fc404d [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 "garnet/lib/cpuperf/controller.h"
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <zircon/syscalls.h>
#include "lib/fxl/logging.h"
#include "lib/fxl/strings/string_printf.h"
namespace cpuperf {
const char kCpuPerfDev[] = "/dev/sys/cpu-trace/cpu-trace";
static bool IsSampleMode(const cpuperf_config_t& config) {
for (size_t i = 0; i < countof(config.rate); ++i) {
// If any event is doing sampling, then we're in "sample mode".
if (config.rate[i] != 0) {
return true;
}
}
return false;
}
static uint32_t GetBufferSize(bool sample_mode, uint32_t requested_size_in_mb) {
if (sample_mode)
return requested_size_in_mb * 1024 * 1024;
// For "counting mode" we just need something large enough to hold
// the header + records for each event.
unsigned num_events = CPUPERF_MAX_EVENTS;
uint32_t size = (sizeof(cpuperf_buffer_header_t) +
num_events * sizeof(cpuperf_value_record_t));
return (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
}
Controller::Controller(uint32_t buffer_size_in_mb,
const cpuperf_config_t& config)
: sample_mode_(IsSampleMode(config)),
buffer_size_(GetBufferSize(sample_mode_, buffer_size_in_mb)),
config_(config),
alloc_(false),
started_(false) {
int fd = open(kCpuPerfDev, O_WRONLY);
if (fd < 0) {
FXL_LOG(ERROR) << "Failed to open " << kCpuPerfDev << ": errno=" << errno;
return;
}
fd_.reset(fd);
Alloc();
if (!alloc_)
fd_.reset();
}
Controller::~Controller() {
Stop();
Free();
}
bool Controller::is_valid() const { return fd_.is_valid() && alloc_; }
bool Controller::Start() {
if (!is_valid()) {
return false;
}
if (started_) {
FXL_LOG(ERROR) << "already started";
return false;
}
if (!Stage()) {
return false;
}
auto status = ioctl_cpuperf_start(fd_.get());
if (status != ZX_OK) {
FXL_LOG(ERROR) << "ioctl_cpuperf_start failed: status=" << status;
} else {
started_ = true;
}
return status == ZX_OK;
}
void Controller::Stop() {
if (!is_valid()) {
return;
}
auto status = ioctl_cpuperf_stop(fd_.get());
if (status != ZX_OK) {
// This can get called while tracing is currently stopped.
if (!started_ && status == ZX_ERR_BAD_STATE) {
; // dont report an error in this case
} else {
FXL_LOG(ERROR) << "ioctl_cpuperf_stop failed: status=" << status;
}
} else {
started_ = false;
}
}
void Controller::Alloc() {
FXL_DCHECK(!alloc_ && !started_);
ioctl_cpuperf_alloc_t alloc;
alloc.num_buffers = zx_system_get_num_cpus();
alloc.buffer_size = buffer_size_;
FXL_VLOG(2) << fxl::StringPrintf("num_buffers=%u, buffer_size=0x%x",
alloc.num_buffers, alloc.buffer_size);
auto status = ioctl_cpuperf_alloc_trace(fd_.get(), &alloc);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "ioctl_cpuperf_alloc_trace failed: status=" << status;
} else {
alloc_ = true;
}
}
bool Controller::Stage() {
FXL_DCHECK(is_valid() && !started_);
auto status = ioctl_cpuperf_stage_config(fd_.get(), &config_);
if (status != ZX_OK) {
FXL_LOG(ERROR) << "ioctl_cpuperf_stage_config failed: status=" << status;
}
return status == ZX_OK;
}
void Controller::Free() {
if (!is_valid()) {
return;
}
auto status = ioctl_cpuperf_free_trace(fd_.get());
if (status != ZX_OK) {
// This can get called while tracing is currently stopped.
if (!started_ && status == ZX_ERR_BAD_STATE) {
; // dont report an error in this case
} else {
FXL_LOG(ERROR) << "ioctl_cpuperf_free_trace failed: status=" << status;
}
}
}
std::unique_ptr<Reader> Controller::GetReader() {
return std::unique_ptr<Reader>(new Reader(fd_.get(), buffer_size_));
}
} // namespace cpuperf