1 // Copyright (C) 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <getopt.h>
16 
17 #include <ditto/embedded_benchmarks.h>
18 
19 #include <ditto/arg_parser.h>
20 
21 namespace dittosuite {
22 
ArgToResultsOutput(const std::string_view optarg)23 ResultsOutput ArgToResultsOutput(const std::string_view optarg) {
24   if (optarg == "report" || optarg == "0") {
25     return ResultsOutput::kReport;
26   }
27   if (optarg == "csv" || optarg == "1") {
28     return ResultsOutput::kCsv;
29   }
30   if (optarg == "pb" || optarg == "2") {
31     return ResultsOutput::kPb;
32   }
33   if (optarg == "null" || optarg == "-1") {
34     return ResultsOutput::kNull;
35   }
36   return ResultsOutput::kReport;  // by default, the results output is the report (= 0)
37 }
38 
ArgToLogStream(const std::string_view optarg)39 LogStream ArgToLogStream(const std::string_view optarg) {
40   if (optarg == "logcat" || optarg == "1") {
41 #ifdef __ANDROID__
42     return LogStream::kLogcat;
43 #else
44     PLOGF("Cannot set log stream as logcat outside of Android");
45 #endif
46   }
47   return LogStream::kStdout;  // by default, the log stream is stdout
48 }
49 
ArgToLogLevel(const std::string_view optarg)50 LogLevel ArgToLogLevel(const std::string_view optarg) {
51   if (optarg == "VERBOSE" || optarg == "5") {
52     return LogLevel::kVerbose;
53   }
54   if (optarg == "DEBUG" || optarg == "4") {
55     return LogLevel::kDebug;
56   }
57   if (optarg == "INFO" || optarg == "3") {
58     return LogLevel::kInfo;
59   }
60   if (optarg == "WARNING" || optarg == "2") {
61     return LogLevel::kWarning;
62   }
63   if (optarg == "ERROR" || optarg == "1") {
64     return LogLevel::kError;
65   }
66   if (optarg == "FATAL" || optarg == "0") {
67     return LogLevel::kFatal;
68   }
69   return LogLevel::kInfo;  // by default, the log level is info
70 }
71 
PrintHelpAndExit(std::string_view program_name,const std::string & help_arg)72 void PrintHelpAndExit(std::string_view program_name, const std::string& help_arg) {
73   if (help_arg.compare("workloads") == 0) {
74     std::cerr << "The following workloads are available for execution:\n";
75     for (const auto& c : ditto_static_config) {
76       std::cerr << "  - " << c.first << '\n';
77     }
78   } else {
79     std::cerr << "Usage: " << program_name << " [OPTION]... [FILE]\n"
80               << "Benchmarking tool for generic workloads.\n\n"
81 
82               << "  -w, --workload[=WORKLOAD]"
83               << " The workload to run. Full list with \"-h workloads\".\n"
84 
85               << "  -f, --format[=FMT]" << "\tresults output format, where FMT can be one of:\n"
86               << "\t\t\t  - report (or 0, default): human readable summary;\n"
87               << "\t\t\t  - csv (or 1): for comma-separated detailed stats;\n"
88               << "\t\t\t  - pb (or 2): protocol-buffer text;\n"
89               << "\t\t\t  - null (-1): do not print.\n"
90 
91               << "  -p, --param[=PAR]..."
92               << "\tif the benchmark is parametric, all the parameters can be passed\n"
93               << "\t\t\tthrough PAR (comma separated)\n"
94 
95               << "  -l, --log[=LOG]" << "\toutput stream for the log messages.\n"
96               << "\t\t\tLOG can be one of: stdout (or 0, default), logcat (or 1)\n"
97 
98               << "  -v, --verbosity[=VER]" << "\toutput messages verbosity.\n"
99               << "\t\t\tVER can be one of: VERBOSE (or 5), DEBUG (or 4), INFO (or 3, default),\n"
100               << "\t\t\tWARNING (or 2), ERROR (or 1), FATAL (or 0)\n"
101 
102               << "  -h, --help" << "\t\tdisplay this help and exit\n\n";
103   }
104 
105   exit(EXIT_SUCCESS);
106 }
107 
ParseArguments(int argc,char ** argv)108 CmdArguments ParseArguments(int argc, char** argv) {
109   CmdArguments arguments;
110   bool is_embedded = false;
111   std::string help_arg;
112 
113   while (true) {
114     int option_index = 0;
115     static struct option long_options[] = {{"workload", required_argument, 0, 'w'},
116                                            {"format", required_argument, 0, 'f'},
117                                            {"param", required_argument, 0, 'p'},
118                                            {"log", required_argument, 0, 'l'},
119                                            {"verbosity", required_argument, 0, 'v'},
120                                            {"help", optional_argument, 0, 'h'},
121                                            {0, 0, 0, 0}};
122 
123     int c = getopt_long(argc, argv, "w:f:l:v:p:h::", long_options, &option_index);
124     if (c == -1) break;
125 
126     switch (c) {
127       case 'w':
128         arguments.embedded_benchmark = optarg;
129         is_embedded = true;
130         break;
131       case 'f':
132         arguments.results_output = ArgToResultsOutput(optarg);
133         break;
134       case 'l':
135         dittosuite::Logger::GetInstance().SetLogStream(ArgToLogStream(optarg));
136         break;
137       case 'v':
138         dittosuite::Logger::GetInstance().SetLogLevel(ArgToLogLevel(optarg));
139         break;
140       case 'p': {
141         char* token = strtok(optarg, ",");
142         while (token != nullptr) {
143           arguments.parameters.push_back(token);
144           token = strtok(nullptr, ",");
145         }
146         break;
147       }
148       case 'h':
149         if (!optarg && optind < argc    // make sure optind is valid
150             && nullptr != argv[optind]  // make sure it's not a null string
151             && '\0' != argv[optind][0]  // ... or an empty string
152             && '-' != argv[optind][0]   // ... or another option
153         ) {
154           help_arg = argv[optind++];
155         }
156         [[fallthrough]];
157       default: {
158         PrintHelpAndExit(argv[0], help_arg);
159         break;
160       }
161     }
162   }
163 
164   if (is_embedded && optind < argc) {
165     LOGE("Unexpected both embedded benchmark and .ditto file");
166     PrintHelpAndExit(argv[0], help_arg);
167   }
168   if (!is_embedded) {
169     if (optind >= argc) {
170       LOGE("Expected either embedded benchmark or .ditto file");
171       PrintHelpAndExit(argv[0], help_arg);
172     } else {
173       arguments.file_path = argv[optind];
174     }
175   }
176 
177   return arguments;
178 }
179 
180 }  // namespace dittosuite
181