blob: 9cec3c46027f8ae8389f1628c312b428fce4b1bf [file] [log] [blame]
Anders Lewisa7b0f882017-07-24 20:01:13 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Anders Lewisa98a5fb2017-08-09 16:52:19 -070017#include <err.h>
Anders Lewisa7b0f882017-07-24 20:01:13 -070018#include <getopt.h>
19#include <math.h>
20#include <sys/resource.h>
21
22#include <map>
23#include <mutex>
24#include <sstream>
25#include <string>
26#include <vector>
27
28#include "android-base/strings.h"
29#include <benchmark/benchmark.h>
30#include <tinyxml2.h>
31#include "util.h"
32
33std::map<std::string, benchmark_func_t> g_str_to_func;
34
35std::mutex g_map_lock;
36
37static struct option g_long_options[] =
38{
39 {"bionic_cpu", required_argument, 0, 'c'},
40 {"bionic_xml", required_argument, 0, 'x'},
41 {"bionic_iterations", required_argument, 0, 'i'},
42 {"bionic_extra", required_argument, 0, 'a'},
43 {"help", no_argument, 0, 'h'},
44 {0, 0, 0, 0},
45};
46
47typedef std::vector<std::vector<int>> args_vector_t;
48
49void Usage() {
50 printf("Usage:\n");
51 printf("bionic_benchmarks [--bionic_cpu=<cpu_to_isolate>] [--bionic_xml=<path_to_xml>]\n");
52 printf(" [--bionic_iterations=<num_iter>]\n");
Anders Lewisa98a5fb2017-08-09 16:52:19 -070053 printf(" [--bionic_extra=\"<fn_name> <arg1> <arg 2> ... ]\n");
Anders Lewisa7b0f882017-07-24 20:01:13 -070054 printf(" [<Google benchmark flags>]\n");
55 printf("Google benchmark flags:\n");
56
57 int fake_argc = 2;
58 char argv0[] = "bionic_benchmarks";
59 char argv1[] = "--help";
60 char* fake_argv[3] {argv0, argv1, NULL};
61 benchmark::Initialize(&fake_argc, fake_argv);
Anders Lewisa98a5fb2017-08-09 16:52:19 -070062 exit(1);
Anders Lewisa7b0f882017-07-24 20:01:13 -070063}
64
65// This function removes any bionic benchmarks command line arguments by checking them
66// against g_long_options. It fills new_argv with the filtered args.
67void SanitizeOpts(int argc, char** argv, std::vector<char*>* new_argv) {
68 // TO THOSE ADDING OPTIONS: This currently doesn't support optional arguments.
69 (*new_argv)[0] = argv[0];
70 for (int i = 1; i < argc; ++i) {
71 char* optarg = argv[i];
72 size_t opt_idx = 0;
73
74 // Iterate through g_long_options until either we hit the end or we have a match.
75 for (opt_idx = 0; g_long_options[opt_idx].name &&
76 strncmp(g_long_options[opt_idx].name, optarg + 2,
77 strlen(g_long_options[opt_idx].name)); ++opt_idx) {
78 }
79
80 if (!g_long_options[opt_idx].name) {
81 new_argv->push_back(optarg);
82 } else {
83 if (g_long_options[opt_idx].has_arg == required_argument) {
84 // If the arg was passed in with an =, it spans one char *.
85 // Otherwise, we skip a spot for the argument.
86 if (!strchr(optarg, '=')) {
87 i++;
88 }
89 }
90 }
91 }
92 new_argv->push_back(0);
93}
94
95bench_opts_t ParseOpts(int argc, char** argv) {
96 bench_opts_t opts;
97 int opt;
98 int option_index = 0;
99
100 opts.cpu_to_lock = LONG_MAX;
101 opts.num_iterations = 0;
102
103 // To make this parser handle the benchmark options silently:
104 extern int opterr;
105 opterr = 0;
106
107 while ((opt = getopt_long(argc, argv, "c:x:i:a:h", g_long_options, &option_index)) != -1) {
108 if (opt == -1) {
109 break;
110 }
111 switch (opt) {
112 case 'c':
113 if (*optarg) {
114 char* check_null;
115 opts.cpu_to_lock = strtol(optarg, &check_null, 10);
116 if (*check_null) {
Anders Lewisa98a5fb2017-08-09 16:52:19 -0700117 errx(1, "ERROR: Args %s is not a valid integer.", optarg);
Anders Lewisa7b0f882017-07-24 20:01:13 -0700118 }
119 } else {
120 printf("ERROR: no argument specified for bionic_cpu\n");
121 Usage();
122 }
123 break;
124 case 'x':
125 if (*optarg) {
126 opts.xmlpath = optarg;
127 } else {
128 printf("ERROR: no argument specified for bionic_xml\n");
129 Usage();
130 }
131 break;
132 case 'a':
133 if (*optarg) {
134 opts.extra_benchmarks.push_back(optarg);
135 } else {
136 printf("ERROR: no argument specified for bionic_extra\n");
137 Usage();
138 }
139 break;
140 case 'i':
141 if (*optarg){
142 char* check_null;
143 opts.num_iterations = strtol(optarg, &check_null, 10);
Anders Lewisa98a5fb2017-08-09 16:52:19 -0700144 if (*check_null != '\0' or opts.num_iterations < 0) {
145 errx(1, "ERROR: Args %s is not a valid number of iterations.", optarg);
Anders Lewisa7b0f882017-07-24 20:01:13 -0700146 }
147 } else {
148 printf("ERROR: no argument specified for bionic_iterations\n");
149 Usage();
150 }
151 break;
152 case 'h':
153 Usage();
154 break;
155 case '?':
156 break;
157 default:
Anders Lewisa98a5fb2017-08-09 16:52:19 -0700158 exit(1);
Anders Lewisa7b0f882017-07-24 20:01:13 -0700159 }
160 }
161 return opts;
162}
163
164// This is a wrapper for every function call for per-benchmark cpu pinning.
165void LockAndRun(benchmark::State& state, benchmark_func_t func_to_bench, long cpu_to_lock) {
166 if (cpu_to_lock != LONG_MAX) LockToCPU(cpu_to_lock);
167 // To avoid having to link against Google benchmarks in libutil,
168 // benchmarks are kept without parameter information, necessitating this cast.
169 reinterpret_cast<void(*) (benchmark::State&)>(func_to_bench)(state);
170}
171
172args_vector_t* ResolveArgs(args_vector_t* to_populate, std::string args,
173 std::map<std::string, args_vector_t>& args_shorthand) {
174 // args is either a space-separated list of ints or a macro name.
175 // To ease formatting in XML files, args is left and right trimmed.
176 if (args_shorthand.count(args)) {
177 return &args_shorthand[args];
178 }
179 to_populate->push_back(std::vector<int>());
180 std::stringstream sstream(args);
181 std::string argstr;
182 while (sstream >> argstr) {
183 char* check_null;
184 int converted = static_cast<int>(strtol(argstr.c_str(), &check_null, 10));
185 if (*check_null) {
Anders Lewisa98a5fb2017-08-09 16:52:19 -0700186 errx(1, "ERROR: Args str %s contains an invalid macro or int.", args.c_str());
Anders Lewisa7b0f882017-07-24 20:01:13 -0700187 }
188 (*to_populate)[0].push_back(converted);
189 }
190 return to_populate;
191}
192
193void RegisterGoogleBenchmarks(bench_opts_t primary_opts, bench_opts_t secondary_opts,
194 std::string fn_name, args_vector_t* run_args) {
195 if (g_str_to_func.find(fn_name) == g_str_to_func.end()) {
Anders Lewisa98a5fb2017-08-09 16:52:19 -0700196 errx(1, "ERROR: No benchmark for function %s", fn_name.c_str());
Anders Lewisa7b0f882017-07-24 20:01:13 -0700197 }
198 long iterations_to_use = primary_opts.num_iterations ? primary_opts.num_iterations :
199 secondary_opts.num_iterations;
200 int cpu_to_use = INT_MAX;
201 if (primary_opts.cpu_to_lock != INT_MAX) {
202 cpu_to_use = primary_opts.cpu_to_lock;
203
204 } else if (secondary_opts.cpu_to_lock != INT_MAX) {
205 cpu_to_use = secondary_opts.cpu_to_lock;
206 }
207
208 for (std::vector<int> args : (*run_args)) {
209 auto registration = benchmark::RegisterBenchmark(fn_name.c_str(), LockAndRun,
210 g_str_to_func.at(fn_name),
211 cpu_to_use)->Args(args);
212 if (iterations_to_use > 0) {
213 registration->Iterations(iterations_to_use);
214 }
215 }
216}
217
218void RegisterCliBenchmarks(bench_opts_t cmdline_opts,
219 std::map<std::string, args_vector_t>& args_shorthand) {
220 // Register any of the extra benchmarks that were specified in the options.
221 args_vector_t arg_vector;
222 args_vector_t* run_args = &arg_vector;
223 for (std::string extra_fn : cmdline_opts.extra_benchmarks) {
224 android::base::Trim(extra_fn);
225 size_t first_space_pos = extra_fn.find(" ");
226 std::string fn_name = extra_fn.substr(0, first_space_pos);
227 std::string cmd_args;
228 if (first_space_pos != std::string::npos) {
229 cmd_args = extra_fn.substr(extra_fn.find(" ") + 1);
230 } else {
231 cmd_args = "";
232 }
233 run_args = ResolveArgs(run_args, cmd_args, args_shorthand);
234 RegisterGoogleBenchmarks(bench_opts_t(), cmdline_opts, fn_name, run_args);
235
236 run_args = &arg_vector;
237 arg_vector.clear();
238 }
239}
240
241int RegisterXmlBenchmarks(bench_opts_t cmdline_opts,
242 std::map<std::string, args_vector_t>& args_shorthand) {
243 // Structure of the XML file:
244 // - Element "fn" Function to benchmark.
245 // - - Element "iterations" Number of iterations to run. Leaving this blank uses
246 // Google benchmarks' convergence heuristics.
247 // - - Element "cpu" CPU to isolate to, if any.
248 // - - Element "args" Whitespace-separated list of per-function integer arguments, or
249 // one of the macros defined in util.h.
250 tinyxml2::XMLDocument doc;
251 if (doc.LoadFile(cmdline_opts.xmlpath.c_str()) != tinyxml2::XML_NO_ERROR) {
252 doc.PrintError();
253 return doc.ErrorID();
254 }
255
256 // Read and register the functions.
257 tinyxml2::XMLNode* fn = doc.FirstChildElement("fn");
258 args_vector_t arg_vector;
259 args_vector_t* run_args = &arg_vector;
260 while (fn) {
261 auto fn_elem = fn->FirstChildElement("name");
262 if (!fn_elem) {
Anders Lewisa98a5fb2017-08-09 16:52:19 -0700263 errx(1, "ERROR: Malformed XML entry: missing name element.");
Anders Lewisa7b0f882017-07-24 20:01:13 -0700264 }
265 std::string fn_name = fn_elem->GetText();
266 if (fn_name.empty()) {
Anders Lewisa98a5fb2017-08-09 16:52:19 -0700267 errx(1, "ERROR: Malformed XML entry: error parsing name text.");
Anders Lewisa7b0f882017-07-24 20:01:13 -0700268 }
269 auto* xml_args = fn->FirstChildElement("args");
270 run_args = ResolveArgs(run_args, xml_args ? android::base::Trim(xml_args->GetText()) : "",
271 args_shorthand);
272
273 // XML values for CPU and iterations take precedence over those passed in via CLI.
274 bench_opts_t xml_opts{};
275 auto* num_iterations_elem = fn->FirstChildElement("iterations");
276 if (num_iterations_elem) {
277 int temp;
278 num_iterations_elem->QueryIntText(&temp);
279 xml_opts.num_iterations = temp;
280 } else {
281 xml_opts.num_iterations = 0;
282 }
283 auto* cpu_to_lock_elem = fn->FirstChildElement("cpu");
284 if (cpu_to_lock_elem) {
285 int temp;
286 cpu_to_lock_elem->QueryIntText(&temp);
287 xml_opts.cpu_to_lock = temp;
288 } else {
289 xml_opts.cpu_to_lock = INT_MAX;
290 }
291
292 RegisterGoogleBenchmarks(xml_opts, cmdline_opts, fn_name, run_args);
293
294 fn = fn->NextSibling();
295 run_args = &arg_vector;
296 arg_vector.clear();
297 }
298 return 0;
299}
300
301std::map<std::string, args_vector_t> GetShorthand() {
302 std::map<std::string, args_vector_t> args_shorthand {
303 {"AT_ALIGNED_TWOBUF", args_vector_t{ {8, 0, 0},
Anders Lewisac4f4b42017-08-08 18:29:51 -0700304 {64, 0, 0},
305 {512, 0, 0},
306 {1 * KB, 0, 0},
307 {8 * KB, 0, 0},
308 {16 * KB, 0, 0},
309 {32 * KB, 0, 0},
310 {64 * KB, 0, 0} }},
Anders Lewisa7b0f882017-07-24 20:01:13 -0700311 {"AT_ALIGNED_ONEBUF", args_vector_t{ {(8), 0},
Anders Lewisac4f4b42017-08-08 18:29:51 -0700312 {(64), 0},
313 {(512), 0},
314 {(1*KB), 0},
315 {(8*KB), 0},
316 {(16*KB), 0},
317 {(32*KB), 0},
318 {(64*KB), 0}}},
Anders Lewisa7b0f882017-07-24 20:01:13 -0700319
320 {"AT_COMMON_SIZES", args_vector_t{ {8}, {64}, {512}, {1*KB}, {8*KB}, {16*KB},
321 {32*KB}, {64*KB}}},
322
323 // Do not exceed 512. that is about the largest number of properties
324 // that can be created with the current property area size.
325 {"NUM_PROPS", args_vector_t{ {1}, {4}, {16}, {64}, {128}, {256}, {512} }},
326
327 {"MATH_COMMON", args_vector_t{ {0}, {1}, {2}, {3} }}
328 };
329 for (int i = 1; i < 15; i++) {
330 int align = pow(2, i);
331 std::stringstream sstream;
332 sstream << "AT_" << align << "_ALIGN_TWOBUF";
333 args_shorthand.emplace(sstream.str(),
334 args_vector_t{ {8, align, align},
335 {64, align, align},
336 {512, align, align},
337 {1 * KB, align, align},
338 {8 * KB, align, align},
339 {16 * KB, align, align},
340 {32 * KB, align, align},
341 {64 * KB, align, align} });
342 sstream.str("");
343 sstream << "AT_" << align << "_ALIGN_ONEBUF";
344 args_shorthand.emplace(sstream.str(),
345 args_vector_t{ {(8), align},
346 {(64), align},
347 {(512), align},
348 {(1*KB), align},
349 {(8*KB), align},
350 {(16*KB), align},
351 {(32*KB), align},
352 {(64*KB), align} });
353 sstream.str("");
354 }
355 return args_shorthand;
356}
357
358
359int main(int argc, char** argv) {
360 std::map<std::string, args_vector_t> args_shorthand = GetShorthand();
361 bench_opts_t opts = ParseOpts(argc, argv);
362 std::vector<char*> new_argv(argc);
363 SanitizeOpts(argc, argv, &new_argv);
364
365 if (!opts.xmlpath.empty()) {
366 if (int err = RegisterXmlBenchmarks(opts, args_shorthand)) {
367 return err;
368 }
369 }
370 RegisterCliBenchmarks(opts, args_shorthand);
371
372 // Set the thread priority to the maximum.
373 if (setpriority(PRIO_PROCESS, 0, -20)) {
374 perror("Failed to raise priority of process. Are you root?\n");
375 }
376
377 int new_argc = new_argv.size();
378 benchmark::Initialize(&new_argc, new_argv.data());
379 benchmark::RunSpecifiedBenchmarks();
380}