blob: 1cde501fcd9b380f1d4aa3733511e2c4e3759160 [file] [log] [blame]
// Copyright 2020 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 "args.h"
#include <lib/cmdline/args_parser.h>
#include <lib/fitx/result.h>
#include <stdlib.h>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "src/lib/fxl/strings/string_printf.h"
namespace hwstress {
namespace {
std::unique_ptr<cmdline::ArgsParser<CommandLineArgs>> GetParser() {
auto parser = std::make_unique<cmdline::ArgsParser<CommandLineArgs>>();
parser->AddSwitch("cleanup-test-partitions", 'c', "Cleanup all existing flash test partitions.",
&CommandLineArgs::destroy_partitions);
parser->AddSwitch("duration", 'd', "Test duration in seconds.",
&CommandLineArgs::test_duration_seconds);
parser->AddSwitch("fvm-path", 'f', "Path to Fuchsia Volume Manager.", &CommandLineArgs::fvm_path);
parser->AddSwitch("help", 'h', "Show this help.", &CommandLineArgs::help);
parser->AddSwitch("verbose", 'v', "Show verbose logging.", &CommandLineArgs::verbose);
parser->AddSwitch("memory", 'm', "Amount of memory to test in megabytes.",
&CommandLineArgs::mem_to_test_megabytes);
parser->AddSwitch("percent-memory", 0, "Percent of memory to test.",
&CommandLineArgs::ram_to_test_percent);
parser->AddSwitch("utilization", 'u', "Target CPU utilization percent.",
&CommandLineArgs::utilization_percent);
return parser;
}
} // namespace
std::istream& operator>>(std::istream& is, OptionalInt64& result) {
is >> result.emplace();
return is;
}
void PrintUsage() {
puts(
R"(usage:
hwstress <subcommand> [options]
Attempts to stress hardware components by placing them under high load.
Subcommands:
cpu Perform a CPU stress test.
flash Perform a flash stress test.
light Perform a device light / LED stress test.
memory Perform a RAM stress test.
Global options:
-d, --duration=<secs> Test duration in seconds. A value of "0" (the default)
indicates to continue testing until stopped.
-v, --verbose Show additional logging.
-h, --help Show this help.
CPU test options:
-u, --utilization=<percent>
Percent of system CPU to use. A value of
100 (the default) indicates that all the
CPU should be used, while 50 would indicate
to use 50% of CPU. Must be strictly greater
than 0, and no more than 100.
Flash test options:
-c, --cleanup-test-partitions
Cleanup all existing flash test partitions in the
system, and then exit without testing. Can be used
to clean up persistent test partitions left over from
previous flash tests which did not exit cleanly.
-f, --fvm-path=<path> Path to Fuchsia Volume Manager.
-m, --memory=<size> Amount of flash memory to test, in megabytes.
Memory test options:
-m, --memory=<size> Amount of RAM to test, in megabytes.
--percent-memory=<percent>
Percentage of total system RAM to test.
)");
}
fitx::result<std::string, CommandLineArgs> ParseArgs(fbl::Span<const char* const> args) {
CommandLineArgs result;
StressTest subcommand;
// Ensure a subcommand was provided.
if (args.size() < 2) {
return fitx::error("A subcommand specifying what type of test to run must be specified.");
}
std::string_view first_arg(args.data()[1]);
// If "--help" or "-h" was provided, don't try parsing anything else.
if (first_arg == "-h" || first_arg == "--help") {
result.help = true;
return fitx::success(result);
}
// Parse the subcommand.
if (first_arg == std::string_view("cpu")) {
subcommand = StressTest::kCpu;
} else if (first_arg == std::string_view("flash")) {
subcommand = StressTest::kFlash;
} else if (first_arg == std::string_view("memory")) {
subcommand = StressTest::kMemory;
} else if (first_arg == std::string_view("light")) {
subcommand = StressTest::kLight;
} else {
return fitx::error(
fxl::StringPrintf("Unknown subcommand or option: '%s'.", std::string(first_arg).data())
.c_str());
}
fbl::Span other_args = args.subspan(1); // Strip first element.
std::unique_ptr<cmdline::ArgsParser<CommandLineArgs>> parser = GetParser();
std::vector<std::string> params;
cmdline::Status status = parser->Parse(other_args.size(), other_args.data(), &result, &params);
if (!status.ok()) {
return fitx::error(status.error_message().c_str());
}
// If help is provided, ignore any further invalid args and just show the
// help screen.
if (result.help) {
return fitx::success(result);
}
result.subcommand = subcommand;
// Validate duration.
if (result.test_duration_seconds < 0) {
return fitx::error("Test duration cannot be negative.");
}
// Validate memory flags.
if (result.ram_to_test_percent.has_value()) {
if (result.ram_to_test_percent.value() <= 0 || result.ram_to_test_percent.value() >= 100) {
return fitx::error("Percent of RAM to test must be between 1 and 99, inclusive.");
}
}
if (result.mem_to_test_megabytes.has_value()) {
if (result.mem_to_test_megabytes.value() <= 0) {
return fitx::error("RAM to test must be strictly positive.");
}
}
if (result.mem_to_test_megabytes.has_value() && result.ram_to_test_percent.has_value()) {
return fitx::error("--memory and --percent-memory cannot both be specified.");
}
// Validate utilization.
if (result.utilization_percent <= 0.0 || result.utilization_percent > 100.0) {
return fitx::error("--utilization must be greater than 0%%, and no more than 100%%.");
}
// Ensure mandatory flash test argument is provided
if (result.subcommand == StressTest::kFlash) {
if (result.destroy_partitions && !result.fvm_path.empty()) {
return fitx::error(fxl::StringPrintf("Path to Fuchsia Volume Manager invalid with cleanup"));
} else if (!result.destroy_partitions && result.fvm_path.empty()) {
return fitx::error(fxl::StringPrintf("Path to Fuchsia Volume Manager must be specified"));
}
}
// Ensure no more parameters were given.
if (!params.empty()) {
return fitx::error(fxl::StringPrintf("Unknown option: '%s'.", params[1].c_str()).c_str());
}
return fitx::success(result);
}
} // namespace hwstress