| // 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 |