blob: 5ebe272ed044171a734f64455ceea1888fad41dd [file] [log] [blame]
// Copyright 2018 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 "src/performance/cpuperf/session_spec.h"
#include <inttypes.h>
#include <lib/syslog/cpp/macros.h>
#include <stdio.h>
#include <stdlib.h>
#include <zircon/syscalls.h>
#include <array>
#include <iterator>
#include <limits>
#include <string>
#include <vector>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#include <rapidjson/schema.h>
#include <rapidjson/stringbuffer.h>
#include "src/lib/fxl/strings/string_printf.h"
#include "src/lib/json_parser/rapidjson_validation.h"
#include "src/performance/lib/perfmon/events.h"
namespace cpuperf {
namespace {
// Top-level schema.
const char kRootSchema[] = R"({
"type": "object",
"additionalProperties": false,
"properties": {
"config_name": {
"type": "string"
},
"model_name": {
"type": "string"
},
"events": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"group_name": {
"type": "string"
},
"event_name": {
"type": "string"
},
"rate": {
"type": "integer"
},
"flags": {
"type": "array",
"uniqueItems": true,
"items": {
"type": "string",
"enum": [
"os",
"user",
"pc",
"last_branch",
"timebase"
]
}
},
"required": [ "group_name", "event_name" ]
}
}
},
"buffer_size_in_mb": {
"type": "integer",
"minimum": 1
},
"duration": {
"type": "integer",
"minimum": 0
},
"num_iterations": {
"type": "integer",
"minimum": 1
},
"output_path_prefix": {
"type": "string"
},
"session_result_spec_path": {
"type": "string"
},
"required": [ "events" ]
}
})";
const char kConfigNameKey[] = "config_name";
const char kModelNameKey[] = "model_name";
const char kEventsKey[] = "events";
const char kGroupNameKey[] = "group_name";
const char kEventNameKey[] = "event_name";
const char kRateKey[] = "rate";
const char kFlagsKey[] = "flags";
const char kDurationKey[] = "duration";
const char kBufferSizeInMbKey[] = "buffer_size_in_mb";
const char kNumIterationsKey[] = "num_iterations";
const char kOutputPathPrefixKey[] = "output_path_prefix";
const char kSessionResultSpecPathKey[] = "session_result_spec_path";
template <typename T>
bool DecodeEvents(T events, const perfmon::ModelEventManager* model_event_manager,
SessionSpec* out_spec) {
FX_VLOGS(1) << "Processing " << events.Size() << " events";
for (const auto& event : events) {
perfmon::EventId id = perfmon::kEventIdNone;
perfmon::EventRate rate = 0;
uint32_t flags = 0;
if (!event.HasMember(kGroupNameKey) || !event.HasMember(kEventNameKey)) {
FX_LOGS(ERROR) << "Event is missing group_name,event_name fields";
return false;
}
const std::string& group_name = event[kGroupNameKey].GetString();
const std::string& event_name = event[kEventNameKey].GetString();
const perfmon::EventDetails* details;
if (!model_event_manager->LookupEventByName(group_name.c_str(), event_name.c_str(), &details)) {
FX_LOGS(ERROR) << "Unknown event: " << group_name << ":" << event_name;
return false;
}
id = details->id;
if (event.HasMember(kRateKey)) {
rate = event[kRateKey].GetUint();
}
if (event.HasMember(kFlagsKey)) {
for (const auto& flag : event[kFlagsKey].GetArray()) {
if (!flag.IsString()) {
FX_LOGS(ERROR) << "Flag for event " << group_name << ":" << event_name
<< " is not a string";
return false;
}
const std::string& flag_name = flag.GetString();
if (flag_name == "os") {
flags |= perfmon::Config::kFlagOs;
} else if (flag_name == "user") {
flags |= perfmon::Config::kFlagUser;
} else if (flag_name == "pc") {
flags |= perfmon::Config::kFlagPc;
} else if (flag_name == "timebase") {
flags |= perfmon::Config::kFlagTimebase;
} else if (flag_name == "last_branch") {
flags |= perfmon::Config::kFlagLastBranch;
} else {
FX_LOGS(ERROR) << "Unknown flag for event " << group_name << ":" << event_name << ": "
<< flag_name;
return false;
}
}
}
FX_VLOGS(2) << "Found event: " << group_name << ":" << event_name << ", id 0x" << std::hex << id
<< ", rate " << std::dec << rate << ", flags 0x" << std::hex << flags;
perfmon::Config::Status status = out_spec->perfmon_config.AddEvent(id, rate, flags);
if (status != perfmon::Config::Status::OK) {
FX_LOGS(ERROR) << "Error processing event configuration: "
<< perfmon::Config::StatusToString(status);
return false;
}
}
return true;
}
} // namespace
bool DecodeSessionSpec(const std::string& json, SessionSpec* out_spec) {
// Initialize schemas for JSON validation.
auto root_schema_result = json_parser::InitSchema(kRootSchema);
if (root_schema_result.is_error()) {
FX_LOGS(ERROR) << "Invalid schema: " << root_schema_result.error_value().ToString();
return false;
}
SessionSpec result;
rapidjson::Document document;
document.Parse<rapidjson::kParseCommentsFlag>(json.c_str(), json.size());
if (document.HasParseError()) {
auto offset = document.GetErrorOffset();
auto code = document.GetParseError();
FX_LOGS(ERROR) << "Couldn't parse the session config file: offset " << offset << ", "
<< GetParseError_En(code);
return false;
}
auto root_schema = std::move(root_schema_result.value());
auto validation_result = json_parser::ValidateSchema(document, root_schema, "session config");
if (validation_result.is_error()) {
FX_LOGS(ERROR) << "json validation failed: " << validation_result.error_value();
return false;
}
if (document.HasMember(kConfigNameKey)) {
result.config_name = document[kConfigNameKey].GetString();
}
if (document.HasMember(kModelNameKey)) {
result.model_name = document[kModelNameKey].GetString();
}
if (result.model_name == SessionSpec::kDefaultModelName) {
result.model_name = perfmon::GetDefaultModelName();
}
std::unique_ptr<perfmon::ModelEventManager> model_event_manager =
perfmon::ModelEventManager::Create(result.model_name);
if (!model_event_manager) {
FX_LOGS(ERROR) << "Unsupported model: " << result.model_name;
return false;
}
if (document.HasMember(kEventsKey)) {
const auto& events = document[kEventsKey].GetArray();
if (events.Size() == 0) {
FX_LOGS(ERROR) << "Need at least one event";
return false;
}
if (!DecodeEvents(events, model_event_manager.get(), &result)) {
return false;
}
}
if (document.HasMember(kBufferSizeInMbKey)) {
result.buffer_size_in_mb = document[kBufferSizeInMbKey].GetUint();
}
if (document.HasMember(kDurationKey)) {
result.duration = zx::sec(document[kDurationKey].GetUint());
}
if (document.HasMember(kNumIterationsKey)) {
result.num_iterations = document[kNumIterationsKey].GetUint();
}
if (document.HasMember(kOutputPathPrefixKey)) {
result.output_path_prefix = document[kOutputPathPrefixKey].GetString();
}
if (document.HasMember(kSessionResultSpecPathKey)) {
result.session_result_spec_path = document[kSessionResultSpecPathKey].GetString();
}
*out_spec = std::move(result);
out_spec->model_event_manager = std::move(model_event_manager);
return true;
}
const char SessionSpec::kDefaultModelName[] = "default";
const char SessionSpec::kDefaultOutputPathPrefix[] = "/tmp/cpuperf";
const char SessionSpec::kDefaultSessionResultSpecPath[] = "/tmp/cpuperf.cpsession";
SessionSpec::SessionSpec()
: model_name(kDefaultModelName),
output_path_prefix(kDefaultOutputPathPrefix),
session_result_spec_path(kDefaultSessionResultSpecPath) {}
} // namespace cpuperf