Skip to content

Commit 7d5347b

Browse files
committed
feat: add print help method
1 parent 024baf5 commit 7d5347b

File tree

5 files changed

+196
-43
lines changed

5 files changed

+196
-43
lines changed

parser/arg-parser-arg.cpp

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,14 @@ bool arg_parser_arg::is_match(const std::wstring& other_arg) const
2121

2222
std::wstring arg_parser_arg::get_help() const
2323
{
24-
return m_name + L" or " + get_alias_string() + L"/n " + m_description;
24+
25+
return L"\t" + get_all_flags_string() + L":\n" + arg_parser_add_wstring_behind_multiline_text(arg_parser_format_string_to_length(m_description, MAX_HELP_WIDTH), L"\t ");
26+
}
27+
28+
std::wstring arg_parser_arg::get_all_flags_string() const
29+
{
30+
if (get_alias_string().empty()) return m_name;
31+
return m_name + L", " + get_alias_string();
2532
}
2633

2734
std::wstring arg_parser_arg::get_usage_text() const
@@ -50,8 +57,11 @@ std::wstring arg_parser_arg::get_alias_string() const
5057
// convert alias vector to wstring
5158
std::wstring alias_string;
5259
for (auto& m_alias : m_aliases) {
53-
alias_string.append(m_alias);
60+
if (m_alias.empty()) continue;
61+
alias_string.append(m_alias + L", ");
5462
}
63+
if (alias_string.find(L", ") != std::wstring::npos) alias_string.erase(alias_string.end() - 2, alias_string.end());
64+
5565
return alias_string;
5666
}
5767

@@ -110,3 +120,64 @@ bool arg_parser_arg::parse(std::vector<std::wstring> arg_vect)
110120
return true;
111121
}
112122

123+
124+
std::wstring arg_parser_add_wstring_behind_multiline_text(const std::wstring& str, const std::wstring& prefix)
125+
{
126+
std::wstring formatted_str;
127+
std::wstring current_line;
128+
std::wistringstream iss(str);
129+
while (getline(iss, current_line, L'\n'))
130+
{
131+
if (current_line.empty())
132+
{
133+
formatted_str += L"\n";
134+
continue;
135+
}
136+
137+
formatted_str += prefix;
138+
formatted_str += current_line;
139+
formatted_str += L"\n";
140+
}
141+
return formatted_str;
142+
}
143+
144+
std::wstring arg_parser_format_string_to_length(const std::wstring& str, size_t max_width)
145+
{
146+
std::wstring formatted_str;
147+
std::wistringstream lines_stream(str);
148+
std::wstring line;
149+
150+
while (std::getline(lines_stream, line, L'\n'))
151+
{
152+
std::wstring current_line;
153+
std::wistringstream word_stream(line);
154+
std::wstring word;
155+
156+
while (word_stream >> word)
157+
{
158+
if (current_line.size() + word.size() + 1 > max_width)
159+
{
160+
formatted_str += current_line + L"\n";
161+
current_line = word + L" ";
162+
}
163+
else
164+
{
165+
current_line += word + L" ";
166+
}
167+
}
168+
169+
if (!current_line.empty())
170+
{
171+
formatted_str += current_line + L"\n\n";
172+
}
173+
}
174+
175+
// Remove the trailing newline, if any
176+
if (!formatted_str.empty() && formatted_str.back() == L'\n\n')
177+
{
178+
formatted_str.pop_back();
179+
formatted_str.pop_back();
180+
}
181+
182+
return formatted_str;
183+
}

parser/arg-parser-arg.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
#include <vector>
44
#include <set>
55
#include <functional>
6+
#include <sstream>
7+
#include <string>
8+
#define MAX_HELP_WIDTH 80
69

710
struct arg_parser_arg {
811
const std::wstring m_name;
@@ -27,6 +30,7 @@ struct arg_parser_arg {
2730
);
2831
bool is_match(const std::wstring& arg) const;
2932
virtual std::wstring get_help() const;
33+
virtual std::wstring get_all_flags_string() const;
3034
virtual std::wstring get_usage_text() const;
3135
arg_parser_arg add_alias(std::wstring new_alias);
3236
int get_arg_count() const;
@@ -61,3 +65,5 @@ class arg_parser_arg_pos : public arg_parser_arg {
6165
) : arg_parser_arg(name, alias, description, default_values, arg_count) {};
6266
};
6367

68+
std::wstring arg_parser_add_wstring_behind_multiline_text(const std::wstring& str, const std::wstring& prefix);
69+
std::wstring arg_parser_format_string_to_length(const std::wstring& str, size_t max_width);

parser/arg-parser.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <vector>
3737
#include <sstream>
3838

39+
3940
arg_parser::arg_parser() {}
4041

4142
void arg_parser::parse(
@@ -95,6 +96,31 @@ void arg_parser::parse(
9596
}
9697
}
9798

99+
void arg_parser::print_help() const
100+
{
101+
std::wcout << L"NAME:\n"
102+
103+
<< L"\twperf - Performance analysis tools for Windows on Arm\n\n"
104+
<< L"\tUsage: wperf <command> [options]\n\n"
105+
<< L"SYNOPSIS:\n\n";
106+
for (auto& command : m_commands_list)
107+
{
108+
std::wcout << L"\t" << command->get_all_flags_string() << L":\n" << command->get_usage_text() << L"\n" ;
109+
}
110+
111+
std::wcout << L"OPTIONS:\n\n";
112+
for (auto& flag : m_flags_list)
113+
{
114+
std::wcout << L" " << flag->get_help() << L"\n";
115+
}
116+
std::wcout << L"EXAMPLES:\n\n";
117+
for (auto& command : m_commands_list)
118+
{
119+
if (command->get_examples().empty()) continue;
120+
std::wcout << L" " << command->get_examples() <<L"\n";
121+
}
122+
}
123+
98124
#pragma region error handling
99125
void arg_parser::throw_invalid_arg(const std::wstring& arg, const std::wstring& additional_message) const
100126
{
@@ -131,3 +157,18 @@ void arg_parser::throw_invalid_arg(const std::wstring& arg, const std::wstring&
131157
}
132158

133159
#pragma endregion
160+
161+
wstring arg_parser_arg_command::get_usage_text() const
162+
{
163+
return arg_parser_add_wstring_behind_multiline_text(arg_parser_format_string_to_length(
164+
m_useage_text + L"\n" + m_description, MAX_HELP_WIDTH), L"\t ");
165+
}
166+
167+
wstring arg_parser_arg_command::get_examples() const
168+
{
169+
std::wstring example_output;
170+
for (auto& example : m_examples) {
171+
example_output += example + L"\n";
172+
}
173+
return arg_parser_add_wstring_behind_multiline_text(arg_parser_format_string_to_length(example_output, MAX_HELP_WIDTH),L"\t");
174+
}

parser/arg-parser.h

Lines changed: 70 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,17 @@ class arg_parser_arg_command : public arg_parser_arg_opt {
6363
const std::wstring name,
6464
const std::vector<std::wstring> alias,
6565
const std::wstring description,
66-
const std::wstring examples,
67-
const COMMAND_CLASS command
68-
) : arg_parser_arg_opt(name, alias, description), m_examples(examples), m_command(command) {};
66+
const std::wstring useage_text,
67+
const COMMAND_CLASS command,
68+
const wstr_vec examples
69+
) : arg_parser_arg_opt(name, alias, description), m_examples(examples), m_command(command), m_useage_text(useage_text) {};
6970
const COMMAND_CLASS m_command = NO_COMMAND;
70-
const std::wstring m_examples;
71+
const wstr_vec m_examples;
72+
const std::wstring m_useage_text;
73+
74+
wstring get_usage_text() const override;
75+
76+
wstring get_examples() const;
7177

7278
};
7379
#pragma region arg structs
@@ -84,72 +90,95 @@ class arg_parser
8490
_In_ const int argc,
8591
_In_reads_(argc) const wchar_t* argv[]
8692
);
93+
void print_help() const;
8794
#pragma endregion
8895

8996
#pragma region Commands
9097
arg_parser_arg_command list_command = arg_parser_arg_command::arg_parser_arg_command(
9198
L"list",
9299
{ L"-l" },
93-
L"List available metrics and events.",
94-
L"",
95-
COMMAND_CLASS::LIST
100+
L"List supported events and metrics. Enable verbose mode for more details.",
101+
L"wperf list [-v] [--json] [--force-lock]",
102+
COMMAND_CLASS::LIST,
103+
{
104+
L"> wperf list -v List all events and metrics available on your host with extended information."
105+
}
96106
);
97-
98107
arg_parser_arg_command test_command = arg_parser_arg_command::arg_parser_arg_command(
99108
L"test",
100109
{ L"" },
101-
L"",
102-
L"",
103-
COMMAND_CLASS::TEST
110+
L"Configuration information about driver and application.",
111+
L"wperf test [--json] [OPTIONS]",
112+
COMMAND_CLASS::TEST,
113+
{}
104114
);
105115
arg_parser_arg_command help_command = arg_parser_arg_command::arg_parser_arg_command(
106116
L"-h",
107117
{ L"--help" },
108-
L"",
109-
L"",
110-
COMMAND_CLASS::HELP
118+
L"Run wperf help command.",
119+
L"wperf help",
120+
COMMAND_CLASS::HELP,
121+
{}
111122
);
112123
arg_parser_arg_command version_command = arg_parser_arg_command::arg_parser_arg_command(
113124
L"--version",
114125
{ L"" },
115-
L"",
116-
L"",
117-
COMMAND_CLASS::VERSION
126+
L"Display version.",
127+
L"wperf --version",
128+
COMMAND_CLASS::VERSION,
129+
{}
118130
);
119131
arg_parser_arg_command detect_command = arg_parser_arg_command::arg_parser_arg_command(
120132
L"detect",
121133
{ L"" },
122-
L"",
123-
L"",
124-
COMMAND_CLASS::DETECT
134+
L"List installed WindowsPerf-like Kernel Drivers (match GUID).",
135+
L"wperf detect [--json] [OPTIONS]",
136+
COMMAND_CLASS::DETECT,
137+
{}
125138
);
126139
arg_parser_arg_command sample_command = arg_parser_arg_command::arg_parser_arg_command(
127140
L"sample",
128141
{ L"" },
129-
L"",
130-
L"",
131-
COMMAND_CLASS::SAMPLE
142+
L"Sampling mode, for determining the frequencies of event occurrences produced by program locations at the function, basic block, and /or instruction levels.",
143+
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]",
144+
COMMAND_CLASS::SAMPLE,
145+
{
146+
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.",
147+
}
132148
);
133149
arg_parser_arg_command record_command = arg_parser_arg_command::arg_parser_arg_command(
134150
L"record",
135151
{ L"" },
136-
L"",
137-
L"",
138-
COMMAND_CLASS::RECORD
152+
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].",
153+
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]",
154+
COMMAND_CLASS::RECORD,
155+
{
156+
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\"."
157+
#ifdef ENABLE_SPE
158+
,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."
159+
#endif
160+
}
139161
);
140162
arg_parser_arg_command count_command = arg_parser_arg_command::arg_parser_arg_command(
141163
L"stat",
142164
{ L"" },
143-
L"",
144-
L"",
145-
COMMAND_CLASS::STAT
165+
L"Counting mode, for obtaining aggregate counts of occurrences of special events.",
166+
L"wperf stat [-e] [-m] [-t] [-i] [-n] [-c] [-C] [-E] [-k] [--dmc] [-q] [--json] [--output][--config] [--force-lock] --COMMAND[ARGS]",
167+
COMMAND_CLASS::STAT,
168+
{
169+
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.",
170+
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.",
171+
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."
172+
}
146173
);
147174
arg_parser_arg_command man_command = arg_parser_arg_command::arg_parser_arg_command(
148-
L"stat",
175+
L"man",
149176
{ L"" },
150-
L"",
151-
L"",
152-
COMMAND_CLASS::MAN
177+
L"Plain text information about one or more specified event(s), metric(s), and or group metric(s).",
178+
L"wperf man [--json]",
179+
COMMAND_CLASS::MAN,
180+
{
181+
}
153182
);
154183

155184
#pragma endregion
@@ -344,19 +373,21 @@ class arg_parser
344373

345374
COMMAND_CLASS m_command = COMMAND_CLASS::NO_COMMAND;
346375
std::vector<arg_parser_arg_command*> m_commands_list = {
347-
&list_command,
348-
&test_command,
349376
&help_command,
350377
&version_command,
351-
&detect_command,
352378
&sample_command,
353-
&record_command,
354379
&count_command,
380+
&record_command,
381+
&list_command,
382+
&test_command,
383+
&detect_command,
355384
&man_command
356385
};
357386

358387
std::vector<arg_parser_arg*> m_flags_list = {
359388
&json_opt,
389+
&metrics_arg,
390+
&events_arg,
360391
&kernel_opt,
361392
&force_lock_opt,
362393
&sample_display_long_opt,
@@ -382,8 +413,6 @@ class arg_parser
382413
&interval_arg,
383414
&iteration_arg,
384415
&dmc_arg,
385-
&metrics_arg,
386-
&events_arg,
387416
&extra_args_arg
388417
};
389418

@@ -395,4 +424,5 @@ class arg_parser
395424
protected:
396425
void throw_invalid_arg(const std::wstring& arg, const std::wstring& additional_message = L"") const;
397426
#pragma endregion
398-
};
427+
};
428+

parser/main.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,10 @@ int wmain(
3838
{
3939
arg_parser parser;
4040
parser.parse(argc, argv);
41-
std::wcout << L"Hello " << (parser.annotate_opt.is_set() ? L"annotate" : L"no annotate") << L" World!\n";
41+
if (parser.m_command == COMMAND_CLASS::HELP)
42+
{
43+
parser.print_help();
44+
return 0;
45+
}
46+
std::wcout << L"Hello " << argv[1]<< (parser.annotate_opt.is_set() ? L"annotate" : L"no annotate") << L" World!\n";
4247
}

0 commit comments

Comments
 (0)