diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fe378c..d15f77c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,11 +5,7 @@ name: MSBuild -on: - push: - branches: ["master"] - pull_request: - branches: ["master"] +on: push env: # Path to the solution file relative to the root of the project. diff --git a/parser-tests/mock-arg-parser.h b/parser-tests/mock-arg-parser.h index 7ec8279..39633e0 100644 --- a/parser-tests/mock-arg-parser.h +++ b/parser-tests/mock-arg-parser.h @@ -11,15 +11,4 @@ class mock_arg_parser : public arg_parser L"C:\\Program\\sample.exe", L"C:\\Program\\sample.pdb" }; - -protected: - void check_file_path(std::wstring file_path) override - { - if (existing_files.find(file_path) == existing_files.end()) - { - // Simulate file does not exist - throw_invalid_arg(file_path, L"Mock: File does not exist."); - } - // Else, do nothing (file exists) - } }; diff --git a/parser-tests/parser-tests.cpp b/parser-tests/parser-tests.cpp index a08c2ac..b2b541e 100644 --- a/parser-tests/parser-tests.cpp +++ b/parser-tests/parser-tests.cpp @@ -41,7 +41,10 @@ namespace parsertests TEST_CLASS(parsertests) { public: - + bool check_value_in_vector(const std::vector& vec, const std::wstring& value) + { + return std::find(vec.begin(), vec.end(), value) != vec.end(); + } TEST_METHOD(TEST_CORRECT_TEST_COMMAND) { const wchar_t* argv[] = { L"wperf", L"test", L"-v", L"--json" }; @@ -49,9 +52,9 @@ namespace parsertests mock_arg_parser parser; parser.parse(argc, argv); - Assert::AreEqual(true, parser.do_verbose.get()); - Assert::AreEqual(true, parser.do_json.get()); - Assert::IsTrue(COMMAND_CLASS::TEST == parser.command); + Assert::AreEqual(true, parser.do_verbose.is_set()); + Assert::AreEqual(true, parser.do_json.is_set()); + Assert::IsTrue(COMMAND_CLASS::TEST == parser.m_command); } TEST_METHOD(TEST_RANDOM_ARGS_REJECTION) { @@ -63,7 +66,7 @@ namespace parsertests } ); } - // Test parsing the 'help' command with no arguments + // Test parsing the "help" command with no arguments TEST_METHOD(TEST_HELP_COMMAND) { const wchar_t* argv[] = { L"wperf", L"--help" }; @@ -71,8 +74,8 @@ namespace parsertests mock_arg_parser parser; parser.parse(argc, argv); - Assert::IsTrue(parser.do_help.get()); - Assert::IsTrue(COMMAND_CLASS::HELP == parser.command); + Assert::IsTrue(parser.do_help.is_set()); + Assert::IsTrue(COMMAND_CLASS::HELP == parser.m_command); } // Test parsing the 'version' command with no arguments @@ -83,8 +86,8 @@ namespace parsertests mock_arg_parser parser; parser.parse(argc, argv); - Assert::IsTrue(parser.do_version.get()); - Assert::IsTrue(COMMAND_CLASS::VERSION == parser.command); + Assert::IsTrue(parser.do_version.is_set()); + Assert::IsTrue(COMMAND_CLASS::VERSION == parser.m_command); } // Test parsing the 'list' command with no arguments @@ -95,21 +98,8 @@ namespace parsertests mock_arg_parser parser; parser.parse(argc, argv); - Assert::IsTrue(parser.do_list.get()); - Assert::IsTrue(COMMAND_CLASS::LIST == parser.command); - } - - // Test parsing the 'sample' command with various flags - TEST_METHOD(TEST_SAMPLE_COMMAND_WITH_MULTIPLE_CORES) - { - const wchar_t* argv[] = { L"wperf", L"sample", L"--annotate", L"--timeout", L"5s", L"-c", L"0,1,2" }; - int argc = 7; - mock_arg_parser parser; - - Assert::ExpectException([&parser, argc, &argv]() { - parser.parse(argc, argv); - } - ); + Assert::IsTrue(parser.do_list.is_set()); + Assert::IsTrue(COMMAND_CLASS::LIST == parser.m_command); } // Test parsing the 'record' command with command line separator and arguments @@ -119,12 +109,9 @@ namespace parsertests int argc = 5; mock_arg_parser parser; parser.parse(argc, argv); - - Assert::IsTrue(parser.do_record.get()); - Assert::AreEqual(std::wstring(L"notepad.exe test_arg"), parser.record_commandline.get()); - Assert::AreEqual(std::wstring(L"notepad.exe"), parser.sample_pe_file.get()); - Assert::AreEqual(std::wstring(L"notepad.pdb"), parser.sample_pdb_file.get()); - Assert::IsTrue(COMMAND_CLASS::RECORD == parser.command); + Assert::IsTrue(parser.do_record.is_set()); + Assert::IsTrue(check_value_in_vector(parser.double_dash.get_values(), L"notepad.exe")); + Assert::IsTrue(COMMAND_CLASS::RECORD == parser.m_command); } // Test that missing required arguments cause exceptions @@ -159,20 +146,9 @@ namespace parsertests mock_arg_parser parser; parser.parse(argc, argv); - Assert::AreEqual(120.0, parser.count_duration.get()); // 2 minutes in seconds + Assert::IsTrue(check_value_in_vector(parser.count_duration.get_values(), L"2m")); } - // Test that invalid timeout format causes exception - TEST_METHOD(TEST_INVALID_TIMEOUT_WITH_WRONG_UNIT) - { - const wchar_t* argv[] = { L"wperf", L"sample", L"--timeout", L"5x" }; - int argc = 4; - mock_arg_parser parser; - Assert::ExpectException([&parser, argc, &argv]() { - parser.parse(argc, argv); - } - ); - } TEST_METHOD(TEST_INVALID_TIMEOUT_WITH_WRONG_FORMAT) { const wchar_t* argv[] = { L"wperf", L"sample", L"--timeout", L"5.4", L"ms"}; @@ -192,11 +168,11 @@ namespace parsertests mock_arg_parser parser; parser.parse(argc, argv); - Assert::IsTrue(parser.do_detect.get()); - Assert::IsTrue(COMMAND_CLASS::DETECT == parser.command); + Assert::IsTrue(parser.do_detect.is_set()); + Assert::IsTrue(COMMAND_CLASS::DETECT == parser.m_command); } - // Test parsing multiple flags together + // Test parsing multiple flags tois_sether TEST_METHOD(TEST_MULTIPLE_FLAGS) { const wchar_t* argv[] = { L"wperf", L"sample", L"--verbose", L"-q", L"--json" }; @@ -204,10 +180,10 @@ namespace parsertests mock_arg_parser parser; parser.parse(argc, argv); - Assert::IsTrue(parser.do_verbose.get()); - Assert::IsTrue(parser.is_quite.get()); - Assert::IsTrue(parser.do_json.get()); - Assert::IsTrue(COMMAND_CLASS::SAMPLE == parser.command); + Assert::IsTrue(parser.do_verbose.is_set()); + Assert::IsTrue(parser.is_quite.is_set()); + Assert::IsTrue(parser.do_json.is_set()); + Assert::IsTrue(COMMAND_CLASS::SAMPLE == parser.m_command); } // Test parsing sample command with symbol argument @@ -218,8 +194,8 @@ namespace parsertests mock_arg_parser parser; parser.parse(argc, argv); - Assert::AreEqual(std::wstring(L"main"), parser.symbol_arg.get()); - Assert::IsTrue(COMMAND_CLASS::SAMPLE == parser.command); + Assert::IsTrue(check_value_in_vector(parser.symbol_arg.get_values(), L"main")); + Assert::IsTrue(COMMAND_CLASS::SAMPLE == parser.m_command); } // Test parsing sample command with sample display row @@ -230,8 +206,8 @@ namespace parsertests mock_arg_parser parser; parser.parse(argc, argv); - Assert::AreEqual((uint32_t)100, parser.sample_display_row.get()); - Assert::IsTrue(COMMAND_CLASS::SAMPLE == parser.command); + Assert::IsTrue(check_value_in_vector(parser.sample_display_row.get_values(), L"100")); + Assert::IsTrue(COMMAND_CLASS::SAMPLE == parser.m_command); } // Test parsing sample command with pe_file @@ -243,10 +219,8 @@ namespace parsertests parser.parse(argc, argv); - Assert::AreEqual(std::wstring(L"C:\\Program\\sample.exe"), parser.sample_pe_file.get()); - Assert::AreEqual(std::wstring(L"C:\\Program\\sample.pdb"), parser.sample_pdb_file.get()); - Assert::AreEqual(std::wstring(L"C:\\Program\\sample.exe"), parser.sample_image_name.get()); - Assert::IsTrue(COMMAND_CLASS::SAMPLE == parser.command); + Assert::IsTrue(check_value_in_vector(parser.sample_pe_file.get_values(), L"C:\\Program\\sample.exe")); + Assert::IsTrue(COMMAND_CLASS::SAMPLE == parser.m_command); } // Test parsing sample command with pdb_file @@ -259,8 +233,8 @@ namespace parsertests // Similarly, adjust or mock check_file_path for testing parser.parse(argc, argv); - Assert::AreEqual(std::wstring(L"C:\\Program\\sample.pdb"), parser.sample_pdb_file.get()); - Assert::IsTrue(COMMAND_CLASS::SAMPLE == parser.command); + Assert::IsTrue(check_value_in_vector(parser.sample_pdb_file.get_values(), L"C:\\Program\\sample.pdb")); + Assert::IsTrue(COMMAND_CLASS::SAMPLE == parser.m_command); } // Test parsing sample command with image_name @@ -271,8 +245,8 @@ namespace parsertests mock_arg_parser parser; parser.parse(argc, argv); - Assert::AreEqual(std::wstring(L"notepad.exe"), parser.sample_image_name.get()); - Assert::IsTrue(COMMAND_CLASS::SAMPLE == parser.command); + Assert::IsTrue(check_value_in_vector(parser.sample_image_name.get_values(), L"notepad.exe")); + Assert::IsTrue(COMMAND_CLASS::SAMPLE == parser.m_command); } // Test parsing with --force-lock flag @@ -283,8 +257,8 @@ namespace parsertests mock_arg_parser parser; parser.parse(argc, argv); - Assert::IsTrue(parser.do_force_lock.get()); - Assert::IsTrue(COMMAND_CLASS::TEST == parser.command); + Assert::IsTrue(parser.do_force_lock.is_set()); + Assert::IsTrue(COMMAND_CLASS::TEST == parser.m_command); } // Test parsing the 'stat' command (not fully implemented in the parser) @@ -296,7 +270,7 @@ namespace parsertests parser.parse(argc, argv); // Assuming command is set correctly - Assert::IsTrue(COMMAND_CLASS::STAT == parser.command); + Assert::IsTrue(COMMAND_CLASS::STAT == parser.m_command); } // Test parsing with unknown flags @@ -310,5 +284,35 @@ namespace parsertests } ); } + + // Test parsing with no command + TEST_METHOD(TEST_NO_COMMAND) + { + const wchar_t* argv[] = { L"wperf", L"--annotate", L"--json" }; + int argc = 3; + mock_arg_parser parser; + Assert::ExpectException([&parser, argc, &argv]() { + parser.parse(argc, argv); + } + ); + } + + // Test complex stat command + TEST_METHOD(TEST_FULL_STAT_COMMAND) + { + const wchar_t* argv[] = { L"wperf", L"stat", L"--output", L"_output_02.json", L"-e", L"inst_spec,vfp_spec,ase_spec,dp_spec,ld_spec,st_spec,br_immed_spec,crypto_spec", L"-c", L"0", L"sleep", L"5" }; + int argc = 10; + mock_arg_parser parser; + parser.parse(argc, argv); + Assert::IsTrue(COMMAND_CLASS::STAT == parser.m_command); + Assert::IsTrue(parser.raw_events.is_set()); + Assert::IsTrue(parser.output_filename.is_set()); + Assert::IsTrue(parser.cores_idx.is_set()); + Assert::IsTrue(parser.count_duration.is_set()); + Assert::IsTrue(check_value_in_vector(parser.count_duration.get_values(), L"5")); + Assert::IsTrue(check_value_in_vector(parser.cores_idx.get_values(), L"0")); + Assert::IsTrue(check_value_in_vector(parser.output_filename.get_values(), L"_output_02.json")); + Assert::IsTrue(check_value_in_vector(parser.raw_events.get_values(), L"inst_spec,vfp_spec,ase_spec,dp_spec,ld_spec,st_spec,br_immed_spec,crypto_spec")); + } }; } diff --git a/parser-tests/parser-tests.vcxproj b/parser-tests/parser-tests.vcxproj index 8674fd2..d8999d1 100644 --- a/parser-tests/parser-tests.vcxproj +++ b/parser-tests/parser-tests.vcxproj @@ -103,7 +103,7 @@ Windows $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories);$(SolutionDir)parser\$(Platform)\$(Configuration)\ - $(CoreLibraryDependencies);%(AdditionalDependencies);$(SolutionDir)parser\$(Platform)\$(Configuration)\arg-parser.obj;$(SolutionDir)parser\$(Platform)\$(Configuration)\utils.obj + $(CoreLibraryDependencies);%(AdditionalDependencies);$(SolutionDir)parser\$(Platform)\$(Configuration)\arg-parser.obj;$(SolutionDir)parser\$(Platform)\$(Configuration)\arg-parser-arg.obj @@ -158,7 +158,7 @@ true true $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories);$(SolutionDir)parser\$(Platform)\$(Configuration)\ - $(CoreLibraryDependencies);%(AdditionalDependencies);$(SolutionDir)parser\$(Platform)\$(Configuration)\arg-parser.obj;$(SolutionDir)parser\$(Platform)\$(Configuration)\utils.obj + $(CoreLibraryDependencies);%(AdditionalDependencies);$(SolutionDir)parser\$(Platform)\$(Configuration)\arg-parser.obj;$(SolutionDir)parser\$(Platform)\$(Configuration)\arg-parser-arg.obj diff --git a/parser/arg-parser-arg.cpp b/parser/arg-parser-arg.cpp new file mode 100644 index 0000000..c1482f2 --- /dev/null +++ b/parser/arg-parser-arg.cpp @@ -0,0 +1,112 @@ +#include "arg-parser-arg.h" +#include + +arg_parser_arg::arg_parser_arg( + const std::wstring name, + const std::vector alias, + const std::wstring description, + const std::vector default_values, + const int arg_count +) : m_name(name), m_aliases(alias), m_description(description), m_arg_count(arg_count), m_values(default_values) {}; + +inline bool arg_parser_arg::operator==(const std::wstring& other_arg) const +{ + return is_match(other_arg); +} + +bool arg_parser_arg::is_match(const std::wstring& other_arg) const +{ + return !other_arg.empty() && (other_arg == m_name || std::find(m_aliases.begin(), m_aliases.end(), other_arg) != m_aliases.end()); +} + +std::wstring arg_parser_arg::get_help() const +{ + return m_name + L" or " + get_alias_string() + L"/n " + m_description; +} + +std::wstring arg_parser_arg::get_usage_text() const +{ + return m_description; +} + +arg_parser_arg arg_parser_arg::add_alias(std::wstring new_alias) +{ + m_aliases.push_back(new_alias); + return *this; +} + +int arg_parser_arg::get_arg_count() const +{ + return m_arg_count; +} + +std::wstring arg_parser_arg::get_name() const +{ + return m_name; +} + +std::wstring arg_parser_arg::get_alias_string() const +{ + // convert alias vector to wstring + std::wstring alias_string; + for (auto& m_alias : m_aliases) { + alias_string.append(m_alias); + } + return alias_string; +} + +arg_parser_arg arg_parser_arg::add_check_func(std::function check_func) +{ + m_check_funcs.push_back(check_func); + return *this; +} + +void arg_parser_arg::set_is_parsed() +{ + m_is_parsed = true; +} + +bool arg_parser_arg::is_parsed() +{ + return m_is_parsed; +} + +bool arg_parser_arg::is_set() +{ + return is_parsed() && m_values.size() == m_arg_count; +} + +std::vector arg_parser_arg::get_values() +{ + return m_values; +} + +bool arg_parser_arg::parse(std::vector arg_vect) +{ + if (arg_vect.size() == 0 || !is_match(arg_vect[0])) + return false; + + if (m_arg_count == -1 && arg_vect.size() > 0) m_arg_count = arg_vect.size() - 1; + + if (arg_vect.size() < m_arg_count + 1) + throw std::invalid_argument("Not enough arguments provided."); + + if (m_arg_count == 0) + { + set_is_parsed(); + return true; + } + + for (int i = 1; i < m_arg_count + 1; ++i) + { + for (auto& check_func : m_check_funcs) + { + if (!check_func(arg_vect[i])) + throw std::invalid_argument("Invalid arguments provided."); + } + m_values.push_back(arg_vect[i]); + } + set_is_parsed(); + return true; +} + diff --git a/parser/arg-parser-arg.h b/parser/arg-parser-arg.h new file mode 100644 index 0000000..85b1423 --- /dev/null +++ b/parser/arg-parser-arg.h @@ -0,0 +1,63 @@ +#pragma once +#include +#include +#include +#include + +struct arg_parser_arg { + const std::wstring m_name; + std::vector m_aliases{}; + const std::wstring m_description; + + int m_arg_count; // -1 for variable number of arguments + bool m_is_parsed = false; + std::vector m_values{}; + + std::vector > m_check_funcs = {}; + + inline bool operator==(const std::wstring& other) const; + +public: + arg_parser_arg( + const std::wstring name, + const std::vector alias, + const std::wstring description, + const std::vector default_values = {}, + const int arg_count = 0 + ); + bool is_match(const std::wstring& arg) const; + virtual std::wstring get_help() const; + virtual std::wstring get_usage_text() const; + arg_parser_arg add_alias(std::wstring new_alias); + int get_arg_count() const; + std::wstring get_name() const; + std::wstring get_alias_string() const; + arg_parser_arg add_check_func(std::function check_func); + void set_is_parsed(); + bool is_parsed(); + bool is_set(); + std::vector get_values(); + bool parse(std::vector arg_vect); +}; + +class arg_parser_arg_opt : public arg_parser_arg { +public: + arg_parser_arg_opt( + const std::wstring name, + const std::vector alias, + const std::wstring description, + const std::vector default_values = {} + ) : arg_parser_arg(name, alias, description, default_values, 0) {}; +}; + +class arg_parser_arg_pos : public arg_parser_arg { +public: + arg_parser_arg_pos( + const std::wstring name, + const std::vector alias, + const std::wstring description, + const std::vector default_values = {}, + const int arg_count = 1 + ) : arg_parser_arg(name, alias, description, default_values, arg_count) {}; +}; + diff --git a/parser/arg-parser.cpp b/parser/arg-parser.cpp index 185ff1b..2ed8781 100644 --- a/parser/arg-parser.cpp +++ b/parser/arg-parser.cpp @@ -29,12 +29,12 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "arg-parser.h" -#include "utils.h" #include #include #include #include #include +#include arg_parser::arg_parser() {} @@ -47,402 +47,63 @@ void arg_parser::parse( for (int i = 1; i < argc; i++) { raw_args.push_back(argv[i]); - arg_array.push_back(argv[i]); - + m_arg_array.push_back(argv[i]); } - try_match_and_set_arg(raw_args, do_list); - try_match_and_set_arg(raw_args, do_help); - try_match_and_set_arg(raw_args, do_version); - try_match_and_set_arg(raw_args, do_test); - try_match_and_set_arg(raw_args, do_detect); - try_match_and_set_arg(raw_args, do_sample); - try_match_and_set_arg(raw_args, do_record); - try_match_and_set_arg(raw_args, do_count); - - while (raw_args.size() > 0) - { - size_t initial_size = raw_args.size(); - if (command == COMMAND_CLASS::NO_COMMAND) { - throw_invalid_arg(raw_args.front(), L"Error: at least one command is needed "); - } - - if (command == COMMAND_CLASS::SAMPLE || command == COMMAND_CLASS::RECORD) - parse_sampling_args(raw_args); - - if (commands_with_no_args.find(command) != commands_with_no_args.end()) goto standard_arguments; + if (raw_args.size() == 0) + throw_invalid_arg(L"", L"warning: No arguments were found!"); - if (try_match_arg(raw_args, record_commandline)) { - if (command != COMMAND_CLASS::RECORD && command != COMMAND_CLASS::STAT && command != COMMAND_CLASS::TIMELINE && command != COMMAND_CLASS::SPE) - throw_invalid_arg(raw_args.front(), L"warning: only `stat` and `record` support process spawn!"); +#pragma region Command Selector + for (auto& command : m_commands_list) + { + if (command->parse(raw_args)) { + m_command = command->m_command; + raw_args.erase(raw_args.begin()); - parse_record_commandline(raw_args); break; } - - standard_arguments: - try_match_and_set_bool_flag(raw_args, do_json); - try_match_and_set_bool_flag(raw_args, do_verbose); - try_match_and_set_bool_flag(raw_args, do_force_lock); - - if (initial_size == raw_args.size()) - { - throw_invalid_arg(raw_args.front()); - } } -#pragma region Check for required flags per command - switch (command) - { - case STAT: - break; - case SAMPLE: - check_sampling_required_args(); - break; - case RECORD: - check_record_required_args(); - break; - case TEST: - break; - case DETECT: - break; - case HELP: - break; - case VERSION: - break; - case LIST: - break; - case MAN: - break; - case SPE: - break; - case TIMELINE: - break; - case NO_COMMAND: - break; - default: - break; + if (m_command == COMMAND_CLASS::NO_COMMAND) { + throw_invalid_arg(raw_args.front(), L"warning: command not recognized!"); } - #pragma endregion -} - -#pragma region Sampling section parsing - -void arg_parser::parse_sampling_args(wstr_vec& raw_args_vect) -{ - while (raw_args_vect.size() > 0) + while (raw_args.size() > 0) { - size_t initial_size = raw_args_vect.size(); - try_match_and_set_bool_flag(raw_args_vect, do_annotate); - try_match_and_set_bool_flag(raw_args_vect, do_kernel); - try_match_and_set_bool_flag(raw_args_vect, sample_display_long); - try_match_and_set_bool_flag(raw_args_vect, is_quite); - try_match_and_set_bool_flag(raw_args_vect, do_disassembly); - if (try_match_arg(raw_args_vect, cores_idx)) - { - check_flag_value_existance(raw_args_vect); - - parse_cpu_core(raw_args_vect, 1); - } - if (try_match_arg(raw_args_vect, count_duration)) - { - check_flag_value_existance(raw_args_vect); - - count_duration.value = convert_timeout_arg_to_seconds(raw_args_vect.front()); - raw_args_vect.erase(raw_args_vect.begin()); - } - if (try_match_arg(raw_args_vect, symbol_arg)) - { - check_flag_value_existance(raw_args_vect); - - symbol_arg.value = raw_args_vect.front(); - raw_args_vect.erase(raw_args_vect.begin()); - } - if (try_match_arg(raw_args_vect, record_spawn_delay)) - { - check_flag_value_existance(raw_args_vect); + size_t initial_size = raw_args.size(); - record_spawn_delay.value = convert_timeout_arg_to_seconds(raw_args_vect.front()); - raw_args_vect.erase(raw_args_vect.begin()); - } - if (try_match_arg(raw_args_vect, sample_display_row)) + for (auto& current_flag : m_flags_list) { - check_flag_value_existance(raw_args_vect); try { - sample_display_row.value = _wtoi(raw_args_vect.front().c_str()); - raw_args_vect.erase(raw_args_vect.begin()); + if (current_flag->parse(raw_args)) { + raw_args.erase(raw_args.begin(), raw_args.begin() + current_flag->get_arg_count() + 1); + } } - catch (const std::exception&) + catch (const std::exception& err) { - throw_invalid_arg(raw_args_vect.front()); + throw_invalid_arg(raw_args.front(), L"Error: " + std::wstring(err.what(), err.what() + std::strlen(err.what()))); } } - if (try_match_arg(raw_args_vect, sample_pe_file)) - { - check_flag_value_existance(raw_args_vect); - check_file_path(raw_args_vect.front()); - sample_pe_file.value = raw_args_vect.front(); - - fill_pdb_and_image_name_if_empty(); - - raw_args_vect.erase(raw_args_vect.begin()); - } - if (try_match_arg(raw_args_vect, sample_pdb_file)) - { - check_flag_value_existance(raw_args_vect); - check_file_path(raw_args_vect.front()); - - sample_pdb_file.value = raw_args_vect.front(); - raw_args_vect.erase(raw_args_vect.begin()); - } - if (try_match_arg(raw_args_vect, sample_image_name)) - { - check_flag_value_existance(raw_args_vect); - - sample_image_name.value = raw_args_vect.front(); - raw_args_vect.erase(raw_args_vect.begin()); - } - - if (try_match_arg(raw_args_vect, events_string)) { - check_flag_value_existance(raw_args_vect); - - raw_args_vect.erase(raw_args_vect.begin()); - - } - - if (initial_size == raw_args_vect.size()) break; - } -} - -void arg_parser::parse_event_list(wstring i_events) { - events_string.value = i_events; - if (command == COMMAND_CLASS::SAMPLE || command == COMMAND_CLASS::RECORD) { - if (i_events.rfind(ARM_SPE_EVENT_PREFIX)) command = COMMAND_CLASS::SPE; - - if (i_events.rfind(L"{") != std::wstring::npos) { - throw_invalid_arg(L"{", L"Error: Event groups are only available in the stat command"); - } - } - if (command == COMMAND_CLASS::STAT) { - if (i_events.rfind(ARM_SPE_EVENT_PREFIX)) - throw_invalid_arg(ARM_SPE_EVENT_PREFIX, L"Error: SPE sampling is not available with the stat command"); - } - if (commands_with_events_and_metrics.find(command) == commands_with_events_and_metrics.end()) { - throw_invalid_arg(events_string.key, L"Error: Event list is only supported with the commands: stat, record, sample"); - } -} - -void arg_parser::parse_cpu_core(wstr_vec& raw_args_vect, uint8_t MAX_CPU_CORES) -{ - wstring cores = raw_args_vect.front(); - if (TokenizeWideStringOfInts(cores.c_str(), L',', cores_idx.value) == false) - { - throw_invalid_arg(raw_args_vect.front()); - } - // TODO: add CPU core ranges - if (cores_idx.value.size() > MAX_CPU_CORES) - { - std::wostringstream error_message; - error_message << L"Maximum number of cores allowed is " << MAX_CPU_CORES; - throw_invalid_arg(raw_args_vect.front(), error_message.str()); - } - raw_args_vect.erase(raw_args_vect.begin()); -} - - -void arg_parser::check_sampling_required_args() { - // TODO: implement this function -} - -void arg_parser::check_record_required_args() { - // TODO: implement this function -} -#pragma endregion - - -#pragma region command line parsing after "--" -void arg_parser::fill_pdb_and_image_name_if_empty() { - if (sample_image_name.value.empty()) - { - sample_image_name.value = sample_pe_file.value; - } - - if (sample_pdb_file.value.empty()) { - sample_pdb_file.value = ReplaceFileExtension(sample_pe_file.value, L"pdb"); - } -} -void arg_parser::parse_record_commandline(wstr_vec& raw_args_vect) -{ - while (raw_args_vect.size() > 0) - { - wstring arg = raw_args_vect.front(); - - if (sample_pe_file.value.empty()) + // if going over all the known arguments doesn't affect the size of the raw_args, then the command is unkown + if (initial_size == raw_args.size()) { - sample_pe_file.value = arg; - record_commandline.value = arg; - fill_pdb_and_image_name_if_empty(); - + throw_invalid_arg(raw_args.front(), L"Error: Unrecognized command"); } - else - record_commandline.value += L" " + arg; - - raw_args_vect.erase(raw_args_vect.begin()); - } -} -#pragma endregion -#pragma region arg_matching and setting -bool arg_parser::try_match_and_set_bool_flag(wstr_vec& raw_args_vect, flag_type& flag) -{ - if(!try_match_arg(raw_args_vect, flag)) return false; - - flag.value = true; - return true; -} - -bool arg_parser::try_match_and_set_arg(wstr_vec& raw_args_vect, arg_type& flag) -{ - if (raw_args_vect.size() == 0) - return false; - - if (!(flag == raw_args_vect.front())) - return false; - - flag.value = true; - command = flag.command; - raw_args_vect.erase(raw_args_vect.begin()); - return true; -} -#pragma endregion - -#pragma region arg matching - -bool arg_parser::try_match_arg(wstr_vec& raw_args_vect, arg_base_type& flag) -{ - if (raw_args_vect.size() == 0) - return false; - - if (!(flag == raw_args_vect.front())) - return false; - - raw_args_vect.erase(raw_args_vect.begin()); - return true; -} -#pragma endregion - - -#pragma region Utils -bool arg_parser::check_timeout_arg(std::wstring number_and_suffix, const std::unordered_map& unit_map) -{ - std::wstring accept_units; - - for (const auto& pair : unit_map) - { - if (!accept_units.empty()) { - accept_units += L"|"; - } - accept_units += pair.first; - } - - - std::wstring regex_string = L"^(0|([1-9][0-9]*))(\\.[0-9]{1,2})?(" + accept_units + L")?$"; - std::wregex r(regex_string); - - std::wsmatch match; - if (std::regex_search(number_and_suffix, match, r)) { - return true; - } - else { - return false; } } -double arg_parser::convert_timeout_arg_to_seconds(std::wstring number_and_suffix) -{ - std::unordered_map unit_map = { {L"s", 1}, { L"m", 60 }, {L"ms", 0.001}, {L"h", 3600}, {L"d" , 86400} }; - - if (!check_timeout_arg(number_and_suffix, unit_map)) { - throw_invalid_arg(number_and_suffix); - } - //logic to split number and suffix - int i = 0; - for (; i < number_and_suffix.size(); i++) - { - if (!std::isdigit(number_and_suffix[i]) && (number_and_suffix[i] != L'.')) { - break; - } - } - - std::wstring number_wstring = number_and_suffix.substr(0, i); - - double number; - try { - number = std::stod(number_wstring); - } - catch (...) { - throw_invalid_arg(number_and_suffix); - } - - std::wstring suffix = number_and_suffix.substr(i); - - //default to seconds if unit is not provided - if (suffix.empty()) { - return number; - } - - //check if the unit exists in the map - auto it = unit_map.find(suffix); - if (it == unit_map.end()) - { - throw_invalid_arg(number_and_suffix); - - } - //Note: This exception should never be reached, as it should be caught in the regex construction of check_timeout_arg - //However, if the unit map/regex construction is changeed in the future, this serves as a good safety net - return ConvertNumberWithUnit(number, suffix, unit_map); -} - -void arg_parser::check_file_path(wstring file_path) -{ - if (std::filesystem::exists(file_path) == false) - { - std::wostringstream error_message; - error_message << L"File path '" << file_path << L"' doesn't exist"; - throw_invalid_arg(file_path, error_message.str()); - } -} -#pragma endregion - #pragma region error handling -void arg_parser::check_flag_value_existance(const wstr_vec& raw_args_vect) const -{ - check_next_arg(raw_args_vect); - if (raw_args_vect.front().find(L"-") == 0) - { - throw_invalid_arg(raw_args_vect.front(), L"Hint: Missing value for argument!"); - } -} - -void arg_parser::check_next_arg(const wstr_vec& raw_args_vect) const -{ - if (raw_args_vect.size() == 0) - { - throw_invalid_arg(L""); - } -} - void arg_parser::throw_invalid_arg(const std::wstring& arg, const std::wstring& additional_message) const { std::wstring command = L"wperf"; - for (int i = 1; i < arg_array.size(); ++i) { + for (int i = 1; i < m_arg_array.size(); ++i) { if (i > 0) { command.append(L" "); } - command.append(arg_array.at(i)); + command.append(m_arg_array.at(i)); } std::size_t pos = command.find(arg); diff --git a/parser/arg-parser.h b/parser/arg-parser.h index eb269ef..7bce741 100644 --- a/parser/arg-parser.h +++ b/parser/arg-parser.h @@ -37,15 +37,13 @@ #include #include #include +#include "arg-parser-arg.h" using namespace std; typedef std::vector wstr_vec; -#define ARM_SPE_EVENT_PREFIX L"arm_spe_0" - -#pragma region arg structs enum COMMAND_CLASS { STAT, SAMPLE, @@ -56,49 +54,25 @@ enum COMMAND_CLASS { VERSION, LIST, MAN, - SPE, - TIMELINE, NO_COMMAND }; -static const std::set commands_with_events_and_metrics = { - COMMAND_CLASS::STAT, - COMMAND_CLASS::SAMPLE, - COMMAND_CLASS::RECORD, - COMMAND_CLASS::TIMELINE, - COMMAND_CLASS::SPE -}; -static const std::set commands_with_no_args = { - COMMAND_CLASS::HELP, - COMMAND_CLASS::VERSION, - COMMAND_CLASS::LIST, - COMMAND_CLASS::DETECT, - COMMAND_CLASS::TEST, -}; - -struct arg_base_type { - const wstring key; - const wstring alias; - inline bool operator==(const wstring& arg) const { - return is_match(arg); - } - bool is_match(const wstring& arg) const { - return !arg.empty() && (arg == key || arg == alias); - } +class arg_parser_arg_command : public arg_parser_arg_opt { +public: + arg_parser_arg_command( + const std::wstring name, + const std::vector alias, + const std::wstring description, + const std::wstring examples, + const COMMAND_CLASS command + ) : arg_parser_arg_opt(name, alias, description), m_examples(examples), m_command(command) {}; + const COMMAND_CLASS m_command = NO_COMMAND; + const std::wstring m_examples; }; -template -struct flag_type : arg_base_type { - wstring description; - T value; - T get() const { - return value; - } -}; +#pragma region arg structs + -struct arg_type : flag_type { - const COMMAND_CLASS command; -}; #pragma endregion class arg_parser @@ -107,247 +81,318 @@ class arg_parser #pragma region Methods arg_parser(); void parse( - _In_ const int argc, - _In_reads_(argc) const wchar_t* argv[] + _In_ const int argc, + _In_reads_(argc) const wchar_t* argv[] ); #pragma endregion #pragma region Commands - arg_type do_list = { - L"list", - L"-l", - L"", - false, - COMMAND_CLASS::LIST - }; - arg_type do_test = { - L"test", - L"", - L"", - false, - COMMAND_CLASS::TEST - }; - arg_type do_help = { - L"-h", - L"--help", - L"", - false, - COMMAND_CLASS::HELP - }; - arg_type do_version = { - L"--version", - L"", - L"", - false, - COMMAND_CLASS::VERSION - }; - arg_type do_detect = { - L"detect", - L"", - L"", - false, - COMMAND_CLASS::DETECT - }; - arg_type do_sample = { - L"sample", - L"", - L"", - false, - COMMAND_CLASS::SAMPLE - }; - arg_type do_record = { - L"record", - L"", - L"", - false, - COMMAND_CLASS::RECORD - }; - arg_type do_count = { - L"stat", - L"", - L"", - false, - COMMAND_CLASS::STAT - }; + arg_parser_arg_command do_list = arg_parser_arg_command::arg_parser_arg_command( + L"list", + { L"-l" }, + L"List available metrics and events.", + L"", + COMMAND_CLASS::LIST + ); + + arg_parser_arg_command do_test = arg_parser_arg_command::arg_parser_arg_command( + L"test", + { L"" }, + L"", + L"", + COMMAND_CLASS::TEST + ); + arg_parser_arg_command do_help = arg_parser_arg_command::arg_parser_arg_command( + L"-h", + { L"--help" }, + L"", + L"", + COMMAND_CLASS::HELP + ); + arg_parser_arg_command do_version = arg_parser_arg_command::arg_parser_arg_command( + L"--version", + { L"" }, + L"", + L"", + COMMAND_CLASS::VERSION + ); + arg_parser_arg_command do_detect = arg_parser_arg_command::arg_parser_arg_command( + L"detect", + { L"" }, + L"", + L"", + COMMAND_CLASS::DETECT + ); + arg_parser_arg_command do_sample = arg_parser_arg_command::arg_parser_arg_command( + L"sample", + { L"" }, + L"", + L"", + COMMAND_CLASS::SAMPLE + ); + arg_parser_arg_command do_record = arg_parser_arg_command::arg_parser_arg_command( + L"record", + { L"" }, + L"", + L"", + COMMAND_CLASS::RECORD + ); + arg_parser_arg_command do_count = arg_parser_arg_command::arg_parser_arg_command( + L"stat", + { L"" }, + L"", + L"", + COMMAND_CLASS::STAT + ); + arg_parser_arg_command do_man = arg_parser_arg_command::arg_parser_arg_command( + L"stat", + { L"" }, + L"", + L"", + COMMAND_CLASS::MAN + ); #pragma endregion -#pragma region Flags - flag_type do_json = { - L"--json", - L"", - L"Define output type as JSON.", - false, - }; - flag_type do_kernel = { - L"--k", - L"", - L"Count kernel mode as well (disabled by default).", - false, - }; - flag_type do_force_lock = { - L"--force-lock", - L"", - LR"(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.)", - false, - }; +#pragma region Boolean Flags + arg_parser_arg_opt do_json = arg_parser_arg_opt::arg_parser_arg_opt( + L"--json", + {}, + L"Define output type as JSON.", + {} + ); + arg_parser_arg_opt do_kernel = arg_parser_arg_opt::arg_parser_arg_opt( + L"-k", + {}, + L"Count kernel mode as well (disabled by default).", + {} + ); + arg_parser_arg_opt do_force_lock = arg_parser_arg_opt::arg_parser_arg_opt( + L"--force-lock", + {}, + L"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.", + {} + ); // used to be called sample_display_short - flag_type sample_display_long = { - L"--sample-display-long", - L"", - L"Display decorated symbol names.", - false, - }; - flag_type do_verbose = { - L"--verbose", - L"-v", - L"Enable verbose output also in JSON output.", - false, - }; - flag_type is_quite = { - L"-q", - L"", - L"Quiet mode, no output is produced.", - false, - }; - flag_type do_annotate = { - L"--annotate", - L"", - L"Enable translating addresses taken from samples in sample/record mode into source code line numbers.", - false, - }; - flag_type do_disassembly = { - L"--disassemble", - L"", - L"Enable disassemble output on sampling mode. Implies 'annotate'.", - false, - }; - flag_type record_commandline = { - L"--", - L"", - L"-- Process name is defined by COMMAND. User can pass verbatim arguments to the process with[ARGS].", - L"", - }; // ... + arg_parser_arg_opt sample_display_long = arg_parser_arg_opt::arg_parser_arg_opt( + L"--sample-display-long", + {}, + L"Display decorated symbol names.", + {} + ); + arg_parser_arg_opt do_verbose = arg_parser_arg_opt::arg_parser_arg_opt( + L"--verbose", + { L"-v" }, + L"Enable verbose output also in JSON output.", + {} + ); + arg_parser_arg_opt is_quite = arg_parser_arg_opt::arg_parser_arg_opt( + L"-q", + {}, + L"Quiet mode, no output is produced.", + {} + ); + arg_parser_arg_opt do_annotate = arg_parser_arg_opt::arg_parser_arg_opt( + L"--annotate", + {}, + L"Enable translating addresses taken from samples in sample/record mode into source code line numbers.", + {} + ); + arg_parser_arg_opt do_disassembly = arg_parser_arg_opt::arg_parser_arg_opt( + L"--disassemble", + {}, + L"Enable disassemble output on sampling mode. Implies 'annotate'.", + {} + ); + arg_parser_arg_opt do_timeline = arg_parser_arg_opt::arg_parser_arg_opt( + L"-t", + {}, + L"Enable timeline mode (count multiple times with specified interval). Use `-i` to specify timeline interval, and `-n` to specify number of counts.", + {} + ); - flag_type> cores_idx = { - L"-c", - L"--cores", - L"Specify comma separated list of CPU cores, and or ranges of CPU cores, to count on, or one CPU to sample on.", - std::vector {}, - }; - flag_type count_duration = { - L"--timeout", - L"sleep", - LR"(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.)", - -1.0, - }; - flag_type symbol_arg = { - L"--symbol", - L"-s", - L"Filter results for specific symbols (for use with 'record' and 'sample' commands).", - L"", - }; - flag_type record_spawn_delay = { - L"--record_spawn_delay", - L"", - L"Set the waiting time, in milliseconds, before reading process data after spawning it with `record`.", - 1000, - }; - flag_type sample_display_row = { - L"--sample-display-row", - L"", - L"Set how many samples you want to see in the summary (50 by default).", - 50, - }; - flag_type sample_pe_file = { - L"--pe_file", - L"", - L"Specify the PE filename (and path).", - L"", - }; - flag_type sample_image_name = { - L"--image_name", - L"", - L"Specify the image name you want to sample.", - L"", - }; - flag_type sample_pdb_file = { - L"--pdb_file", - L"", - L"Specify the PDB filename (and path), PDB file should directly corresponds to a PE file set with `- - pe_file`.", - L"", - }; - flag_type events_string = { - L"-e", - L"", - LR"( 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`. +#pragma endregion - 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`. +#pragma region Flags with arguments + arg_parser_arg_pos double_dash = 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].", + {}, + -1 + ); - Note: see list of available event names using `list` command.)", - L"", - }; + arg_parser_arg_pos cores_idx = arg_parser_arg_pos::arg_parser_arg_pos( + L"-c", + { L"--cores" }, + L"Specify comma separated list of CPU cores, and or ranges of CPU cores, to count on, or one CPU to sample on.", + {} + ); + + arg_parser_arg_pos count_duration = arg_parser_arg_pos::arg_parser_arg_pos( + L"--timeout", + { L"sleep" }, + L"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.", + {} + ); + arg_parser_arg_pos symbol_arg = arg_parser_arg_pos::arg_parser_arg_pos( + L"--symbol", + { L"-s" }, + L"Filter results for specific symbols (for use with 'record' and 'sample' commands).", + {} + ); + arg_parser_arg_pos record_spawn_delay = arg_parser_arg_pos::arg_parser_arg_pos( + L"--record_spawn_delay", + {}, + L"Set the waiting time, in milliseconds, before reading process data after spawning it with `record`.", + {} + ); + arg_parser_arg_pos sample_display_row = arg_parser_arg_pos::arg_parser_arg_pos( + L"--sample-display-row", + {}, + L"Set how many samples you want to see in the summary (50 by default).", + { L"50" } + ); + arg_parser_arg_pos sample_pe_file = arg_parser_arg_pos::arg_parser_arg_pos( + L"--pe_file", + {}, + L"Specify the PE filename (and path).", + {} + ); + arg_parser_arg_pos sample_image_name = arg_parser_arg_pos::arg_parser_arg_pos( + L"--image_name", + {}, + L"Specify the image name you want to sample.", + {} + ); + arg_parser_arg_pos sample_pdb_file = arg_parser_arg_pos::arg_parser_arg_pos( + L"--pdb_file", + {}, + L"Specify the PDB filename (and path), PDB file should directly corresponds to a PE file set with `--pe_file`.", + {} + ); + arg_parser_arg_pos metric_config = arg_parser_arg_pos::arg_parser_arg_pos( + L"-C", + {}, + L"Provide customized config file which describes metrics.", + {} + ); + arg_parser_arg_pos event_config = arg_parser_arg_pos::arg_parser_arg_pos( + L"-E", + {}, + L"Provide customized config file which describes custom events or provide custom events from the command line.", + {} + ); + arg_parser_arg_pos output_filename = arg_parser_arg_pos::arg_parser_arg_pos( + L"--output", + { L"-o" }, + L"Specify JSON output filename.", + {} + ); + arg_parser_arg_pos output_csv_filename = arg_parser_arg_pos::arg_parser_arg_pos( + L"--output-csv", + {}, + L"Specify CSV output filename. Only with timeline `-t`.", + {} + ); + arg_parser_arg_pos m_cwd = arg_parser_arg_pos::arg_parser_arg_pos( + L"--output-prefix", + { L"--cwd" }, + L"Set current working dir for storing output JSON and CSV file.", + {} + ); + arg_parser_arg_pos drv_config = arg_parser_arg_pos::arg_parser_arg_pos( + L"--config", + {}, + L"Specify configuration parameters.", + {} + ); + arg_parser_arg_pos count_interval = arg_parser_arg_pos::arg_parser_arg_pos( + L"-i", + {}, + L"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).", + {} + ); + arg_parser_arg_pos timeline_count = arg_parser_arg_pos::arg_parser_arg_pos( + L"-i", + {}, + L"Number of consecutive counts in timeline mode (disabled by default).", + {} + ); + arg_parser_arg_pos dmc_idx = arg_parser_arg_pos::arg_parser_arg_pos( + L"--dmc", + {}, + L"Profile on the specified DDR controller. Skip `--dmc` to count on all DMCs.", + {} + ); + arg_parser_arg_pos raw_metrics = 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.", + {} + ); + arg_parser_arg_pos raw_events = 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.", + {} + ); #pragma endregion #pragma region Attributes - COMMAND_CLASS command = COMMAND_CLASS::NO_COMMAND; - wstr_vec arg_array; - -#pragma endregion + COMMAND_CLASS m_command = COMMAND_CLASS::NO_COMMAND; + std::vector m_commands_list = { + &do_list, + &do_test, + &do_help, + &do_version, + &do_detect, + &do_sample, + &do_record, + &do_count, + &do_man + }; + + std::vector m_flags_list = { + &do_json, + &do_kernel, + &do_force_lock, + &sample_display_long, + &do_verbose, + &is_quite, + &do_annotate, + &do_disassembly, + &do_timeline, + &cores_idx, + &count_duration, + &symbol_arg, + &record_spawn_delay, + &sample_display_row, + &sample_pe_file, + &sample_image_name, + &sample_pdb_file, + &metric_config, + &event_config, + &output_filename, + &output_csv_filename, + &m_cwd, + &drv_config, + &count_interval, + &timeline_count, + &dmc_idx, + &raw_metrics, + &raw_events, + &double_dash + }; -#pragma region Not implemented yet - bool do_timeline; - bool do_man; - bool do_export_perf_data; - bool do_cwd = false; // Set current working dir for storing output files - bool report_l3_cache_metric; - bool report_ddr_bw_metric; - uint8_t dmc_idx; - double count_interval; - int count_timeline; - std::wstring man_query_args; + wstr_vec m_arg_array; - std::wstring timeline_output_file; - std::wstring m_cwd; // Current working dir for storing output files - std::map sampling_inverval; //!< [event_index] -> event_sampling_interval - bool m_sampling_with_spe = false; // SPE: User requested sampling with SPE - std::map m_sampling_flags; // SPE: sampling flags #pragma endregion #pragma region Protected Methods protected: - virtual void check_file_path(wstring file_path); void throw_invalid_arg(const std::wstring& arg, const std::wstring& additional_message = L"") const; #pragma endregion - -#pragma region Private Methods -private: - void parse_record_commandline(wstr_vec& raw_args_vect); - void check_sampling_required_args(); - void fill_pdb_and_image_name_if_empty(); - void check_record_required_args(); - void parse_sampling_args(wstr_vec& raw_args_vect); - void parse_event_list(wstring events_string); - void parse_cpu_core(wstr_vec& raw_args_vect, uint8_t MAX_CPU_CORES); - bool try_match_and_set_bool_flag(wstr_vec& raw_args_vect, flag_type& flag); - bool try_match_and_set_arg(wstr_vec& raw_args_vect, arg_type& flag); - bool try_match_arg(wstr_vec& raw_args_vect, arg_base_type& flag); - bool check_timeout_arg(std::wstring number_and_suffix, const std::unordered_map& unit_map); - double convert_timeout_arg_to_seconds(std::wstring number_and_suffix); - void check_next_arg(const wstr_vec& raw_args_vect) const; - void check_flag_value_existance(const wstr_vec& raw_args_vect) const; -#pragma endregion }; \ No newline at end of file diff --git a/parser/main.cpp b/parser/main.cpp index ffba19a..4180b59 100644 --- a/parser/main.cpp +++ b/parser/main.cpp @@ -38,5 +38,5 @@ int wmain( { arg_parser parser; parser.parse(argc, argv); - std::wcout << L"Hello " << (parser.do_annotate.get() ? L"annotate" : L"no annotate") << L" World!\n"; + std::wcout << L"Hello " << (parser.do_annotate.is_set() ? L"annotate" : L"no annotate") << L" World!\n"; } \ No newline at end of file diff --git a/parser/parser.vcxproj b/parser/parser.vcxproj index e47b534..4da2870 100644 --- a/parser/parser.vcxproj +++ b/parser/parser.vcxproj @@ -131,12 +131,12 @@ + - - + diff --git a/parser/parser.vcxproj.filters b/parser/parser.vcxproj.filters index 1cb6855..94dbd71 100644 --- a/parser/parser.vcxproj.filters +++ b/parser/parser.vcxproj.filters @@ -21,7 +21,7 @@ Source Files - + Source Files @@ -29,7 +29,7 @@ Header Files - + Header Files diff --git a/parser/utils.cpp b/parser/utils.cpp deleted file mode 100644 index 907c0d2..0000000 --- a/parser/utils.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -/// -/// Convert time from one of [milliseconds, seconds, minutes, hours, days] to seconds -/// -/// Input number to be converted -/// Unit of input number -/// Map of wstring unit multiplier -double ConvertNumberWithUnit(double number, std::wstring unit, const std::unordered_map& unitConversionMap) -{ - //takes a number and a unit, multiplies the number by a value to obtain number in desired units - auto it = unitConversionMap.find(unit); - double multiplier = it->second; - double result = number * multiplier; - - return result; -} - -std::wstring ReplaceFileExtension(std::wstring filename, std::wstring ext) -{ - size_t index = filename.find_last_of(L"."); - if (index != std::string::npos) - filename = filename.substr(0, index) + L"." + ext; - return filename; -} diff --git a/parser/utils.h b/parser/utils.h deleted file mode 100644 index 5ca29f1..0000000 --- a/parser/utils.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/// -/// Function tokenizes string which contains a range, returning the validity of the string (and the output vector) -/// Example Input: -/// -/// L"0-4" -/// -/// -/// INT -/// Input Range as a wide string -/// Wide Character to seperate range, e.g. L'-' -/// Output Vector -/// -template -_Success_(return) bool TokenizeWideStringofIntRange(_In_ std::wstring Range, _In_ wchar_t Separator, _Out_ std::vector&Output) { - - auto dash_pos = Range.find(Separator); - - if (dash_pos != std::wstring::npos) { - std::wstring startWString = Range.substr(0, dash_pos); - std::wstring endWString = Range.substr(dash_pos + 1); - - if (std::all_of(startWString.begin(), startWString.end(), ::isdigit) && std::all_of(endWString.begin(), endWString.end(), ::isdigit)) { - - T startNum = (T)_wtoi(startWString.c_str()); - T endNum = (T)_wtoi(endWString.c_str()); - - T lowerBound = startNum < endNum ? startNum : endNum; - T upperBound = startNum < endNum ? endNum : startNum; - - for (T i = lowerBound; i <= upperBound; i++) { - Output.push_back((i)); - } - } - else - return false; - } - else - return false; - - return true; -} - - -/// -/// Function tokenizes string and returns vector in INT values. -/// Example string input: -/// -/// L"0,2,3,5" -/// -/// -/// Input WSTRING -/// Delimeter used to tokenize INPUT -/// Vector with tokenized values (is cleared by function) -/// Count of elements tokenized -template -bool TokenizeWideStringOfInts(_In_ std::wstring Input, _In_ const wchar_t Delimiter, _Out_ std::vector& Output) { - static_assert(std::is_integral::value, "Integral type required in Output"); - - std::wstring token; - std::wistringstream ss(Input); - - std::wstring regex_string = L"^[0-9]+\\-[0-9]+$"; - std::wregex r(regex_string); - std::wsmatch match; - - Output.clear(); - while (std::getline(ss, token, Delimiter)) { - if (std::all_of(token.begin(), token.end(), ::isdigit)) - Output.push_back((T)_wtoi(token.c_str())); - else if (std::regex_search(token, match, r)) { //enables the function to accept different input formats, here: ranges e.g 1-3 = 1,2,3 - TokenizeWideStringofIntRange(token, L'-', Output); - } - else - return false; - } - - return true; -} - - -double ConvertNumberWithUnit(double number, std::wstring unit, const std::unordered_map& unitConversionMap); -std::wstring ReplaceFileExtension(std::wstring filename, std::wstring ext);