blob: e953999e0194183c4abd72ffa0f0e416e3b4c7da [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/bin/cpuperf_provider/app.h"
#include <iostream>
#include <limits>
#include <memory>
#include <lib/async/default.h>
#include <src/lib/fxl/command_line.h>
#include <src/lib/fxl/logging.h>
#include <src/lib/fxl/strings/string_number_conversions.h>
#include "garnet/bin/cpuperf_provider/categories.h"
#include "garnet/bin/cpuperf_provider/importer.h"
#include "garnet/lib/perfmon/controller.h"
#include "garnet/lib/perfmon/reader.h"
namespace cpuperf_provider {
namespace {
// If only fxl string/number conversions supported 0x.
bool ParseNumber(const char* name, const fxl::StringView& arg, uint64_t* value) {
if (arg.size() > 2 && arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X')) {
if (!fxl::StringToNumberWithError<uint64_t>(arg.substr(2), value, fxl::Base::k16)) {
FXL_LOG(ERROR) << "Invalid value for " << name << ": " << arg;
return false;
}
} else {
if (!fxl::StringToNumberWithError<uint64_t>(arg, value)) {
FXL_LOG(ERROR) << "Invalid value for " << name << ": " << arg;
return false;
}
}
return true;
}
bool GetBufferSizeInPages(uint32_t size_in_mb, uint32_t* out_num_pages) {
const uint64_t kPagesPerMb = 1024 * 1024 / perfmon::Controller::kPageSize;
const uint64_t kMaxSizeInMb = std::numeric_limits<uint32_t>::max() / kPagesPerMb;
if (size_in_mb > kMaxSizeInMb) {
return false;
}
*out_num_pages = size_in_mb * kPagesPerMb;
return true;
}
} // namespace
App::App(const fxl::CommandLine& command_line) : startup_context_(sys::ComponentContext::Create()) {
if (command_line.HasOption("help")) {
PrintHelp();
exit(EXIT_SUCCESS);
}
std::string buffer_size_as_string;
if (command_line.GetOptionValue("buffer-size", &buffer_size_as_string)) {
uint64_t buffer_size;
if (!ParseNumber("buffer-size", buffer_size_as_string, &buffer_size))
exit(EXIT_FAILURE);
if (buffer_size == 0) {
FXL_LOG(ERROR) << "Buffer size cannot be zero";
exit(EXIT_FAILURE);
}
// The provided buffer size is in MB, the controller takes the buffer size
// in pages.
uint32_t buffer_size_in_pages;
if (!GetBufferSizeInPages(buffer_size, &buffer_size_in_pages)) {
FXL_LOG(ERROR) << "Buffer size too large";
exit(EXIT_FAILURE);
}
buffer_size_in_pages_ = buffer_size_in_pages;
}
// The supported models and their names are determined by lib/perfmon.
// These are defaults for now.
model_event_manager_ = perfmon::ModelEventManager::Create(perfmon::GetDefaultModelName());
FXL_CHECK(model_event_manager_);
trace_observer_.Start(async_get_default_dispatcher(), [this] { UpdateState(); });
}
App::~App() {}
void App::PrintHelp() {
std::cout << "cpuperf_provider [options]" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " --help: Produce this help message" << std::endl;
std::cout << " --buffer-size=<size>: Trace data buffer size (MB) [default="
<< kDefaultBufferSizeInMb << "]" << std::endl;
}
void App::UpdateState() {
if (trace_state() == TRACE_STARTED) {
FXL_DCHECK(!IsTracing());
auto new_config = TraceConfig::Create(model_event_manager_.get(), trace_is_category_enabled);
if (new_config != nullptr && new_config->is_enabled()) {
StartTracing(std::move(new_config));
}
} else {
StopTracing();
}
}
void App::StartTracing(std::unique_ptr<TraceConfig> trace_config) {
FXL_DCHECK(trace_config->is_enabled());
FXL_DCHECK(!context_);
FXL_DCHECK(!controller_);
perfmon::Config device_config;
if (!trace_config->TranslateToDeviceConfig(&device_config)) {
FXL_LOG(ERROR) << "Error converting trace config to device config";
return;
}
std::unique_ptr<perfmon::Controller> controller;
if (!perfmon::Controller::Create(buffer_size_in_pages_, device_config, &controller)) {
FXL_LOG(ERROR) << "Perfmon controller failed to initialize";
return;
}
context_ = trace_acquire_prolonged_context();
if (!context_) {
// Tracing was disabled in the meantime.
return;
}
FXL_VLOG(1) << "Starting trace, config = " << trace_config->ToString();
start_time_ = zx_ticks_get();
if (!controller->Start())
goto Fail;
FXL_LOG(INFO) << "Started tracing";
trace_config_ = std::move(trace_config);
controller_.reset(controller.release());
return;
Fail:
trace_release_prolonged_context(context_);
context_ = nullptr;
}
void App::StopTracing() {
if (!IsTracing()) {
return;
}
FXL_DCHECK(trace_config_->is_enabled());
FXL_LOG(INFO) << "Stopping trace";
controller_->Stop();
stop_time_ = zx_ticks_get();
// Acquire a context for writing to the trace buffer.
auto buffer_context = trace_acquire_context();
auto reader = controller_->GetReader();
if (reader) {
Importer importer(buffer_context, trace_config_.get(), start_time_, stop_time_);
const perfmon::Config& config = controller_->config();
if (!importer.Import(*reader, config)) {
FXL_LOG(ERROR) << "Errors encountered while importing perfmon data";
}
} else {
FXL_LOG(ERROR) << "Unable to initialize reader";
}
trace_release_context(buffer_context);
trace_release_prolonged_context(context_);
context_ = nullptr;
trace_config_.reset();
controller_.reset();
}
} // namespace cpuperf_provider