blob: 259793ecdcd7432ce75c4b2e417563337b9d01c5 [file] [log] [blame]
// Copyright 2019 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 "workload.h"
#include <lib/syslog/cpp/macros.h>
#include <lib/zircon-internal/ktrace.h>
#include <lib/zx/port.h>
#include <lib/zx/profile.h>
#include <lib/zx/time.h>
#include <lib/zx/timer.h>
#include <zircon/syscalls/port.h>
#include <chrono>
#include <memory>
#include <optional>
#include <regex>
#include <sstream>
#include <utility>
#include <vector>
#include "action.h"
#include "object.h"
#include "random.h"
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "src/lib/files/file.h"
#include "utility.h"
#include "worker.h"
struct SequenceAction : ActionBase<SequenceAction, ActionDefaultCopyable::False> {
explicit SequenceAction(std::vector<std::unique_ptr<Action>> actions)
: actions{std::move(actions)} {}
void Perform(Worker* worker) override {
for (auto& action : actions) {
action->Perform(worker);
}
}
std::unique_ptr<Action> Copy() const override {
std::vector<std::unique_ptr<Action>> actions_copy;
for (const auto& action : actions) {
actions_copy.emplace_back(action->Copy());
}
return std::make_unique<SequenceAction>(std::move(actions_copy));
}
std::vector<std::unique_ptr<Action>> actions;
};
struct SleepDurationAction : ActionBase<SleepDurationAction> {
explicit SleepDurationAction(std::chrono::nanoseconds duration_ns) : duration_ns{duration_ns} {}
void Perform(Worker* worker) override { worker->Sleep(duration_ns); }
const std::chrono::nanoseconds duration_ns;
};
struct SleepUniformAction : ActionBase<SleepUniformAction> {
SleepUniformAction(std::chrono::nanoseconds min_ns, std::chrono::nanoseconds max_ns)
: min_ns{min_ns}, max_ns{max_ns} {}
void Perform(Worker* worker) override {
const std::chrono::nanoseconds duration_ns{random.GetUniform(min_ns.count(), max_ns.count())};
worker->Sleep(duration_ns);
}
const std::chrono::nanoseconds min_ns;
const std::chrono::nanoseconds max_ns;
Random random;
};
struct SpinDurationAction : ActionBase<SpinDurationAction> {
explicit SpinDurationAction(std::chrono::nanoseconds duration_ns) : duration_ns{duration_ns} {}
void Perform(Worker* worker) override { worker->Spin(duration_ns); }
const std::chrono::nanoseconds duration_ns;
};
struct SpinUniformAction : ActionBase<SpinUniformAction> {
SpinUniformAction(std::chrono::nanoseconds min_ns, std::chrono::nanoseconds max_ns)
: min_ns{min_ns}, max_ns{max_ns} {}
void Perform(Worker* worker) override {
const std::chrono::nanoseconds duration_ns{random.GetUniform(min_ns.count(), max_ns.count())};
worker->Spin(duration_ns);
}
const std::chrono::nanoseconds min_ns;
const std::chrono::nanoseconds max_ns;
Random random;
};
struct YieldAction : ActionBase<YieldAction> {
void Perform(Worker* worker) override { worker->Yield(); }
};
struct ExitAction : ActionBase<ExitAction> {
void Perform(Worker* worker) override { worker->Exit(); }
};
struct SetProfileAction : ActionBase<SetProfileAction> {
SetProfileAction(zx::unowned_profile profile, bool once)
: profile{std::move(profile)}, once{once} {}
void Perform(Worker* worker) override {
if (!once || !completed) {
completed = true;
worker->SetProfile(profile);
}
}
zx::unowned_profile profile;
const bool once;
bool completed{false};
};
struct SetTimerAction : ActionBase<SetTimerAction> {
SetTimerAction(TimerObject timer, std::chrono::nanoseconds relative_deadline_ns,
std::chrono::nanoseconds timer_slack_ns)
: timer{timer}, relative_deadline_ns{relative_deadline_ns}, timer_slack_ns{timer_slack_ns} {}
void Perform(Worker*) override {
const zx_status_t status =
timer->set(zx::deadline_after(zx::duration{relative_deadline_ns.count()}),
zx::duration{timer_slack_ns.count()});
FX_CHECK(status == ZX_OK);
}
TimerObject timer;
const std::chrono::nanoseconds relative_deadline_ns;
const std::chrono::nanoseconds timer_slack_ns;
};
struct ChannelWriteAction : ActionBase<ChannelWriteAction> {
ChannelWriteAction(ChannelObject channel, size_t side, size_t bytes)
: channel{channel},
endpoint{side == 0 ? channel->first : channel->second},
bytes{bytes},
buffer(bytes) {}
void Perform(Worker*) override {
const auto status = endpoint->write(0, buffer.data(), buffer.size(), nullptr, 0);
FX_CHECK(status == ZX_OK);
}
ChannelObject channel;
zx::unowned_channel endpoint;
size_t bytes;
std::vector<uint8_t> buffer;
};
struct ChannelReadAction : ActionBase<ChannelReadAction> {
ChannelReadAction(ChannelObject channel, size_t side)
: channel{channel},
endpoint{side == 0 ? channel->first : channel->second},
buffer(64 * 1024) {}
void Perform(Worker*) override {
uint32_t actual_bytes;
uint32_t actual_handles;
const auto status =
endpoint->read(0, buffer.data(), nullptr, buffer.size(), 0, &actual_bytes, &actual_handles);
FX_CHECK(status == ZX_OK) << "Failed to read channel: " << status;
}
ChannelObject channel;
zx::unowned_channel endpoint;
std::vector<uint8_t> buffer;
};
struct WaitOneAction : ActionBase<WaitOneAction> {
WaitOneAction(zx::unowned_handle handle, zx_signals_t signals,
std::optional<std::chrono::nanoseconds> relative_deadline_ns = std::nullopt)
: handle{handle}, signals{signals}, relative_deadline_ns{relative_deadline_ns} {}
void Perform(Worker*) {
const auto absolute_deadline =
relative_deadline_ns ? zx::deadline_after(zx::duration{relative_deadline_ns->count()})
: zx::time::infinite();
const auto status = handle->wait_one(signals, absolute_deadline, nullptr);
FX_CHECK(status == ZX_OK) << "Failed to signal object: " << status;
}
zx::unowned_handle handle;
zx_signals_t signals;
std::optional<std::chrono::nanoseconds> relative_deadline_ns;
};
struct WaitAsyncAction : ActionBase<WaitAsyncAction> {
WaitAsyncAction(zx::unowned_port port, zx::unowned_handle handle, zx_signals_t signals)
: port{port}, handle{handle}, signals{signals} {}
void Perform(Worker*) override {
const auto status = handle->wait_async(*port, 0, signals, 0);
FX_CHECK(status == ZX_OK) << "Faild to wait async: " << status;
}
zx::unowned_port port;
zx::unowned_handle handle;
zx_signals_t signals;
};
struct PortWaitAction : ActionBase<PortWaitAction, ActionDefaultCopyable::False> {
explicit PortWaitAction(
PortObject port, std::optional<std::chrono::nanoseconds> relative_deadline_ns = std::nullopt)
: port{port}, relative_deadline_ns{relative_deadline_ns} {
RegisterTerminateEvent();
}
void RegisterTerminateEvent() {
const auto status = PortObject::GetTerminateEvent()->wait_async(
*port.object(), 0, PortObject::kTerminateSignal, 0);
FX_CHECK(status == ZX_OK) << "Failed to wait async on terminate event: " << status;
}
void Perform(Worker*) override {
zx_port_packet_t packet;
const auto absolute_deadline =
relative_deadline_ns ? zx::deadline_after(zx::duration{relative_deadline_ns->count()})
: zx::time::infinite();
const auto status = port->wait(absolute_deadline, &packet);
// TODO(eieio): Add option to fail or not on timeout.
FX_CHECK(status == ZX_OK || status == ZX_ERR_TIMED_OUT) << "Failed to port wait: " << status;
}
std::unique_ptr<Action> Copy() const override {
auto copy = std::make_unique<PortWaitAction>(*this);
// Register an additional packet for every copy.
copy->RegisterTerminateEvent();
return copy;
}
PortObject port;
const std::optional<std::chrono::nanoseconds> relative_deadline_ns;
};
struct ObjectSignalAction : ActionBase<ObjectSignalAction> {
ObjectSignalAction(zx::unowned_handle handle, zx_signals_t clear_mask, zx_signals_t set_mask)
: handle{handle}, clear_mask{clear_mask}, set_mask{set_mask} {}
void Perform(Worker*) {
const auto status = handle->signal(clear_mask, set_mask);
FX_CHECK(status == ZX_OK) << "Failed to signal object: " << status;
}
zx::unowned_handle handle;
zx_signals_t clear_mask;
zx_signals_t set_mask;
};
// Proxies an iterator over the members of the given value node. As of this
// writing, the version of rapidjson in third_party does not support range-based
// for loops. This adapter provides the missing functionality.
struct IterateMembers {
explicit IterateMembers(const rapidjson::Value& value)
: begin_iterator{value.MemberBegin()}, end_iterator{value.MemberEnd()} {}
auto begin() { return begin_iterator; }
auto end() { return end_iterator; }
rapidjson::Value::ConstMemberIterator begin_iterator;
rapidjson::Value::ConstMemberIterator end_iterator;
};
// Proxies an iterator over the values of the given array node. Provides missing
// functionality similar to the iterator above.
struct IterateValues {
explicit IterateValues(const rapidjson::Value& value)
: begin_iterator{value.Begin()}, end_iterator{value.End()} {}
auto begin() { return begin_iterator; }
auto end() { return end_iterator; }
rapidjson::Value::ConstValueIterator begin_iterator;
rapidjson::Value::ConstValueIterator end_iterator;
};
template <typename... Context>
const auto& GetMember(const char* name, const rapidjson::Value& object, Context&&... context) {
FX_CHECK(object.IsObject())
<< (std::ostringstream{} << ... << std::forward<Context>(context)).rdbuf()
<< " must be a JSON object!";
FX_CHECK(object.HasMember(name))
<< (std::ostringstream{} << ... << std::forward<Context>(context)).rdbuf()
<< " must have a \"" << name << "\" member!";
return object[name];
}
template <typename... Context>
auto GetInt(const char* name, const rapidjson::Value& object, Context&&... context) {
const auto& member = GetMember(name, object, std::forward<Context>(context)...);
FX_CHECK(member.IsInt())
<< (std::ostringstream{} << ... << std::forward<Context>(context)).rdbuf() << " member \""
<< name << "\" must be an integer!";
return member.GetInt();
}
template <typename... Context>
const auto* GetString(const char* name, const rapidjson::Value& object, Context&&... context) {
const auto& member = GetMember(name, object, std::forward<Context>(context)...);
FX_CHECK(member.IsString())
<< (std::ostringstream{} << ... << std::forward<Context>(context)).rdbuf() << " member \""
<< name << "\" must be a string!";
return member.GetString();
}
template <typename... Context>
const auto& GetArray(const char* name, const rapidjson::Value& object, Context&&... context) {
const auto& member = GetMember(name, object, std::forward<Context>(context)...);
FX_CHECK(member.IsArray())
<< (std::ostringstream{} << ... << std::forward<Context>(context)).rdbuf() << " member \""
<< name << "\" must be an array!";
return member;
}
template <typename... Context>
const auto& GetObject(const char* name, const rapidjson::Value& object, Context&&... context) {
const auto& member = GetMember(name, object, std::forward<Context>(context)...);
FX_CHECK(member.IsObject())
<< (std::ostringstream{} << ... << std::forward<Context>(context)).rdbuf() << " member \""
<< name << "\" must be a JSON object!";
return member;
}
template <typename... Context>
auto GetUint(const char* name, const rapidjson::Value& object, Context&&... context) {
const auto& member = GetMember(name, object, std::forward<Context>(context)...);
FX_CHECK(member.IsUint())
<< (std::ostringstream{} << ... << std::forward<Context>(context)).rdbuf() << " member \""
<< name << "\" must be an unsigned integer!";
return member.GetUint();
}
void Workload::ParseObject(const std::string& name, const rapidjson::Value& object) {
const std::string type_string = GetString("type", object, "Named object \"", name, "\"");
if (type_string == "timer") {
Add(name, TimerObject::Create());
} else if (type_string == "port") {
Add(name, PortObject::Create());
} else if (type_string == "channel") {
Add(name, ChannelObject::Create());
} else if (type_string == "event") {
Add(name, EventObject::Create());
} else {
FX_CHECK(false) << "Object \"" << name << "\" has unknown type \"" << type_string << "\"!";
}
}
Workload::Duration Workload::ParseDuration(const rapidjson::Value& object) {
if (object.IsInt()) {
return {std::chrono::nanoseconds{object.GetInt()}};
} else if (object.IsString()) {
return {ParseDurationString(object.GetString())};
} else {
FX_CHECK(false) << "Duration must be an integer or string!";
__builtin_unreachable();
}
}
Workload::Uniform Workload::ParseUniform(const rapidjson::Value& object) {
auto [min_ns] = ParseDuration(GetMember("min", object, "Uniform object"));
auto [max_ns] = ParseDuration(GetMember("max", object, "Uniform object"));
return {min_ns, max_ns};
}
std::variant<Workload::Duration, Workload::Uniform> Workload::ParseInterval(
const rapidjson::Value& object, AcceptNamedIntervalFlag accept_named_interval) {
FX_CHECK(object.IsObject()) << "Interval must be a JSON object!";
const bool has_duration = object.HasMember("duration");
const bool has_uniform = object.HasMember("uniform");
const bool has_interval = object.HasMember("interval");
FX_CHECK(accept_named_interval || !has_interval)
<< "Timespec \"interval\" is not supported in this context!";
FX_CHECK((has_duration + has_uniform + has_interval) == 1)
<< "Interval must have exactly one timespec: either \"uniform\" or "
"\"duration\""
<< (accept_named_interval ? " or \"interval\"" : "") << "!";
if (has_duration) {
return {ParseDuration(object["duration"])};
} else if (has_uniform) {
return {ParseUniform(object["uniform"])};
} else if (has_interval) {
const auto* interval_name_string = GetString("interval", object, "Interval");
auto search = intervals_.find(interval_name_string);
FX_CHECK(search != intervals_.end())
<< "Undefined named interval \"" << interval_name_string << "\"!";
return search->second;
} else {
__builtin_unreachable();
}
}
void Workload::ParseNamedInterval(const std::string& name, const rapidjson::Value& object) {
FX_CHECK(object.IsObject()) << "Named interval must be a JSON object!";
const auto result = ParseInterval(object, RejectNamedInterval);
auto [iter, okay] = intervals_.emplace(name, result);
FX_CHECK(okay) << "Named interval \"" << name << "\" defined more than once!";
}
zx::unowned_handle Workload::ParseTargetObjectAndGetHandle(const std::string& name,
const rapidjson::Value& object,
const std::string& context) {
Object& target = Get(name);
switch (target.type()) {
case TimerObject::Type:
return zx::unowned_handle{static_cast<TimerObject&>(target)->get()};
case ChannelObject::Type: {
const size_t side = GetInt("side", object, context);
FX_CHECK(side == 0 || side == 1)
<< "Wait async action member \"side\" must be an integer value 0 or 1!";
auto [first, second] = static_cast<ChannelObject&>(target).bind();
return zx::unowned_handle{side == 0 ? first->get() : second->get()};
}
case EventObject::Type:
return zx::unowned_handle{static_cast<EventObject&>(target)->get()};
case PortObject::Type:
return zx::unowned_handle{static_cast<PortObject&>(target)->get()};
default:
FX_CHECK(false) << "Unknown object type: " << target.type();
__builtin_unreachable();
}
}
std::unique_ptr<Action> Workload::ParseAction(const rapidjson::Value& action) {
const std::string action_string = GetString("action", action, "Action");
if (action_string == "spin") {
const auto result = ParseInterval(action, AcceptNamedInterval);
if (std::holds_alternative<Duration>(result)) {
const auto [duration_ns] = std::get<Duration>(result);
return SpinDurationAction::Create(duration_ns);
} else /*if (std::holds_alternative<Uniform>(result))*/ {
const auto [min_ns, max_ns] = std::get<Uniform>(result);
return SpinUniformAction::Create(min_ns, max_ns);
}
} else if (action_string == "sleep") {
const auto result = ParseInterval(action, AcceptNamedInterval);
if (std::holds_alternative<Duration>(result)) {
const auto [duration_ns] = std::get<Duration>(result);
return SleepDurationAction::Create(duration_ns);
} else /*if (std::holds_alternative<Uniform>(result))*/ {
const auto [min_ns, max_ns] = std::get<Uniform>(result);
return SleepUniformAction::Create(min_ns, max_ns);
}
} else if (action_string == "yield") {
return YieldAction::Create();
} else if (action_string == "write") {
const auto* channel_name = GetString("channel", action, "Write action");
const size_t side = GetInt("side", action, "Write action");
const size_t bytes = GetInt("bytes", action, "Write action");
return ChannelWriteAction::Create(Get<ChannelObject>(channel_name), side, bytes);
} else if (action_string == "read") {
const auto* channel_name = GetString("channel", action, "Read action");
const size_t side = GetInt("side", action, "Read action");
return ChannelReadAction::Create(Get<ChannelObject>(channel_name), side);
} else if (action_string == "behavior") {
const auto* behavior_name = GetString("name", action, "Behavior action");
auto search = behaviors_.find(behavior_name);
FX_CHECK(search != behaviors_.end()) << "Unknown named behavior \"" << behavior_name << "\"!";
return search->second->Copy();
} else if (action_string == "wait_async") {
const auto* context = "Wait async action";
const auto* port_name = GetString("port", action, context);
const auto* object_name = GetString("object", action, context);
const auto signals = GetInt("signals", action, context);
auto& port_object = Get<PortObject>(port_name);
zx::unowned_handle handle = ParseTargetObjectAndGetHandle(object_name, action, context);
return WaitAsyncAction::Create(zx::unowned_port{port_object->get()}, std::move(handle),
signals);
} else if (action_string == "wait_one") {
const auto* context = "Wait one action";
const auto* object_name = GetString("object", action, context);
const auto signals = GetInt("signals", action, context);
std::optional<std::chrono::nanoseconds> relative_deadline_ns;
if (action.HasMember("deadline")) {
relative_deadline_ns = ParseDuration(action["deadline"]).value;
}
zx::unowned_handle handle = ParseTargetObjectAndGetHandle(object_name, action, context);
return WaitOneAction::Create(std::move(handle), signals, relative_deadline_ns);
} else if (action_string == "port_wait") {
const auto* port_name = GetString("port", action, "Port wait action");
std::optional<std::chrono::nanoseconds> relative_deadline_ns;
if (action.HasMember("deadline")) {
relative_deadline_ns = ParseDuration(action["deadline"]).value;
}
return PortWaitAction::Create(Get<PortObject>(port_name), relative_deadline_ns);
} else if (action_string == "signal") {
const auto* context = "Signal action";
const auto* object_name = GetString("object", action, context);
const auto clear_mask = GetInt("clear", action, context);
const auto set_mask = GetInt("set", action, context);
zx::unowned_handle handle = ParseTargetObjectAndGetHandle(object_name, action, context);
return ObjectSignalAction::Create(std::move(handle), clear_mask, set_mask);
} else if (action_string == "timer_set") {
const auto* timer_name = GetString("timer", action, "Timer set action");
const auto relative_deadline_ns = ParseDuration(action["deadline"]).value;
const auto timer_slack_ns = action.HasMember("slack") ? ParseDuration(action["slack"]).value
: std::chrono::nanoseconds{0};
auto& timer_object = Get<TimerObject>(timer_name);
return SetTimerAction::Create(timer_object, relative_deadline_ns, timer_slack_ns);
} else if (action_string == "exit") {
return ExitAction::Create();
} else {
FX_CHECK(false) << "Unknown action \"" << action_string << "\"!";
__builtin_unreachable();
}
}
void Workload::ParseNamedBehavior(const std::string& name, const rapidjson::Value& behavior) {
if (behavior.IsObject()) {
auto [iter, okay] = behaviors_.emplace(name, ParseAction(behavior));
FX_CHECK(okay) << "Behavior \"" << name << "\" defined more than once!";
} else if (behavior.IsArray()) {
std::vector<std::unique_ptr<Action>> actions;
for (const auto& action : IterateValues(behavior)) {
actions.emplace_back(ParseAction(action));
}
auto [iter, okay] = behaviors_.emplace(name, SequenceAction::Create(std::move(actions)));
FX_CHECK(okay) << "Behavior \"" << name << "\" defined more than once!";
} else {
FX_CHECK(false) << "Behavior \"" << name << "\" must be a JSON object or array!";
}
}
void Workload::ParseWorker(const rapidjson::Value& worker) {
FX_CHECK(worker.IsObject()) << "Worker must be a JSON object!";
WorkerConfig config;
if (worker.HasMember("name")) {
config.name = GetString("name", worker, "Worker");
}
if (worker.HasMember("group")) {
config.group = GetString("group", worker, "Worker");
}
if (worker.HasMember("priority")) {
const auto& priority_member = worker["priority"];
const bool is_int = priority_member.IsInt();
const bool is_object = priority_member.IsObject();
FX_CHECK(is_int || is_object)
<< "Worker member \"priority\" must either be an integer or a JSON object!";
if (is_int) {
config.priority = GetInt("priority", worker, "Worker");
} else if (is_object) {
const bool has_capacity = priority_member.HasMember("capacity");
const bool has_deadline = priority_member.HasMember("deadline");
const bool has_period = priority_member.HasMember("period");
FX_CHECK(has_capacity && has_deadline && has_period)
<< "Worker member \"priority\" must have members \"capacity\", \"deadline\", and "
"\"period\"!";
const zx::duration capacity{ParseDuration(priority_member["capacity"]).value.count()};
const zx::duration deadline{ParseDuration(priority_member["deadline"]).value.count()};
const zx::duration period{ParseDuration(priority_member["period"]).value.count()};
config.priority = WorkerConfig::DeadlineParams{capacity, deadline, period};
}
}
if (worker.HasMember("actions")) {
const auto& actions_member = worker["actions"];
const bool is_array = actions_member.IsArray();
const bool is_string = actions_member.IsString();
FX_CHECK(is_array || is_string)
<< "Worker member \"actions\" must either be a string or a JSON object!";
if (is_array) {
const auto& actions = GetArray("actions", worker, "Worker");
for (const auto& action : IterateValues(actions)) {
config.actions.emplace_back(ParseAction(action));
}
} else if (is_string) {
const auto* behavior_name = GetString("actions", worker, "Worker");
auto search = behaviors_.find(behavior_name);
FX_CHECK(search != behaviors_.end()) << "Unknown named behavior \"" << behavior_name << "\"!";
config.actions.emplace_back(search->second->Copy());
}
}
int instances = 1;
if (worker.HasMember("instances")) {
const auto& instances_member = worker["instances"];
const bool is_integer = instances_member.IsInt();
const bool is_string = instances_member.IsString();
const bool is_object = instances_member.IsObject();
FX_CHECK(is_integer || is_string || is_object)
<< "Worker member \"instances\" must either be an integer, string or a JSON object!";
if (is_integer) {
instances = GetInt("instances", worker, "Worker");
} else if (is_string) {
instances = ParseInstancesString(GetString("instances", worker, "Worker"));
} else if (is_object) {
FX_CHECK(false) << "Worker member \"instances\" expressions are not yet implemented!";
}
}
if (instances <= 0) {
FX_LOGS(WARNING) << "Worker configured with instances=" << instances << "!";
}
for (int i = 0; i < instances; i++) {
workers_.emplace_back(config);
}
}
void Workload::ParseTracing(const rapidjson::Value& tracing) {
FX_CHECK(tracing.IsObject()) << "Tracing configuration must be a JSON object!";
TracingConfig config;
if (tracing.HasMember("group mask")) {
const auto& group_mask = GetMember("group mask", tracing, "Tracing");
if (group_mask.IsUint()) {
config.group_mask = GetUint("group mask", tracing, "Tracing");
} else if (group_mask.IsString()) {
const std::string group_mask_str = GetString("group mask", tracing, "Tracing");
if (group_mask_str == "KTRACE_GRP_ALL") {
config.group_mask = KTRACE_GRP_ALL;
} else if (group_mask_str == "KTRACE_GRP_META") {
config.group_mask = KTRACE_GRP_META;
} else if (group_mask_str == "KTRACE_GRP_LIFECYCLE") {
config.group_mask = KTRACE_GRP_LIFECYCLE;
} else if (group_mask_str == "KTRACE_GRP_SCHEDULER") {
config.group_mask = KTRACE_GRP_SCHEDULER;
} else if (group_mask_str == "KTRACE_GRP_TASKS") {
config.group_mask = KTRACE_GRP_TASKS;
} else if (group_mask_str == "KTRACE_GRP_IPC") {
config.group_mask = KTRACE_GRP_IPC;
} else if (group_mask_str == "KTRACE_GRP_IRQ") {
config.group_mask = KTRACE_GRP_IRQ;
} else if (group_mask_str == "KTRACE_GRP_PROBE") {
config.group_mask = KTRACE_GRP_PROBE;
} else if (group_mask_str == "KTRACE_GRP_ARCH") {
config.group_mask = KTRACE_GRP_ARCH;
} else if (group_mask_str == "KTRACE_GRP_SYSCALL") {
config.group_mask = KTRACE_GRP_SYSCALL;
} else if (group_mask_str == "KTRACE_GRP_VM") {
config.group_mask = KTRACE_GRP_VM;
} else {
FX_LOGS(WARNING) << "Tracing enabled with unknown group mask, mask set to all groups.";
config.group_mask = KTRACE_GRP_ALL;
}
} else {
FX_CHECK(false) << "Tracing group mask must be an unsigned integer or string!";
__builtin_unreachable();
}
} else /*Set default tracing group mask*/ {
FX_LOGS(WARNING) << "Tracing enabled with no group mask specified, mask set to all groups.";
config.group_mask = KTRACE_GRP_ALL;
}
if (tracing.HasMember("filepath")) {
config.filepath = GetString("filepath", tracing, "Tracing");
}
if (tracing.HasMember("string ref")) {
config.trace_string_ref = GetString("string ref", tracing, "Tracing");
}
tracing_ = config;
}
void GetLineAndColumnForOffset(const std::string& input, size_t offset, int32_t* output_line,
int32_t* output_column) {
if (offset == 0) {
// Errors at position 0 are assumed to be related to the whole file.
*output_line = 0;
*output_column = 0;
return;
}
*output_line = 1;
*output_column = 1;
for (size_t i = 0; i < input.size() && i < offset; i++) {
if (input[i] == '\n') {
*output_line += 1;
*output_column = 1;
} else {
*output_column += 1;
}
}
}
std::string GetErrorMessage(const rapidjson::Document& document, const std::string& file_data) {
int32_t line;
int32_t column;
GetLineAndColumnForOffset(file_data, document.GetErrorOffset(), &line, &column);
std::ostringstream stream;
stream << "at " << line << ":" << column << ": " << GetParseError_En(document.GetParseError());
return stream.str();
}
Workload Workload::Load(const std::string& path) {
std::string file_data;
FX_CHECK(files::ReadFileToString(path, &file_data))
<< "Failed to read workload config file \"" << path << "\"!";
rapidjson::Document document;
const auto kFlags = rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag;
document.Parse<kFlags>(file_data);
FX_CHECK(!document.HasParseError()) << "Error parsing workload config file \"" << path << "\" "
<< GetErrorMessage(document, file_data) << "!";
FX_CHECK(document.IsObject()) << "Document must be a JSON object!";
Workload workload;
// Handle workload name.
if (document.HasMember("name")) {
workload.name_ = GetString("name", document, "Workload");
}
// Handle global config.
if (document.HasMember("config")) {
const auto& config = GetObject("config", document, "Workload");
if (config.HasMember("priority")) {
workload.priority_ = GetInt("priority", config, "Workload config");
}
if (config.HasMember("interval")) {
const auto& interval = config["interval"];
auto [duration_ns] = workload.ParseDuration(interval);
workload.interval_ = duration_ns;
}
}
// Handle named intervals.
if (document.HasMember("intervals")) {
const auto& intervals = GetObject("intervals", document, "Workload");
for (const auto& interval : IterateMembers(intervals)) {
workload.ParseNamedInterval(interval.name.GetString(), interval.value);
}
}
// Handle global objects.
if (document.HasMember("objects")) {
const auto& objects = GetObject("objects", document, "Workload");
for (const auto& object : IterateMembers(objects)) {
workload.ParseObject(object.name.GetString(), object.value);
}
}
// Handle named actions.
if (document.HasMember("behaviors")) {
const auto& behaviors = GetObject("behaviors", document, "Workload");
for (const auto& behavior : IterateMembers(behaviors)) {
workload.ParseNamedBehavior(behavior.name.GetString(), behavior.value);
}
}
// Handle workers.
if (document.HasMember("workers")) {
const auto& workers = GetArray("workers", document, "Workload");
for (const auto& worker : IterateValues(workers)) {
workload.ParseWorker(worker);
}
}
// Handle tracing.
if (document.HasMember("tracing")) {
const auto& tracing = GetObject("tracing", document, "Workload");
workload.ParseTracing(tracing);
}
return workload;
}