blob: c733025280f16446d9e77b57c1394e739565b425 [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 "garnet/bin/cpuperf_provider/categories.h"
#include "garnet/bin/cpuperf_provider/importer.h"
#include "garnet/lib/cpuperf/controller.h"
#include "garnet/lib/cpuperf/reader.h"
#include "lib/fxl/command_line.h"
#include "lib/fxl/logging.h"
#include "lib/fxl/strings/string_number_conversions.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;
}
} // namespace
App::App(const fxl::CommandLine& command_line)
: startup_context_(component::StartupContext::CreateFromStartupInfo()) {
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);
}
if (buffer_size > kMaxBufferSizeInMb) {
FXL_LOG(ERROR) << "Buffer size too large, max " << kMaxBufferSizeInMb;
exit(EXIT_FAILURE);
}
buffer_size_in_mb_ = static_cast<uint32_t>(buffer_size);
}
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) {
TraceConfig config;
config.Update();
if (trace_config_.Changed(config)) {
StopTracing();
if (config.is_enabled())
StartTracing(config);
}
} else {
StopTracing();
}
}
void App::StartTracing(const TraceConfig& trace_config) {
FXL_DCHECK(trace_config.is_enabled());
FXL_DCHECK(!context_);
FXL_DCHECK(!controller_);
cpuperf_config_t device_config;
if (!trace_config.TranslateToDeviceConfig(&device_config)) {
FXL_LOG(ERROR) << "Error converting trace config to device config";
return;
}
std::unique_ptr<cpuperf::Controller> controller;
if (!cpuperf::Controller::Create(buffer_size_in_mb_, device_config,
&controller)) {
FXL_LOG(ERROR) << "Cpuperf 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_ = trace_config;
controller_.reset(controller.release());
return;
Fail:
trace_release_prolonged_context(context_);
context_ = nullptr;
}
void App::StopTracing() {
if (!context_) {
return; // not currently tracing
}
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_, start_time_, stop_time_);
if (!importer.Import(*reader)) {
FXL_LOG(ERROR) << "Errors encountered while importing cpuperf 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