Skip to content

Commit 6324991

Browse files
committed
[client] move some generic functions to client_utils library
1 parent b04359e commit 6324991

17 files changed

+553
-97
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ add_subdirectory(lib)
6363

6464
if(ENABLE_CLIENT)
6565
add_subdirectory(parser)
66+
add_subdirectory(client_utils)
6667
add_subdirectory(client)
6768
endif(ENABLE_CLIENT)
6869

client/CMakeLists.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ add_library(hawktracer_client
44
callgrind_converter.cpp
55
chrome_trace_converter.cpp
66
converter.cpp
7-
tcp_client_stream.cpp
87
tracepoint_map.cpp)
98

109
target_include_directories(hawktracer_client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)
1110

1211
add_executable(hawktracer-converter main.cpp)
1312

14-
target_link_libraries(hawktracer_client hawktracer_parser)
13+
target_link_libraries(hawktracer_client hawktracer_parser client_utils)
1514
target_link_libraries(hawktracer-converter hawktracer_client)
1615

1716
install(TARGETS hawktracer-converter

client/main.cpp

Lines changed: 22 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,17 @@
11
#include "callgrind_converter.hpp"
22
#include "chrome_trace_converter.hpp"
3-
#include "tcp_client_stream.hpp"
4-
#include <hawktracer/parser/file_stream.hpp>
3+
54
#include <hawktracer/parser/protocol_reader.hpp>
65
#include <hawktracer/parser/make_unique.hpp>
76

7+
#include "client_utils/command_line_parser.hpp"
8+
#include "client_utils/stream_factory.hpp"
9+
810
#include <iostream>
9-
#include <cstring>
1011
#include <map>
1112

1213
using namespace HawkTracer;
13-
14-
bool file_exists(const char* name)
15-
{
16-
std::ifstream f(name);
17-
return f.good();
18-
}
19-
20-
bool scan_ip_address(const std::string& address, std::string& out_ip, uint16_t& out_port, uint16_t default_port)
21-
{
22-
unsigned n1, n2, n3, n4, port;
23-
int num = sscanf(address.c_str(), "%u.%u.%u.%u:%u", &n1, &n2, &n3, &n4, &port);
24-
25-
if (num >= 4)
26-
{
27-
out_ip = address.substr(0, address.find(':'));
28-
out_port = (num == 5) ? (uint16_t)port : default_port;
29-
return true;
30-
}
31-
return false;
32-
}
14+
using ClientUtils::CommandLineParser;
3315

3416
std::string create_output_path(const char* path)
3517
{
@@ -52,16 +34,6 @@ std::string supported_formats(std::map<std::string, std::unique_ptr<client::Conv
5234
return supported_formats;
5335
}
5436

55-
void print_help(const char* app_name, std::map<std::string, std::unique_ptr<client::Converter>>& formats)
56-
{
57-
std::cout << "usage: " << app_name << " [OPTION]..." << std::endl
58-
<< " --format supported formats are: " << supported_formats(formats) << "(default is chrome-tracing)" << std::endl
59-
<< " --source data source description (either filename, or server address)" << std::endl
60-
<< " --output an output Chrome Tracing Json file" << std::endl
61-
<< " --map comma-separated list of map files" << std::endl
62-
<< " --help print this text" << std::endl;
63-
}
64-
6537
void init_supported_formats(std::map<std::string, std::unique_ptr<client::Converter>>& formats)
6638
{
6739
formats["chrome-tracing"] = parser::make_unique<client::ChromeTraceConverter>();
@@ -70,68 +42,31 @@ void init_supported_formats(std::map<std::string, std::unique_ptr<client::Conver
7042

7143
int main(int argc, char** argv)
7244
{
73-
std::string format = "chrome-tracing";
74-
std::string output_path = "hawktracer-trace-%d-%m-%Y-%H_%M_%S.httrace";
75-
std::string source;
76-
std::string map_files;
7745
std::map<std::string, std::unique_ptr<client::Converter>> formats;
7846

7947
init_supported_formats(formats);
8048

81-
for (int i = 1; i < argc; i++)
49+
CommandLineParser parser("--", argv[0]);
50+
parser.register_option("format", CommandLineParser::OptionInfo(false, false, "Output format. Supported formats: " + supported_formats(formats)));
51+
parser.register_option("output", CommandLineParser::OptionInfo(false, false, "Output file"));
52+
parser.register_option("source", CommandLineParser::OptionInfo(false, true, "Data source description (either filename, or server address)"));
53+
parser.register_option("map", CommandLineParser::OptionInfo(false, false, "Comma-separated list of map files"));
54+
parser.register_option("help", CommandLineParser::OptionInfo(true, false, "Print this help"));
55+
56+
if (!parser.parse(argc, argv) || parser.has_value("help"))
8257
{
83-
if (strcmp(argv[i], "--format") == 0 && i < argc - 1)
84-
{
85-
format = argv[++i];
86-
}
87-
else if (strcmp(argv[i], "--output") == 0 && i < argc - 1)
88-
{
89-
output_path = argv[++i];
90-
}
91-
else if (strcmp(argv[i], "--source") == 0 && i < argc - 1)
92-
{
93-
source = argv[++i];
94-
}
95-
else if (strcmp(argv[i], "--map") == 0 && i < argc - 1)
96-
{
97-
map_files = argv[++i];
98-
}
99-
else
100-
{
101-
int ret = 0;
102-
if (strcmp(argv[i], "--help") != 0)
103-
{
104-
std::cerr << "unrecognized option '" << argv[i] << "'" << std::endl;
105-
ret = 1;
106-
}
107-
print_help(argv[0], formats);
108-
return ret;
109-
}
110-
}
111-
112-
if (source.empty())
113-
{
114-
std::cerr << "--source parameter is mandatory" << std::endl;
115-
print_help(argv[0], formats);
58+
parser.print_help(std::cerr);
11659
return 1;
11760
}
11861

119-
std::unique_ptr<parser::Stream> stream;
120-
std::string ip;
121-
uint16_t port;
122-
bool is_network = false;
123-
if (file_exists(source.c_str()))
124-
{
125-
stream = parser::make_unique<parser::FileStream>(source);
126-
}
127-
else if (scan_ip_address(source, ip, port, 8765))
128-
{
129-
stream = parser::make_unique<client::TCPClientStream>(ip, port);
130-
is_network = true;
131-
}
132-
else
62+
std::string format = parser.get_value("format", "chrome-tracing");
63+
std::string output_path = parser.get_value("output", "hawktracer-trace-%d-%m-%Y-%H_%M_%S.httrace");
64+
std::string source = parser.get_value("source", "");
65+
std::string map_files = parser.get_value("map", "");
66+
67+
std::unique_ptr<parser::Stream> stream = ClientUtils::make_stream_from_string(source);
68+
if (!stream)
13369
{
134-
std::cerr << "Invalid stream: " << source << std::endl;
13570
return 1;
13671
}
13772

@@ -176,7 +111,7 @@ int main(int argc, char** argv)
176111
return 1;
177112
}
178113

179-
if (is_network)
114+
if (stream->is_continuous())
180115
{
181116
std::cout << "Hit [Enter] to finish the trace..." << std::endl;
182117
getchar();

client_utils/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
add_library(client_utils
2+
STATIC
3+
command_line_parser.cpp
4+
stream_factory.cpp
5+
tcp_client_stream.cpp)
6+
7+
target_link_libraries(client_utils hawktracer_parser)
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
#include "command_line_parser.hpp"
2+
3+
#include <iostream>
4+
#include <iomanip>
5+
#include <cstring>
6+
#include <cassert>
7+
8+
namespace HawkTracer
9+
{
10+
namespace ClientUtils
11+
{
12+
13+
CommandLineParser::CommandLineParser(std::string prefix, std::string app_name) :
14+
_prefix(std::move(prefix)),
15+
_app_name(std::move(app_name))
16+
{
17+
assert(_prefix.length() > 0);
18+
}
19+
20+
void CommandLineParser::register_option(std::string name, OptionInfo option_info)
21+
{
22+
if (option_info.is_flag && option_info.is_mandatory)
23+
{
24+
std::cerr << "Unable to register option " << name
25+
<< ": option can't be flag and mandatory at the same time" << std::endl;
26+
}
27+
else
28+
{
29+
_registered_options.insert(std::make_pair(std::move(name), std::move(option_info)));
30+
}
31+
}
32+
33+
size_t CommandLineParser::_get_max_option_length(size_t value_help_length) const
34+
{
35+
size_t max_option_length = 0;
36+
for (const auto& option : _registered_options)
37+
{
38+
size_t length = option.first.length();
39+
if (!option.second.is_flag)
40+
{
41+
length += value_help_length;
42+
}
43+
if (length > max_option_length)
44+
{
45+
max_option_length = length;
46+
}
47+
}
48+
49+
return max_option_length;
50+
}
51+
52+
void CommandLineParser::print_help(std::ostream& stream) const
53+
{
54+
std::string value_help = " <VALUE>";
55+
size_t max_option_length = _get_max_option_length(value_help.length());
56+
57+
stream << "usage: " << _app_name << " [OPTION]..." << std::endl;
58+
for (const auto& option : _registered_options)
59+
{
60+
stream << " " << _prefix << std::left << std::setw(max_option_length)
61+
<< option.first + (option.second.is_flag ? "" : value_help) << " ";
62+
63+
if (option.second.is_mandatory)
64+
{
65+
stream << "[MANDATORY] ";
66+
}
67+
stream << option.second.help_msg << std::endl << std::setw(0);
68+
}
69+
}
70+
71+
std::string CommandLineParser::get_value(const std::string& option, std::string default_value) const
72+
{
73+
auto it = _actual_options.find(option);
74+
return (it != _actual_options.end()) ? it->second : default_value;
75+
}
76+
77+
bool CommandLineParser::has_value(const std::string& option) const
78+
{
79+
return _actual_options.find(option) != _actual_options.end();
80+
}
81+
82+
bool CommandLineParser::_is_option_valid(const char* option) const
83+
{
84+
return strlen(option) > _prefix.length() && !strncmp(option, _prefix.c_str(), _prefix.length());
85+
}
86+
87+
bool CommandLineParser::_validate_user_options() const
88+
{
89+
for (const auto& option : _registered_options)
90+
{
91+
if (option.second.is_mandatory && _actual_options.find(option.first) == _actual_options.end())
92+
{
93+
std::cerr << "Option " << _prefix << option.first << " must be specified" << std::endl;
94+
return false;
95+
}
96+
}
97+
98+
return true;
99+
}
100+
101+
bool CommandLineParser::parse(int argc, char** argv)
102+
{
103+
for (int i = 1; i < argc; i++)
104+
{
105+
if (!_is_option_valid(argv[i]))
106+
{
107+
std::cerr << "Invalid option: " << argv[i] << std::endl;
108+
return false;
109+
}
110+
111+
std::string option = argv[i] + _prefix.length();
112+
113+
auto it = _registered_options.find(option);
114+
if (it == _registered_options.end())
115+
{
116+
std::cerr << "Unknown option: " << argv[i] << std::endl;
117+
return false;
118+
}
119+
120+
OptionInfo& info = it->second;
121+
std::string value;
122+
if (info.is_flag)
123+
{
124+
// leave the value empty
125+
}
126+
else if (i == argc - 1)
127+
{
128+
std::cerr << argv[i] << " requires a value" << std::endl;
129+
return false;
130+
}
131+
else
132+
{
133+
value = argv[++i];
134+
}
135+
136+
_actual_options.insert(std::make_pair(std::move(option), std::move(value)));
137+
}
138+
139+
return _validate_user_options();
140+
}
141+
142+
} // namespace ClientUtils
143+
} // namespace HawkTracer
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#ifndef HAWKTRACER_CLIENT_UTILS_COMMAND_LINE_PARSER_HPP
2+
#define HAWKTRACER_CLIENT_UTILS_COMMAND_LINE_PARSER_HPP
3+
4+
#include <unordered_map>
5+
#include <string>
6+
7+
namespace HawkTracer
8+
{
9+
namespace ClientUtils
10+
{
11+
12+
class CommandLineParser
13+
{
14+
public:
15+
struct OptionInfo
16+
{
17+
OptionInfo(bool is_flag, bool is_mandatory, std::string help_msg) :
18+
help_msg(std::move(help_msg)), is_flag(is_flag), is_mandatory(is_mandatory) {}
19+
20+
std::string help_msg;
21+
bool is_flag;
22+
bool is_mandatory;
23+
};
24+
25+
CommandLineParser(std::string prefix, std::string app_name);
26+
27+
void register_option(std::string name, OptionInfo option_info);
28+
bool parse(int argc, char** argv);
29+
30+
void print_help(std::ostream& stream) const;
31+
32+
std::string get_value(const std::string& option, std::string default_value) const;
33+
bool has_value(const std::string& option) const;
34+
35+
private:
36+
size_t _get_max_option_length(size_t value_help_length) const;
37+
bool _is_option_valid(const char* option) const;
38+
bool _validate_user_options() const;
39+
40+
std::unordered_map<std::string, OptionInfo> _registered_options;
41+
std::unordered_map<std::string, std::string> _actual_options;
42+
const std::string _prefix;
43+
const std::string _app_name;
44+
};
45+
46+
} // namespace ClientUtils
47+
} // namespace HawkTracer
48+
49+
#endif // HAWKTRACER_CLIENT_UTILS_COMMAND_LINE_PARSER_HPP

0 commit comments

Comments
 (0)