diff --git a/wperf-lib/wperf-lib.vcxproj b/wperf-lib/wperf-lib.vcxproj
index dff9fdc..f682ac2 100644
--- a/wperf-lib/wperf-lib.vcxproj
+++ b/wperf-lib/wperf-lib.vcxproj
@@ -161,7 +161,7 @@
true
- $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj
+ $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj;arg_parser.obj;arg_parser_arg.obj
$(SolutionDir)wperf\$(IntDir)
@@ -181,7 +181,7 @@
true
- $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj
+ $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj;arg_parser.obj;arg_parser_arg.obj
$(SolutionDir)wperf\$(IntDir)
@@ -202,7 +202,7 @@
true
- $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj
+ $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj;arg_parser.obj;arg_parser_arg.obj
$(SolutionDir)wperf\$(IntDir)
@@ -223,7 +223,7 @@
true
- $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj
+ $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj;arg_parser.obj;arg_parser_arg.obj
$(SolutionDir)wperf\$(IntDir)
@@ -247,7 +247,7 @@
true
true
true
- $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj
+ $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj;arg_parser.obj;arg_parser_arg.obj
$(SolutionDir)wperf\$(IntDir)
@@ -272,7 +272,7 @@
true
true
true
- $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj
+ $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj;arg_parser.obj;arg_parser_arg.obj
$(SolutionDir)wperf\$(IntDir)
@@ -293,7 +293,7 @@
true
- $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj
+ $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj;arg_parser.obj;arg_parser_arg.obj
$(SolutionDir)wperf\$(IntDir)
@@ -314,7 +314,7 @@
true
- $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj
+ $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj;arg_parser.obj;arg_parser_arg.obj
$(SolutionDir)wperf\$(IntDir)
@@ -339,7 +339,7 @@
true
true
true
- $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj
+ $(CoreLibraryDependencies);%(AdditionalDependencies);events.obj;output.obj;padding.obj;parsers.obj;pe_file.obj;pmu_device.obj;spe_device.obj;process_api.obj;user_request.obj;utils.obj;wperf.obj;metric.obj;config.obj;timeline.obj;perfdata.obj;arg_parser.obj;arg_parser_arg.obj
$(SolutionDir)wperf\$(IntDir)
diff --git a/wperf-test/wperf-test-arg_parser.cpp b/wperf-test/wperf-test-arg_parser.cpp
index d4cebad..1dfcfcc 100644
--- a/wperf-test/wperf-test-arg_parser.cpp
+++ b/wperf-test/wperf-test-arg_parser.cpp
@@ -32,6 +32,7 @@
#include "CppUnitTest.h"
#include
#include "wperf/arg_parser.h"
+#include "wperf/exception.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace ArgParser;
@@ -61,7 +62,7 @@ namespace wperftest
const wchar_t* argv[] = { L"wperf", L"test", L"-v", L"--json", L"random" };
const int argc = _countof(argv);
arg_parser parser;
- Assert::ExpectException([&parser, argc, &argv]() {
+ Assert::ExpectException([&parser, argc, &argv]() {
parser.parse(argc, argv);
}
);
@@ -117,7 +118,7 @@ namespace wperftest
const wchar_t* argv[] = { L"wperf", L"sample", L"--timeout" };
const int argc = _countof(argv);
arg_parser parser;
- Assert::ExpectException([&parser, argc, &argv]() {
+ Assert::ExpectException([&parser, argc, &argv]() {
parser.parse(argc, argv);
}
);
@@ -129,7 +130,7 @@ namespace wperftest
const wchar_t* argv[] = { L"wperf", L"invalid_command" };
const int argc = _countof(argv);
arg_parser parser;
- Assert::ExpectException([&parser, argc, &argv]() {
+ Assert::ExpectException([&parser, argc, &argv]() {
parser.parse(argc, argv);
}
);
@@ -150,7 +151,7 @@ namespace wperftest
const wchar_t* argv[] = { L"wperf", L"sample", L"--timeout", L"5.4", L"ms" };
const int argc = _countof(argv);
arg_parser parser;
- Assert::ExpectException([&parser, argc, &argv]() {
+ Assert::ExpectException([&parser, argc, &argv]() {
parser.parse(argc, argv);
}
);
@@ -266,7 +267,7 @@ namespace wperftest
const wchar_t* argv[] = { L"wperf", L"sample", L"--unknown" };
const int argc = _countof(argv);
arg_parser parser;
- Assert::ExpectException([&parser, argc, &argv]() {
+ Assert::ExpectException([&parser, argc, &argv]() {
parser.parse(argc, argv);
}
);
@@ -278,7 +279,7 @@ namespace wperftest
const wchar_t* argv[] = { L"wperf", L"--annotate", L"--json" };
const int argc = _countof(argv);
arg_parser parser;
- Assert::ExpectException([&parser, argc, &argv]() {
+ Assert::ExpectException([&parser, argc, &argv]() {
parser.parse(argc, argv);
}
);
@@ -309,7 +310,7 @@ namespace wperftest
Assert::IsFalse(parser.version_command.is_set());
Assert::IsFalse(parser.detect_command.is_set());
Assert::IsFalse(parser.sample_command.is_set());
- Assert::IsTrue(parser.count_command.is_set());
+ Assert::IsTrue(parser.stat_command.is_set());
Assert::IsFalse(parser.man_command.is_set());
// Check all _arg flags setup
@@ -358,7 +359,7 @@ namespace wperftest
Assert::IsFalse(parser.version_command.is_set());
Assert::IsFalse(parser.detect_command.is_set());
Assert::IsFalse(parser.sample_command.is_set());
- Assert::IsFalse(parser.count_command.is_set());
+ Assert::IsFalse(parser.stat_command.is_set());
Assert::IsFalse(parser.man_command.is_set());
// Check all _arg flags setup
@@ -416,7 +417,7 @@ namespace wperftest
Assert::IsFalse(parser.version_command.is_set());
Assert::IsFalse(parser.detect_command.is_set());
Assert::IsFalse(parser.sample_command.is_set());
- Assert::IsFalse(parser.count_command.is_set());
+ Assert::IsFalse(parser.stat_command.is_set());
Assert::IsFalse(parser.man_command.is_set());
// Check all _opt flags setup
diff --git a/wperf-test/wperf-test-arg_parser_arg-utils.cpp b/wperf-test/wperf-test-arg_parser_arg-utils.cpp
index 26790c0..54bcf9d 100644
--- a/wperf-test/wperf-test-arg_parser_arg-utils.cpp
+++ b/wperf-test/wperf-test-arg_parser_arg-utils.cpp
@@ -69,21 +69,21 @@ namespace wperftest
{
std::wstring input = L"Line one.\nLine two is a bit longer.\nShort.";
size_t max_width = 15;
- std::wstring expected = L"Line one.\n\nLine two is a\nbit longer.\n\nShort.";
+ std::wstring expected = L"Line one.\nLine two is a\nbit longer.\nShort.";
Assert::AreEqual(expected, arg_parser_format_string_to_length(input, max_width));
}
TEST_METHOD(TestTrailingNewlineRemoval)
{
std::wstring input = L"Line one.\nLine two is a bit longer.\nShort.\n";
size_t max_width = 15;
- std::wstring expected = L"Line one.\n\nLine two is a\nbit longer.\n\nShort.";
+ std::wstring expected = L"Line one.\nLine two is a\nbit longer.\nShort.";
Assert::AreEqual(expected, arg_parser_format_string_to_length(input, max_width));
}
TEST_METHOD(TestMultipleLinesAndMultipleTrailingReturnToLines)
{
std::wstring input = L"Line one.\nLine two is a bit longer.\nShort.\n\n\n\n\n\n\n";
size_t max_width = 15;
- std::wstring expected = L"Line one.\n\nLine two is a\nbit longer.\n\nShort.\n\n\n\n\n\n";
+ std::wstring expected = L"Line one.\nLine two is a\nbit longer.\nShort.\n\n\n\n\n\n";
Assert::AreEqual(expected, arg_parser_format_string_to_length(input, max_width));
}
TEST_METHOD(MultipleRetrunToLines)
diff --git a/wperf/arg_parser.cpp b/wperf/arg_parser.cpp
index f512c59..dd7eb3f 100644
--- a/wperf/arg_parser.cpp
+++ b/wperf/arg_parser.cpp
@@ -28,13 +28,15 @@
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#include "arg_parser.h"
#include
#include
#include
#include
#include
#include
+#include "arg_parser.h"
+#include "output.h"
+#include "exception.h"
namespace ArgParser {
arg_parser::arg_parser() {}
@@ -82,6 +84,7 @@ namespace ArgParser {
{
if (current_flag->parse(raw_args)) {
raw_args.erase(raw_args.begin(), raw_args.begin() + current_flag->get_arg_count() + 1);
+ parsed_args.push_back(current_flag);
}
}
catch (const std::exception& err)
@@ -101,26 +104,25 @@ namespace ArgParser {
void arg_parser::print_help() const
{
- std::wcout << L"NAME:\n"
-
+ m_out.GetOutputStream() << L"NAME:\n"
<< L"\twperf - Performance analysis tools for Windows on Arm\n\n"
<< L"\tUsage: wperf [options]\n\n"
<< L"SYNOPSIS:\n\n";
for (auto& command : m_commands_list)
{
- std::wcout << L"\t" << command->get_all_flags_string() << L"\n" << command->get_usage_text() << L"\n";
+ m_out.GetOutputStream() << L"\t" << command->get_all_flags_string() << L"\n" << command->get_usage_text() << L"\n";
}
- std::wcout << L"OPTIONS:\n\n";
+ m_out.GetOutputStream() << L"OPTIONS:\n\n";
for (auto& flag : m_flags_list)
{
- std::wcout << L" " << flag->get_help() << L"\n";
+ m_out.GetOutputStream() << flag->get_help() << L"\n\n";
}
- std::wcout << L"EXAMPLES:\n\n";
+ m_out.GetOutputStream() << L"EXAMPLES:\n\n";
for (auto& command : m_commands_list)
{
if (command->get_examples().empty()) continue;
- std::wcout << L" " << command->get_examples() << L"\n";
+ m_out.GetOutputStream() << command->get_examples() << L"\n\n";
}
}
@@ -143,11 +145,6 @@ namespace ArgParser {
std::wstring indicator(pos, L'~');
indicator += L'^';
- /*
- TODO: THIS function should change to use GetErrorOutputStream before migrating to wperf
-
- */
-
std::wostringstream error_message;
error_message << L"Invalid argument detected:\n"
<< command << L"\n"
@@ -155,8 +152,8 @@ namespace ArgParser {
if (!additional_message.empty()) {
error_message << additional_message << L"\n";
}
- std::wcerr << error_message.str();
- throw std::invalid_argument("INVALID_ARGUMENT");
+ m_out.GetErrorOutputStream() << error_message.str();
+ throw fatal_exception("INVALID_ARGUMENT");
}
#pragma endregion
@@ -171,7 +168,7 @@ namespace ArgParser {
{
std::wstring example_output;
for (auto& example : m_examples) {
- example_output += example + L"\n";
+ example_output += example + L"\n\n";
}
return arg_parser_add_wstring_behind_multiline_text(arg_parser_format_string_to_length(example_output), L"\t");
}
diff --git a/wperf/arg_parser.h b/wperf/arg_parser.h
index 1e8d4e5..407aa9e 100644
--- a/wperf/arg_parser.h
+++ b/wperf/arg_parser.h
@@ -58,7 +58,7 @@ namespace ArgParser {
MAN,
NO_COMMAND
};
- class arg_parser_arg_command : public arg_parser_arg_opt {
+ class arg_parser_arg_command : public arg_parser_arg_pos {
public:
arg_parser_arg_command(
@@ -67,8 +67,9 @@ namespace ArgParser {
const std::wstring description,
const std::wstring useage_text,
const COMMAND_CLASS command,
- const wstr_vec examples
- ) : arg_parser_arg_opt(name, alias, description), m_examples(examples), m_command(command), m_useage_text(useage_text) {};
+ const wstr_vec examples,
+ const int arg_count = 0
+ ) : arg_parser_arg_pos(name, alias, description, {}, arg_count), m_examples(examples), m_command(command), m_useage_text(useage_text) {};
const COMMAND_CLASS m_command = COMMAND_CLASS::NO_COMMAND;
const wstr_vec m_examples;
const std::wstring m_useage_text;
@@ -103,7 +104,7 @@ namespace ArgParser {
L"wperf list [-v] [--json] [--force-lock]",
COMMAND_CLASS::LIST,
{
- L"> wperf list -v List all events and metrics available on your host with extended information."
+ L"> wperf list -v \nList all events and metrics available on your host with extended information."
}
);
arg_parser_arg_command test_command = arg_parser_arg_command::arg_parser_arg_command(
@@ -141,36 +142,36 @@ namespace ArgParser {
arg_parser_arg_command sample_command = arg_parser_arg_command::arg_parser_arg_command(
L"sample",
{ L"" },
- L"Sampling mode, for determining the frequencies of event occurrences produced by program locations at the function, basic block, and /or instruction levels.",
+ L"Sampling mode, for determining the frequencies of event occurrences produced by program locations at the function, basic block, and/or instruction levels.",
L"wperf sample [-e] [--timeout] [-c] [-C] [-E] [-q] [--json] [--output] [--config] [--image_name] [--pe_file] [--pdb_file] [--sample-display-long] [--force-lock] [--sample-display-row] [--symbol] [--record_spawn_delay] [--annotate] [--disassemble]",
COMMAND_CLASS::SAMPLE,
{
- L"> wperf sample -e ld_spec:100000 --pe_file python_d.exe -c 1 Sample event `ld_spec` with frequency `100000` already running process `python_d.exe` on core #1. Press Ctrl + C to stop sampling and see the results.",
+ L"> wperf sample -e ld_spec:100000 --pe_file python_d.exe -c 1 \nSample event `ld_spec` with frequency `100000` already running process `python_d.exe` on core #1. Press Ctrl + C to stop sampling and see the results.",
}
);
arg_parser_arg_command record_command = arg_parser_arg_command::arg_parser_arg_command(
L"record",
{ L"" },
- L"Same as sample but also automatically spawns the process and pins it to the core specified by `-c`. Process name is defined by COMMAND.User can pass verbatim arguments to the process with[ARGS].",
+ L"Same as sample but also automatically spawns the process and pins it to the core specified by `-c`. Process name is defined by COMMAND. User can pass verbatim arguments to the process with[ARGS].",
L"wperf record [-e] [--timeout] [-c] [-C] [-E] [-q] [--json] [--output] [--config] [--image_name] [--pe_file] [--pdb_file] [--sample-display-long] [--force-lock] [--sample-display-row] [--symbol] [--record_spawn_delay] [--annotate] [--disassemble] --COMMAND[ARGS]",
COMMAND_CLASS::RECORD,
{
- L"> wperf record -e ld_spec:100000 -c 1 --timeout 30 -- python_d.exe -c 10**10**100 Launch `python_d.exe - c 10 * *10 * *100` process and start sampling event `ld_spec` with frequency `100000` on core #1 for 30 seconds. Hint: add `--annotate` or `--disassemble` to `wperf record` command line parameters to increase sampling \"resolution\"."
+ L"> wperf record -e ld_spec:100000 -c 1 --timeout 30 -- python_d.exe -c 10**10**100 \nLaunch `python_d.exe - c 10 * *10 * *100` process and start sampling event `ld_spec` with frequency `100000` on core #1 for 30 seconds. \nHint: add `--annotate` or `--disassemble` to `wperf record` command line parameters to increase sampling \"resolution\"."
#ifdef ENABLE_SPE
- ,L"> wperf record -e arm_spe_0/ld=1/ -c 8 --cpython\\PCbuild\\arm64\\python_d.exe -c 10**10**100 Launch `python_d.exe -c 10**10**100` process on core no. 8 and start SPE sampling, enable collection of load sampled operations, including atomic operations that return a value to a register. Hint: add `--annotate` or `--disassemble` to `wperf record` command."
+ ,L"> wperf record -e arm_spe_0/ld=1/ -c 8 -- python_d.exe -c 10**10**100 \nLaunch `python_d.exe -c 10**10**100` process on core no. 8 and start SPE sampling, enable collection of load sampled operations, including atomic operations that return a value to a register. Hint: add `--annotate` or `--disassemble` to `wperf record` command."
#endif
}
);
- arg_parser_arg_command count_command = arg_parser_arg_command::arg_parser_arg_command(
+ arg_parser_arg_command stat_command = arg_parser_arg_command::arg_parser_arg_command(
L"stat",
{ L"" },
L"Counting mode, for obtaining aggregate counts of occurrences of special events.",
- L"wperf stat [-e] [-m] [-t] [-i] [-n] [-c] [-C] [-E] [-k] [--dmc] [-q] [--json] [--output][--config] [--force-lock] --COMMAND[ARGS]",
+ L"wperf stat [-e] [-m] [-t] [-i] [-n] [-c] [-C] [-E] [-k] [--dmc] [-q] [--json] [--output][--config] [--force-lock] --COMMAND [ARGS]",
COMMAND_CLASS::STAT,
{
- L"> wperf stat -e inst_spec,vfp_spec,ase_spec,ld_spec -c 0 --timeout 3 Count events `inst_spec`, `vfp_spec`, `ase_spec` and `ld_spec` on core #0 for 3 seconds.",
- L"> wperf stat -m imix -e l1i_cache -c 7 --timeout 10.5 Count metric `imix` (metric events will be grouped) and additional event `l1i_cache` on core #7 for 10.5 seconds.",
- L"> wperf stat -m imix -c 1 -t -i 2 -n 3 --timeout 5 Count in timeline mode(output counting to CSV file) metric `imix` 3 times on core #1 with 2 second intervals(delays between counts).Each count will last 5 seconds."
+ L"> wperf stat -e inst_spec,vfp_spec,ase_spec,ld_spec -c 0 --timeout 3 \nCount events `inst_spec`, `vfp_spec`, `ase_spec` and `ld_spec` on core #0 for 3 seconds.",
+ L"> wperf stat -m imix -e l1i_cache -c 7 --timeout 10.5 \nCount metric `imix` (metric events will be grouped) and additional event `l1i_cache` on core #7 for 10.5 seconds.",
+ L"> wperf stat -m imix -c 1 -t -i 2 -n 3 --timeout 5 \nCount in timeline mode(output counting to CSV file) metric `imix` 3 times on core #1 with 2 second intervals(delays between counts). Each count will last 5 seconds."
}
);
arg_parser_arg_command man_command = arg_parser_arg_command::arg_parser_arg_command(
@@ -180,7 +181,8 @@ namespace ArgParser {
L"wperf man [--json]",
COMMAND_CLASS::MAN,
{
- }
+ },
+ 1
);
#pragma endregion
@@ -241,7 +243,12 @@ namespace ArgParser {
L"Enable timeline mode (count multiple times with specified interval). Use `-i` to specify timeline interval, and `-n` to specify number of counts.",
{}
);
-
+ arg_parser_arg_opt export_perf_data_opt = arg_parser_arg_opt::arg_parser_arg_opt(
+ L"--export_perf_data",
+ {},
+ L"Generate local `perf.data` file with partial profiling information (experimental only).",
+ {}
+ );
#pragma endregion
@@ -249,7 +256,7 @@ namespace ArgParser {
arg_parser_arg_pos extra_args_arg = arg_parser_arg_pos::arg_parser_arg_pos(
L"--",
{},
- L"-- Process name is defined by COMMAND. User can pass verbatim arguments to the process with[ARGS].",
+ L"Double-dash is a syntax used signify end of command options. It separates `wperf` command line options from arguments that the process spawn command operates on. Use `--` to separate `wperf.exe` command line options from the process you want to spawn followed by its verbatim arguments.",
{},
-1
);
@@ -346,7 +353,7 @@ namespace ArgParser {
{}
);
arg_parser_arg_pos iteration_arg = arg_parser_arg_pos::arg_parser_arg_pos(
- L"-i",
+ L"-n",
{},
L"Number of consecutive counts in timeline mode (disabled by default).",
{}
@@ -360,13 +367,13 @@ namespace ArgParser {
arg_parser_arg_pos metrics_arg = arg_parser_arg_pos::arg_parser_arg_pos(
L"-m",
{},
- L"Specify comma separated list of metrics to count.\n\nNote: see list of available metric names using `list` command.",
+ L"Specify comma separated list of metrics to count.\nNote: see list of available metric names using `list` command.",
{}
);
arg_parser_arg_pos events_arg = arg_parser_arg_pos::arg_parser_arg_pos(
L"-e",
{},
- L"Specify comma separated list of event names (or raw events) to count, for example `ld_spec,vfp_spec,r10`. Use curly braces to group events. Specify comma separated list of event names with sampling frequency to sample, for example `ld_spec:100000`. Raw events: specify raw evens with `r` where `` is a 16-bit hexadecimal event index value without leading `0x`. For example `r10` is event with index `0x10`. Note: see list of available event names using `list` command.",
+ L"Specify comma separated list of event names (or raw events) to count, for example `ld_spec,vfp_spec,r10`. Use curly braces to group events. \nSpecify comma separated list of event names with sampling frequency to sample, for example `ld_spec:100000`. \nRaw events: specify raw evens with `r` where `` is a 16-bit hexadecimal event index value without leading `0x`. For example `r10` is event with index `0x10`. \nNote: see list of available event names using `list` command.",
{}
);
#pragma endregion
@@ -378,7 +385,7 @@ namespace ArgParser {
&help_command,
&version_command,
&sample_command,
- &count_command,
+ &stat_command,
&record_command,
&list_command,
&test_command,
@@ -415,9 +422,10 @@ namespace ArgParser {
&interval_arg,
&iteration_arg,
&dmc_arg,
+ &export_perf_data_opt,
&extra_args_arg
};
-
+ std::vector parsed_args = {};
wstr_vec m_arg_array;
#pragma endregion
diff --git a/wperf/arg_parser_arg.cpp b/wperf/arg_parser_arg.cpp
index 8754e51..929cbd0 100644
--- a/wperf/arg_parser_arg.cpp
+++ b/wperf/arg_parser_arg.cpp
@@ -182,9 +182,6 @@ namespace ArgParserArg {
formatted_str += L"\n";
continue;
}
- if (!formatted_str.empty()) {
- formatted_str += L"\n";
- }
std::wstring current_line;
std::wistringstream word_stream(line);
std::wstring word;
diff --git a/wperf/main.cpp b/wperf/main.cpp
index f4fe611..8bd1b81 100644
--- a/wperf/main.cpp
+++ b/wperf/main.cpp
@@ -50,6 +50,7 @@
#include "config.h"
#include "perfdata.h"
#include "disassembler.h"
+#include "arg_parser.h"
static bool no_ctrl_c = true;
@@ -81,10 +82,12 @@ wmain(
)
{
auto exit_code = EXIT_SUCCESS;
+ ArgParser::arg_parser arg_parser;
+ arg_parser.parse(argc, argv);
+
user_request request;
pmu_device pmu_device;
- wstr_vec raw_args;
LLVMDisassembler disassembler;
bool spawned_process = false;
@@ -92,24 +95,14 @@ wmain(
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
- //* Handle CLI options before we initialize PMU device(s)
- for (int i = 1; i < argc; i++)
- raw_args.push_back(argv[i]);
-
- pmu_device.do_force_lock = user_request::is_force_lock(raw_args);
-
- if (raw_args.size() == 1 && user_request::is_help(raw_args))
+ if (arg_parser.m_command == ArgParser::COMMAND_CLASS::HELP)
{
- user_request::print_help();
+ arg_parser.print_help();
goto clean_exit;
}
- if (raw_args.empty())
- {
- user_request::print_help_header();
- user_request::print_help_prompt();
- goto clean_exit;
- }
+ pmu_device.do_force_lock = arg_parser.force_lock_opt.is_set();
+
//* Handle CLI options before we initialize PMU device(s)
try {
@@ -162,7 +155,7 @@ wmain(
{
struct pmu_device_cfg pmu_cfg;
pmu_device.get_pmu_device_cfg(pmu_cfg);
- request.init(raw_args, pmu_cfg,
+ request.init(arg_parser, pmu_cfg,
pmu_device.builtin_metrics,
pmu_device.get_product_groups_metrics_names(),
pmu_events::extra_events);
@@ -183,11 +176,6 @@ wmain(
goto clean_exit;
}
- if (request.do_help)
- {
- user_request::print_help();
- goto clean_exit;
- }
if (request.do_man)
{
std::vector col1, col2;
@@ -1340,7 +1328,6 @@ wmain(
#if defined(ENABLE_ETW_TRACING_APP)
EventUnregisterWindowsPerf_App();
#endif
-
if(spawned_process)
{
TerminateProcess(pi.hProcess, 0);
diff --git a/wperf/user_request.cpp b/wperf/user_request.cpp
index 1667935..ab8c5b3 100644
--- a/wperf/user_request.cpp
+++ b/wperf/user_request.cpp
@@ -42,255 +42,11 @@
#include "wperf-common/public.h"
#include "wperf/config.h"
-void user_request::print_help_usage()
-{
- std::wstring wsHelp = LR"(
-NAME:
- wperf - Performance analysis tools for Windows on Arm
-
-SYNOPSIS:
- wperf [--version] [--help] [OPTIONS]
-
- wperf stat [-e] [-m] [-t] [-i] [-n] [-c] [-C] [-E] [-k] [--dmc] [-q] [--json]
- [--output] [--config] [--force-lock]
- wperf stat [-e] [-m] [-t] [-i] [-n] [-c] [-C] [-E] [-k] [--dmc] [-q] [--json]
- [--output] [--config] -- COMMAND [ARGS]
- Counting mode, for obtaining aggregate counts of occurrences of special
- events.
-
- wperf sample [-e] [--timeout] [-c] [-C] [-E] [-q] [--json] [--output] [--config]
- [--image_name] [--pe_file] [--pdb_file] [--sample-display-long] [--force-lock]
- [--sample-display-row] [--symbol] [--record_spawn_delay] [--annotate] [--disassemble]
- Sampling mode, for determining the frequencies of event occurrences
- produced by program locations at the function, basic block, and/or
- instruction levels.
-
- wperf record [-e] [--timeout] [-c] [-C] [-E] [-q] [--json] [--output] [--config]
- [--image_name] [--pe_file] [--pdb_file] [--sample-display-long] [--force-lock]
- [--sample-display-row] [--symbol] [--record_spawn_delay] [--annotate] [--disassemble] -- COMMAND [ARGS]
- Same as sample but also automatically spawns the process and pins it to
- the core specified by `-c`. Process name is defined by COMMAND. User can
- pass verbatim arguments to the process with [ARGS].
-
- wperf list [-v] [--json] [--force-lock]
- List supported events and metrics. Enable verbose mode for more details.
-
- wperf test [--json] [OPTIONS]
- Configuration information about driver and application.
-
- wperf detect [--json] [OPTIONS]
- List installed WindowsPerf-like Kernel Drivers (match GUID).
-
- wperf man [--json]
- Plain text information about one or more specified event(s), metric(s), and or group metric(s).
-
-OPTIONS:
- -h, --help
- Run wperf help command.
-
- --version
- Display version.
-
- -v, --verbose
- Enable verbose output also in JSON output.
-
- -q
- Quiet mode, no output is produced.
-
- -e
- Specify comma separated list of event names (or raw events) to count, for
- example `ld_spec,vfp_spec,r10`. Use curly braces to group events.
- Specify comma separated list of event names with sampling frequency to
- sample, for example `ld_spec:100000`.
-
- Raw events: specify raw evens with `r` where `` is a 16-bit
- hexadecimal event index value without leading `0x`. For example `r10` is
- event with index `0x10`.
-
- Note: see list of available event names using `list` command.
-
- -m
- Specify comma separated list of metrics to count.
-
- Note: see list of available metric names using `list` command.
-
- --timeout
- Specify counting or sampling duration. If not specified, press
- Ctrl+C to interrupt counting or sampling. Input may be suffixed by
- one (or none) of the following units, with up to 2 decimal
- points: "ms", "s", "m", "h", "d" (i.e. milliseconds, seconds,
- minutes, hours, days). If no unit is provided, the default unit
- is seconds. Accuracy is 0.1 sec.
-
- -t
- Enable timeline mode (count multiple times with specified interval).
- Use `-i` to specify timeline interval, and `-n` to specify number of
- counts.
-
- -i
- Specify counting interval. `0` seconds is allowed. Input may be
- suffixed with one (or none) of the following units, with up to
- 2 decimal points: "ms", "s", "m", "h", "d" (i.e. milliseconds,
- seconds, minutes, hours, days). If no unit is provided, the default
- unit is seconds (60s by default).
-
- -n
- Number of consecutive counts in timeline mode (disabled by default).
-
- --annotate
- Enable translating addresses taken from samples in sample/record mode
- into source code line numbers.
-
- --disassemble
- Enable disassemble output on sampling mode. Implies 'annotate'.
-
- --image_name
- Specify the image (base) name of a module to sample.
-
- --pe_file
- Specify the PE filename (and path) to sample.
-
- --pdb_file
- Specify the PDB filename (and path), PDB file should directly
- corresponds to a PE file set with `--pe_file`.
-
- --sample-display-long
- Display decorated symbol names.
-
- --sample-display-row
- Set how many samples you want to see in the summary (50 by default).
-
- --symbol
- Filter results for specific symbols (for use with 'record' and 'sample' commands).
-
- --record_spawn_delay
- Set the waiting time, in milliseconds, before reading process data after
- spawning it with `record`.
-
- --force-lock
- Force driver to give lock to current `wperf` process, use when you want
- to interrupt currently executing `wperf` session or to recover from the lock.
-
- -c, --cpu
- Specify comma separated list of CPU cores, and or ranges of CPU cores, to count
- on, or one CPU to sample on.
-
- -k
- Count kernel mode as well (disabled by default).
-
- --dmc
- Profile on the specified DDR controller. Skip `--dmc` to count on all
- DMCs.
-
- -C
- Provide customized config file which describes metrics.
-
- -E
- Provide customized config file which describes custom events or
- provide custom events from the command line.
-
- --json
- Define output type as JSON.
-
- --output, -o
- Specify JSON output filename.
-
- --output-csv
- Specify CSV output filename. Only with timeline `-t`.
-
- --output-prefix, --cwd
- Set current working dir for storing output JSON and CSV file.
-
- --config
- Specify configuration parameters.
-
-OPTIONS aliases:
- -l
- Alias of 'list'.
-
- sleep
- Alias of `--timeout`.
- -s
- Alias of `--symbol`.
-
-EXAMPLES:
-
- > wperf list -v
- List all events and metrics available on your host with extended
- information.
-
- > wperf stat -e inst_spec,vfp_spec,ase_spec,ld_spec -c 0 --timeout 3
- Count events `inst_spec`, `vfp_spec`, `ase_spec` and `ld_spec` on core #0
- for 3 seconds.
-
- > wperf stat -m imix -e l1i_cache -c 7 --timeout 10.5
- Count metric `imix` (metric events will be grouped) and additional event
- `l1i_cache` on core #7 for 10.5 seconds.
-
- > wperf stat -m imix -c 1 -t -i 2 -n 3 --timeout 5
- Count in timeline mode (output counting to CSV file) metric `imix` 3 times
- on core #1 with 2 second intervals (delays between counts). Each count
- will last 5 seconds.
-
- > wperf sample -e ld_spec:100000 --pe_file python_d.exe -c 1
- Sample event `ld_spec` with frequency `100000` already running process
- `python_d.exe` on core #1. Press Ctrl+C to stop sampling and see the results.
-
- > wperf record -e ld_spec:100000 -c 1 --timeout 30 -- python_d.exe -c 10**10**100
- Launch `python_d.exe -c 10**10**100` process and start sampling event `ld_spec`
- with frequency `100000` on core #1 for 30 seconds.
- Hint: add `--annotate` or `--disassemble` to `wperf record` command line
- parameters to increase sampling "resolution".
-)";
-
-#ifdef ENABLE_SPE
- wsHelp += LR"(
- > wperf record -e arm_spe_0/ld=1/ -c 8 --cpython\PCbuild\arm64\python_d.exe -c 10**10**100
- Launch `python_d.exe -c 10**10**100` process on core no. 8 and start SPE sampling, enable
- collection of load sampled operations, including atomic operations that return a value to a register.
- Hint: add `--annotate` or `--disassemble` to `wperf record` command.
-)";
-#endif
-
- m_out.GetOutputStream() << wsHelp << std::endl;
-}
-
//
// This file will be modified with wperf's pre-build step
//
#include "wperf-common\gitver.h"
-void user_request::print_help_header()
-{
- m_out.GetOutputStream() << L"WindowsPerf"
- << L" ver. " << MAJOR << "." << MINOR << "." << PATCH
- << L" ("
- << WPERF_GIT_VER_STR
- << L"/"
-#ifdef _DEBUG
- << L"Debug"
-#else
- << L"Release"
-#endif
- << ENABLE_FEAT_STR
- << L") WOA profiling with performance counters."
- << std::endl;
-
- m_out.GetOutputStream() << L"Report bugs to: https://github.com/arm-developer-tools/windowsperf/issues"
- << std::endl;
-}
-
-void user_request::print_help_prompt()
-{
- m_out.GetOutputStream() << L"Use --help for help." << std::endl;
-}
-
-void user_request::print_help()
-{
- print_help_header();
- print_help_usage();
-}
-
user_request::user_request() : do_list{ false }, do_disassembly(false), do_count(false), do_kernel(false), do_timeline(false),
do_sample(false), do_record(false), do_annotate(false), do_version(false), do_verbose(false), do_test(false),
@@ -319,7 +75,7 @@ bool user_request::is_help(const wstr_vec& raw_args)
|| is_cli_option_in_args(raw_args, std::wstring(L"-h"));
}
-void user_request::init(wstr_vec& raw_args, const struct pmu_device_cfg& pmu_cfg,
+void user_request::init(ArgParser::arg_parser& parsed_args, const struct pmu_device_cfg& pmu_cfg,
std::map& builtin_metrics,
const std::map >& groups_of_metrics,
std::map>& extra_events)
@@ -332,7 +88,7 @@ void user_request::init(wstr_vec& raw_args, const struct pmu_device_cfg& pmu_cfg
cores_idx.resize(pmu_cfg.core_num);
std::iota(cores_idx.begin(), cores_idx.end(), (UINT8)0);
- parse_raw_args(raw_args, pmu_cfg, events, groups, builtin_metrics, groups_of_metrics, extra_events);
+ parse_raw_args(parsed_args, pmu_cfg, events, groups, builtin_metrics, groups_of_metrics, extra_events);
// Deduce image name and PDB file name from PE file name
if (sample_pe_file.size())
@@ -392,120 +148,232 @@ void user_request::init(wstr_vec& raw_args, const struct pmu_device_cfg& pmu_cfg
check_events(EVT_DMC_CLKDIV2, MAX_MANAGED_DMC_CLKDIV2_EVENTS);
}
-void user_request::parse_raw_args(wstr_vec& raw_args, const struct pmu_device_cfg& pmu_cfg,
+void user_request::parse_raw_args(ArgParser::arg_parser& parsed_args, const struct pmu_device_cfg& pmu_cfg,
std::map>& events,
std::map>& groups,
std::map& builtin_metrics,
const std::map >& groups_of_metrics,
std::map>& extra_events)
{
- bool waiting_events = false;
- bool waiting_metrics = false;
- bool waiting_core_idx = false;
- bool waiting_dmc_idx = false;
- bool waiting_duration = false;
- bool waiting_interval = false;
- bool waiting_metric_config = false;
- bool waiting_events_config = false;
- bool waiting_output_filename = false;
- bool waiting_output_csv_filename = false;
- bool waiting_image_name = false;
- bool waiting_pe_file = false;
- bool waiting_pdb_file = false;
- bool waiting_sample_display_row = false;
- bool waiting_timeline_count = false;
- bool waiting_config = false;
- bool waiting_commandline = false;
- bool waiting_record_spawn_delay = false;
- bool waiting_man_query = false;
- bool waiting_cwd = false;
- bool waiting_symbol = false;
+
bool sample_pe_file_given = false;
std::wstring waiting_duration_arg;
std::wstring output_filename, output_csv_filename;
- if (raw_args.empty())
+ switch (parsed_args.m_command)
{
- print_help_header();
- print_help_prompt();
+ case ArgParser::COMMAND_CLASS::STAT:
+ do_count = true;
+ break;
+ case ArgParser::COMMAND_CLASS::SAMPLE:
+ do_sample = true;
+ break;
+ case ArgParser::COMMAND_CLASS::RECORD:
+ do_record = true;
+ break;
+ case ArgParser::COMMAND_CLASS::TEST:
+ do_test = true;
+ break;
+ case ArgParser::COMMAND_CLASS::DETECT:
+ do_detect = true;
+ break;
+ case ArgParser::COMMAND_CLASS::HELP:
+ do_help = true;
+ break;
+ case ArgParser::COMMAND_CLASS::VERSION:
+ do_version = true;
+ break;
+ case ArgParser::COMMAND_CLASS::LIST:
+ do_list = true;
+ break;
+ case ArgParser::COMMAND_CLASS::MAN:
+ do_man = true;
+ break;
+ default:
+ break;
}
- for (const auto& a : raw_args)
+ if (parsed_args.metric_config_arg.is_set())
+ {
+ load_config_metrics(parsed_args.metric_config_arg.get_values().front(), pmu_cfg);
+ }
+ if (parsed_args.event_config_arg.is_set())
+ {
+ load_config_events(parsed_args.event_config_arg.get_values().front(), extra_events);
+ }
+
+ if (builtin_metrics.size())
{
- if (waiting_metric_config)
+ for (const auto& [key, value] : builtin_metrics)
{
- waiting_metric_config = false;
- load_config_metrics(a, pmu_cfg);
- continue;
+ if (metrics.find(key) == metrics.end())
+ metrics[key] = value;
}
+ }
+ if (parsed_args.extra_args_arg.is_set())
+ {
+ if (sample_pe_file.empty())
+ sample_pe_file = parsed_args.extra_args_arg.get_values().front();
+
+ record_commandline += WStringJoin(parsed_args.extra_args_arg.get_values(), L" ");
+ if (!(do_record || do_count))
+ m_out.GetErrorOutputStream() << L"warning: only `stat` and `record` support process spawn!" << std::endl;
+
+ sample_pe_file.clear();
+ record_commandline.clear();
+ }
+ if (parsed_args.output_prefix_arg.is_set())
+ {
+ m_cwd = parsed_args.output_prefix_arg.get_values().front();
+ }
+ if (parsed_args.output_filename_arg.is_set())
+ {
+ output_filename = parsed_args.output_filename_arg.get_values().front();
+ }
- if (a == L"-C")
- {
- waiting_metric_config = true;
- continue;
- }
+ if (parsed_args.output_csv_filename_arg.is_set())
+ {
+ output_csv_filename = parsed_args.output_csv_filename_arg.get_values().front();
+ }
- if (waiting_events_config)
- {
- waiting_events_config = false;
- load_config_events(a, extra_events);
- continue;
- }
+ if (parsed_args.config_arg.is_set())
+ {
+ wstring config_values = parsed_args.config_arg.get_values().front();
+ if (drvconfig::set(config_values) == false)
+ m_out.GetErrorOutputStream() << L"error: can't set '" << config_values << "' config" << std::endl;
+ }
+ if (parsed_args.image_name_arg.is_set())
+ {
+ sample_image_name = parsed_args.image_name_arg.get_values().front();
+ }
- if (a == L"-E")
+ if (parsed_args.pe_file_arg.is_set())
+ {
+ wstring parsed_pe_file = parsed_args.pe_file_arg.get_values().front();
+ if (std::filesystem::exists(parsed_pe_file) == false)
{
- waiting_events_config = true;
- continue;
+ m_out.GetErrorOutputStream() << L"PE file '" << parsed_pe_file << L"' doesn't exist"
+ << std::endl;
+ throw fatal_exception("ERROR_PE_FILE_PATH");
}
+ sample_pe_file = parsed_pe_file;
+ sample_pe_file_given = true;
}
-
- if (builtin_metrics.size())
+
+ if (parsed_args.pdb_file_arg.is_set())
{
- for (const auto& [key, value] : builtin_metrics)
+ wstring parsed_pdb_file = parsed_args.pdb_file_arg.get_values().front();
+ if (std::filesystem::exists(parsed_pdb_file) == false)
{
- if (metrics.find(key) == metrics.end())
- metrics[key] = value;
+ m_out.GetErrorOutputStream() << L"PDB file '" << parsed_pdb_file << L"' doesn't exist"
+ << std::endl;
+ throw fatal_exception("ERROR_PDB_FILE_PATH");
}
+ sample_pdb_file = parsed_pdb_file;
}
- for (const auto& a : raw_args)
+
+ if (parsed_args.cores_arg.is_set())
{
- if (waiting_commandline)
- {
- if (sample_pe_file.empty())
- {
- sample_pe_file = a;
- record_commandline = a;
- }
- else
- record_commandline += L" " + a;
- continue;
- }
+ wstring core_idx_str = WStringJoin(parsed_args.cores_arg.get_values(), L",");
- if (waiting_metric_config)
+ if (TokenizeWideStringOfInts(core_idx_str.c_str(), L',', cores_idx) == false)
{
- waiting_metric_config = false;
- continue;
+ m_out.GetErrorOutputStream() << L"option -c format not supported, use comma separated list of integers, or range of integers"
+ << std::endl;
+ throw fatal_exception("ERROR_CORES");
}
- if (waiting_events_config)
+ if (cores_idx.size() > MAX_PMU_CTL_CORES_COUNT)
{
- waiting_events_config = false;
- continue;
+ m_out.GetErrorOutputStream() << L"you can specify up to " << int(MAX_PMU_CTL_CORES_COUNT)
+ << L"cores with -c option"
+ << std::endl;
+ throw fatal_exception("ERROR_CORES");
}
+ }
- if (waiting_events)
+ if (parsed_args.iteration_arg.is_set())
+ {
+ count_timeline = _wtoi(parsed_args.iteration_arg.get_values().front().c_str());
+ }
+
+ if (parsed_args.record_spawn_delay_arg.is_set())
+ {
+ uint32_t val = _wtoi(parsed_args.record_spawn_delay_arg.get_values().front().c_str());
+ assert(val <= UINT32_MAX);
+ record_spawn_delay = val;
+ }
+
+ if (parsed_args.dmc_arg.is_set())
+ {
+
+ int val = _wtoi(parsed_args.output_filename_arg.get_values().front().c_str());
+ assert(val <= UCHAR_MAX);
+ dmc_idx = (uint8_t)val;
+ }
+
+ if (parsed_args.timeout_arg.is_set())
+ {
+ count_duration = convert_timeout_arg_to_seconds(parsed_args.output_filename_arg.get_values().front(), parsed_args.timeout_arg.get_name());
+ }
+
+ if (parsed_args.interval_arg.is_set())
+ {
+ count_interval = convert_timeout_arg_to_seconds(parsed_args.interval_arg.get_values().front(), parsed_args.interval_arg.get_name());
+ }
+
+ if (parsed_args.sample_display_row_arg.is_set())
+ {
+ sample_display_row = _wtoi(parsed_args.sample_display_row_arg.get_values().front().c_str());
+ }
+
+ if (parsed_args.man_command.is_set())
+ {
+ man_query_args = parsed_args.man_command.get_values().front();
+ }
+ if (parsed_args.symbol_arg.is_set())
+ {
+ symbol_arg = parsed_args.symbol_arg.get_values().front();
+ do_symbol = true;
+
+ }
+ do_disassembly = parsed_args.disassembly_opt.is_set();
+ do_annotate = parsed_args.annotate_opt.is_set() || parsed_args.disassembly_opt.is_set();
+ do_force_lock = parsed_args.force_lock_opt.is_set();
+ do_kernel = parsed_args.kernel_opt.is_set();
+ if (parsed_args.timeline_opt.is_set())
+ {
+ do_timeline = true;
+ if (count_interval == -1.0)
+ count_interval = 60;
+ if (count_duration == -1.0)
+ count_duration = 1;
+ }
+ sample_display_short = !parsed_args.sample_display_long_opt.is_set();
+ do_verbose = parsed_args.verbose_opt.is_set();
+ m_out.m_isQuiet = parsed_args.quite_opt.is_set();
+ if (parsed_args.json_opt.is_set() && m_outputType != TableType::ALL)
+ {
+ m_outputType = TableType::JSON;
+ m_out.m_isQuiet = true;
+ }
+ do_export_perf_data = parsed_args.export_perf_data_opt.is_set();
+
+
+ if (parsed_args.events_arg.is_set())
{
+ wstring raw_events_string = WStringJoin(parsed_args.events_arg.get_values(), L",");
if (do_sample || do_record)
{
m_sampling_flags.clear(); // Prepare to collect SPE filters
- if (parse_events_str_for_feat_spe(a, m_sampling_flags)) // Check if we are sampling with SPE
+ if (parse_events_str_for_feat_spe(raw_events_string, m_sampling_flags)) // Check if we are sampling with SPE
{
if (pmu_cfg.has_spe == false)
{
- m_out.GetErrorOutputStream() << L"SPE is not supported by your hardware: " << a << std::endl;
+ m_out.GetErrorOutputStream() << L"SPE is not supported by your hardware: " << raw_events_string << std::endl;
throw fatal_exception("ERROR_SPE_NOT_SUPP");
}
@@ -538,7 +406,7 @@ void user_request::parse_raw_args(wstr_vec& raw_args, const struct pmu_device_cf
}
else // Software sampling (no SPE)
{
- parse_events_str_for_sample(a, ioctl_events_sample, sampling_inverval);
+ parse_events_str_for_sample(raw_events_string, ioctl_events_sample, sampling_inverval);
// After events are parsed check if we can fulfil this request as sampling does not have multiplexing
if (ioctl_events_sample.size() > pmu_cfg.total_gpc_num)
{
@@ -556,44 +424,14 @@ void user_request::parse_raw_args(wstr_vec& raw_args, const struct pmu_device_cf
}
}
else
- parse_events_str(a, events, groups, L"", pmu_cfg);
- waiting_events = false;
- continue;
- }
-
- if (waiting_cwd)
- {
- waiting_cwd = false;
- m_cwd = a;
- continue;
- }
-
- if (waiting_output_filename)
- {
- waiting_output_filename = false;
- output_filename = a;
- continue;
- }
-
- if (waiting_output_csv_filename)
- {
- waiting_output_csv_filename = false;
- output_csv_filename = a;
- continue;
- }
-
- if (waiting_config)
- {
- waiting_config = false;
- if (drvconfig::set(a) == false)
- m_out.GetErrorOutputStream() << L"error: can't set '" << a << "' config" << std::endl;
- continue;
+ parse_events_str(raw_events_string, events, groups, L"", pmu_cfg);
}
- if (waiting_metrics)
+ if (parsed_args.metrics_arg.is_set())
{
+ wstring raw_metrics_string = WStringJoin(parsed_args.metrics_arg.get_values(), L",");
std::vector list_of_defined_metrics;
- std::wistringstream metric_stream(a);
+ std::wistringstream metric_stream(raw_metrics_string);
std::wstring metric_token;
while (std::getline(metric_stream, metric_token, L','))
@@ -642,392 +480,8 @@ void user_request::parse_raw_args(wstr_vec& raw_args, const struct pmu_device_cf
else if (metric == L"ddr_bw")
report_ddr_bw_metric = true;
}
-
- waiting_metrics = false;
- continue;
- }
-
- if (waiting_image_name)
- {
- sample_image_name = a;
- waiting_image_name = false;
- continue;
- }
-
- if (waiting_pe_file)
- {
- if (std::filesystem::exists(a) == false)
- {
- m_out.GetErrorOutputStream() << L"PE file '" << a << L"' doesn't exist"
- << std::endl;
- throw fatal_exception("ERROR_PE_FILE_PATH");
- }
-
- sample_pe_file = a;
- sample_pe_file_given = true;
- waiting_pe_file = false;
- continue;
- }
-
- if (waiting_pdb_file)
- {
- if (std::filesystem::exists(a) == false)
- {
- m_out.GetErrorOutputStream() << L"PDB file '" << a << L"' doesn't exist"
- << std::endl;
- throw fatal_exception("ERROR_PDB_FILE_PATH");
- }
-
- sample_pdb_file = a;
- waiting_pdb_file = false;
- continue;
}
- if (waiting_core_idx)
- {
- if (TokenizeWideStringOfInts(a.c_str(), L',', cores_idx) == false)
- {
- m_out.GetErrorOutputStream() << L"option -c format not supported, use comma separated list of integers, or range of integers"
- << std::endl;
- throw fatal_exception("ERROR_CORES");
- }
-
- if (cores_idx.size() > MAX_PMU_CTL_CORES_COUNT)
- {
- m_out.GetErrorOutputStream() << L"you can specify up to " << int(MAX_PMU_CTL_CORES_COUNT)
- << L"cores with -c option"
- << std::endl;
- throw fatal_exception("ERROR_CORES");
- }
-
- waiting_core_idx = false;
- continue;
- }
-
- if (waiting_timeline_count)
- {
- count_timeline = _wtoi(a.c_str());
- waiting_timeline_count = false;
- continue;
- }
-
- if (waiting_record_spawn_delay)
- {
- uint32_t val = _wtoi(a.c_str());
- assert(val <= UINT32_MAX);
- record_spawn_delay = val;
- waiting_record_spawn_delay = false;
- continue;
- }
-
- if (waiting_dmc_idx)
- {
- int val = _wtoi(a.c_str());
- assert(val <= UCHAR_MAX);
- dmc_idx = (uint8_t)val;
- waiting_dmc_idx = false;
- continue;
- }
-
- if (waiting_duration)
- {
- count_duration = convert_timeout_arg_to_seconds(a, waiting_duration_arg);
- waiting_duration = false;
- continue;
- }
-
- if (waiting_interval)
- {
- count_interval = convert_timeout_arg_to_seconds(a, waiting_duration_arg);
- waiting_interval = false;
- continue;
- }
-
- if (waiting_sample_display_row)
- {
- sample_display_row = _wtoi(a.c_str());
- waiting_sample_display_row = false;
- continue;
- }
-
- if (waiting_man_query)
- {
- man_query_args = a;
- waiting_man_query = false;
- continue;
- }
-
- if (waiting_symbol)
- {
- symbol_arg = a;
- waiting_symbol = false;
- continue;
- }
-
- // For compatibility with Linux perf
- if (a == L"list" || a == L"-l")
- {
- do_list = true;
- continue;
- }
-
- if (a == L"-e")
- {
- waiting_events = true;
- continue;
- }
-
- if (a == L"-C") // Skip this one as it was handled before any other args
- {
- waiting_metric_config = true;
- continue;
- }
-
- if (a == L"-E") // Skip this one as it was handled before any other args
- {
- waiting_events_config = true;
- continue;
- }
-
- if (a == L"-m")
- {
- waiting_metrics = true;
- continue;
- }
-
- if (a == L"stat")
- {
- do_count = true;
- continue;
- }
-
- if (a == L"record")
- {
- do_record = true;
- continue;
- }
-
- if (a == L"sample")
- {
- do_sample = true;
- continue;
- }
-
- if (a == L"detect")
- {
- do_detect = true;
- continue;
- }
- if (a == L"man")
- {
- do_man = true;
- waiting_man_query = true;
- continue;
- }
-
- if (a == L"--image_name")
- {
- waiting_image_name = true;
- continue;
- }
-
- if (a == L"--disassemble")
- {
- do_disassembly = true;
- do_annotate = true;
- continue;
- }
-
- if (a == L"--force-lock")
- {
- do_force_lock = true;
- continue;
- }
-
- if (a == L"--export_perf_data")
- {
- do_export_perf_data = true;
- continue;
- }
-
- if (a == L"--record_spawn_delay")
- {
- waiting_record_spawn_delay = true;
- continue;
- }
-
- if (a == L"--pe_file")
- {
- waiting_pe_file = true;
- continue;
- }
-
- if (a == L"--pdb_file")
- {
- waiting_pdb_file = true;
- continue;
- }
-
- if (a == L"--timeout" || a == L"sleep")
- {
- waiting_duration = true;
- waiting_duration_arg = a;
- continue;
- }
-
- if (a == L"--cwd" || a == L"--output-prefix")
- {
- waiting_cwd = true;
- continue;
- }
-
- if (a == L"--output" || a == L"-o")
- {
- waiting_output_filename = true;
- continue;
- }
-
- if (a == L"--output-csv")
- {
- waiting_output_csv_filename = true;
- continue;
- }
-
- if (a == L"--config")
- {
- waiting_config = true;
- continue;
- }
-
- if (a == L"-k")
- {
- do_kernel = true;
- continue;
- }
-
- if (a == L"-t")
- {
- do_timeline = true;
- if (count_interval == -1.0)
- count_interval = 60;
- if (count_duration == -1.0)
- count_duration = 1;
- continue;
- }
-
- if (a == L"-n")
- {
- waiting_timeline_count = true;
- continue;
- }
-
- if (a == L"--sample-display-row")
- {
- waiting_sample_display_row = true;
- continue;
- }
-
- if (a == L"--sample-display-long")
- {
- sample_display_short = false;
- continue;
- }
-
- if (a == L"-i")
- {
- waiting_duration_arg = a;
- waiting_interval = true;
- continue;
- }
-
- if (a == L"-c" || a == L"--cpu")
- {
- waiting_core_idx = true;
- continue;
- }
-
- if (a == L"--dmc")
- {
- waiting_dmc_idx = true;
- continue;
- }
-
- if (a == L"-v" || a == L"--verbose")
- {
- do_verbose = true;
- continue;
- }
-
- if (a == L"--annotate")
- {
- do_annotate = true;
- continue;
- }
-
- if (a == L"--version")
- {
- do_version = true;
- continue;
- }
-
- if (a == L"-h" || a == L"--help")
- {
- do_help = true;
- continue;
- }
-
- if (a == L"-q")
- {
- m_out.m_isQuiet = true;
- continue;
- }
-
- if (a == L"--json")
- {
- if (m_outputType != TableType::ALL)
- {
- m_outputType = TableType::JSON;
- m_out.m_isQuiet = true;
- }
- continue;
- }
-
- if (a == L"test")
- {
- do_test = true;
- continue;
- }
-
- if (a == L"-s" || a == L"--symbol")
- {
- do_symbol = true;
- waiting_symbol = true;
- continue;
- }
-
- /* This will finish command line argument parsing. After "--" we will see process to spawn in form of:
- *
- * wperf ... -- PROCESS_NAME ARG ARG ARG ARG ...
- *
- * From now on we will parse process spawn name and verbatim arguments
- */
- if (a == L"--")
- {
- if (!(do_record || do_count))
- m_out.GetErrorOutputStream() << L"warning: only `stat` and `record` support process spawn!" << std::endl;
-
- waiting_commandline = true;
- /* We will reload here PE file name to spawn. `waiting_commandline` will expect this
- * to be empty to detect beggining of the scan.
- */
- sample_pe_file.clear();
- record_commandline.clear();
- continue;
- }
-
- m_out.GetErrorOutputStream() << L"warning: unexpected arg '" << a << L"' ignored" << std::endl;
- }
-
std::wstring output_filename_full_path = output_filename;
std::wstring output_filename_csv_full_path = output_csv_filename;
diff --git a/wperf/user_request.h b/wperf/user_request.h
index 25dad10..faa6888 100644
--- a/wperf/user_request.h
+++ b/wperf/user_request.h
@@ -35,7 +35,7 @@
#include "utils.h"
#include "events.h"
#include "output.h"
-
+#include "arg_parser.h"
typedef std::vector wstr_vec;
class user_request
@@ -43,12 +43,12 @@ class user_request
public:
user_request();
- void init(wstr_vec& raw_args, const struct pmu_device_cfg& pmu_cfg,
+ void init(ArgParser::arg_parser& parsed_args, const struct pmu_device_cfg& pmu_cfg,
std::map& builtin_metrics,
const std::map >& groups_of_metrics,
std::map>& extra_events);
- void parse_raw_args(wstr_vec& raw_args, const struct pmu_device_cfg& pmu_cfg,
+ void parse_raw_args(ArgParser::arg_parser& parsed_args, const struct pmu_device_cfg& pmu_cfg,
std::map>& events,
std::map>& groups,
std::map& builtin_metrics,
@@ -62,10 +62,6 @@ class user_request
std::map>& extra_events);
void load_config_metrics(std::wstring config_name, const struct pmu_device_cfg& pmu_cfg);
- static void print_help();
- static void print_help_header();
- static void print_help_prompt();
- static void print_help_usage();
static bool is_cli_option_in_args(const wstr_vec& raw_args, std::wstring opt); // Return true if `opt` is in CLI options
static bool is_force_lock(const wstr_vec& raw_args); // Return true if `--force-lock` is in CLI options
static bool is_help(const wstr_vec& raw_args); // Return true if `--help` is in CLI options