1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
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
16#include <benchmark/benchmark.h>
17#include <err.h>
18#include <getopt.h>
19#include <cinttypes>
20#include <sys/resource.h>
21#include <sys/stat.h>
22#include <sstream>
23#include <string>
24#include <utility>
25#include <vector>
26
27#include "benchmark_fwk.h"
28using namespace std;
29using namespace init_benchmark_test;
30
31namespace {
32constexpr auto K = 1024;
33using args_vector = std::vector<std::vector<int64_t>>;
34
35static const std::vector<int> commonArgs {
36    8,
37    16,
38    32,
39    64,
40    512,
41    1 * K,
42    8 * K,
43    16 * K,
44    32 * K,
45    64 * K,
46    128 * K,
47};
48
49static const std::vector<int> limitSizes {
50    1,
51    2,
52    3,
53    4,
54    5,
55    6,
56    7,
57};
58}
59
60namespace init_benchmark_test {
61std::map<std::string, std::pair<benchmark_func, std::string>> g_allBenchmarks;
62std::mutex g_benchmarkLock;
63static struct option g_benchmarkLongOptions[] = {
64    {"init_cpu", required_argument, nullptr, 'c'},
65    {"init_iterations", required_argument, nullptr, 'i'},
66    {"help", no_argument, nullptr, 'h'},
67    {nullptr, 0, nullptr, 0},
68    };
69}
70
71static void PrintUsageAndExit()
72{
73    printf("Usage:\n");
74    printf("init_benchmarks [--init_cpu=<cpu_to_isolate>]\n");
75    printf("                [--init_iterations=<num_iter>]\n");
76    printf("                [<original benchmark flags>]\n");
77    printf("benchmark flags:\n");
78
79    int argc = 2;
80    char argv0[] = "init_benchmark";
81    char argv1[] = "--help";
82    char *argv[3] = { argv0, argv1, nullptr };
83    benchmark::Initialize(&argc, argv);
84    exit(1);
85}
86
87static void ShiftOptions(int argc, char **argv, std::vector<char *> *argvAfterShift)
88{
89    (*argvAfterShift)[0] = argv[0];
90    for (int i = 1; i < argc; ++i) {
91        char *optarg = argv[i];
92        size_t index = 0;
93        // Find if musl defined this arg.
94        while (g_benchmarkLongOptions[index].name && strncmp(g_benchmarkLongOptions[index].name, optarg + 2, // 2 arg
95            strlen(g_benchmarkLongOptions[index].name))) {
96            ++index;
97        }
98        // Not defined.
99        if (!g_benchmarkLongOptions[index].name) {
100            argvAfterShift->push_back(optarg);
101        } else if ((g_benchmarkLongOptions[index].has_arg == required_argument) && !strchr(optarg, '=')) {
102            i++;
103        }
104    }
105    argvAfterShift->push_back(nullptr);
106}
107
108static bench_opts_t ParseOptions(int argc, char **argv)
109{
110    bench_opts_t opts;
111    int opt;
112    char *errorCheck = nullptr;
113    opterr = 0; // Don't show unrecognized option error.
114
115    while ((opt = getopt_long(argc, argv, "c:i:a:h", g_benchmarkLongOptions, nullptr)) != -1) {
116        switch (opt) {
117            case 'c':
118                if (!(*optarg)) {
119                    printf("ERROR: no argument specified for init_cpu.\n");
120                    PrintUsageAndExit();
121                    break;
122                }
123                opts.cpuNum = strtol(optarg, &errorCheck, 10); // 10 base
124                if (*errorCheck) {
125                    errx(1, "ERROR: Args %s is not a valid integer.", optarg);
126                }
127                break;
128            case 'i':
129                if (!(*optarg)) {
130                    printf("ERROR: no argument specified for init_iterations.\n");
131                    PrintUsageAndExit();
132                    break;
133                }
134                opts.iterNum = strtol(optarg, &errorCheck, 10); // 10 base
135                if (*errorCheck != '\0' || opts.iterNum < 0) {
136                    errx(1, "ERROR: Args %s is not a valid number of iterations.", optarg);
137                }
138                break;
139            case 'h':
140                PrintUsageAndExit();
141                break;
142            case '?':
143                break;
144            default:
145                exit(1);
146        }
147    }
148    return opts;
149}
150
151static void LockAndRun(benchmark::State &state, benchmark_func func, int cpuNum)
152{
153    if (cpuNum >= 0) {
154        cpu_set_t cpuset;
155        CPU_ZERO(&cpuset);
156        CPU_SET(cpuNum, &cpuset);
157
158        if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) {
159            printf("lock CPU failed, ERROR:%s\n", strerror(errno));
160        }
161    }
162
163    reinterpret_cast<void (*)(benchmark::State &)>(func)(state);
164}
165
166static args_vector *ResolveArgs(args_vector *argsVector, std::string args,
167    std::map<std::string, args_vector> &presetArgs)
168{
169    // Get it from preset args.
170    if (presetArgs.count(args)) {
171        return &presetArgs[args];
172    }
173
174    // Convert string to int.
175    argsVector->push_back(std::vector<int64_t>());
176    std::stringstream sstream(args);
177    std::string arg;
178    while (sstream >> arg) {
179        char *errorCheck;
180        int converted = static_cast<int>(strtol(arg.c_str(), &errorCheck, 10)); // 10 base
181        if (*errorCheck) {
182            errx(1, "ERROR: Args str %s contains an invalid macro or int.", args.c_str());
183        }
184        (*argsVector)[0].push_back(converted);
185    }
186    return argsVector;
187}
188
189static args_vector GetArgs(const std::vector<int> &sizes)
190{
191    args_vector args;
192    for (int size : sizes) {
193        args.push_back({size});
194    }
195    return args;
196}
197
198static args_vector GetArgs(const std::vector<int> &sizes, int value)
199{
200    args_vector args;
201    for (int size : sizes) {
202        args.push_back({size, value});
203    }
204    return args;
205}
206
207static args_vector GetArgs(const std::vector<int> &sizes, int value1, int value2)
208{
209    args_vector args;
210    for (int size : sizes) {
211        args.push_back({size, value1, value2});
212    }
213    return args;
214}
215
216static args_vector GetArgs(const std::vector<int> &sizes, const std::vector<int> &limits, int value)
217{
218    args_vector args;
219    for (int size : sizes) {
220        for (int limit : limits) {
221            args.push_back({size, limit, value});
222        }
223    }
224    return args;
225}
226
227static std::map<std::string, args_vector> GetPresetArgs()
228{
229    std::map<std::string, args_vector> presetArgs {
230        {"COMMON_ARGS", GetArgs(commonArgs)},
231        {"ALIGNED_ONEBUF", GetArgs(commonArgs, 0)},
232        {"ALIGNED_TWOBUF", GetArgs(commonArgs, 0, 0)},
233        {"STRING_LIMIT", GetArgs(commonArgs, limitSizes, 0)},
234        {"MATH_COMMON", args_vector{{0}, {1}, {2}, {3}, {4}, {5}}},
235        {"BENCHMARK_VARIABLE", args_vector{{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}}},
236        {"REALPATH_VARIABLE", args_vector{{0}, {1}, {2}, {3}, {4}}},
237        {"MMAP_SIZE", args_vector{{8}, {16}, {32}, {64}, {128}, {512}}},
238    };
239
240    return presetArgs;
241}
242
243static void RegisterSingleBenchmark(bench_opts_t opts, const std::string &funcName, args_vector *runArgs)
244{
245    if (g_allBenchmarks.find(funcName) == g_allBenchmarks.end()) {
246        errx(1, "ERROR: No benchmark for function %s", funcName.c_str());
247    }
248
249    benchmark_func func = g_allBenchmarks.at(funcName).first;
250    for (const std::vector<int64_t> &args : (*runArgs)) {
251        // It will call LockAndRun(func, opts.cpuNum).
252        auto registration = benchmark::RegisterBenchmark(funcName.c_str(), LockAndRun, func, opts.cpuNum)->Args(args);
253        printf("opts.iterNum %ld \n", opts.iterNum);
254        if (opts.iterNum > 0) {
255            registration->Iterations(opts.iterNum);
256        }
257    }
258}
259
260static void RegisterAllBenchmarks(const bench_opts_t &opts, std::map<std::string, args_vector> &presetArgs)
261{
262    for (auto &entry : g_allBenchmarks) {
263        auto &funcInfo = entry.second;
264        args_vector arg_vector;
265        args_vector *runArgs = ResolveArgs(&arg_vector, funcInfo.second, presetArgs);
266        RegisterSingleBenchmark(opts, entry.first, runArgs);
267    }
268}
269
270int main(int argc, char **argv)
271{
272    std::map<std::string, args_vector> presetArgs = GetPresetArgs();
273    bench_opts_t opts = ParseOptions(argc, argv);
274    std::vector<char *> argvAfterShift(argc);
275    ShiftOptions(argc, argv, &argvAfterShift);
276    RegisterAllBenchmarks(opts, presetArgs);
277    if (setpriority(PRIO_PROCESS, 0, -20)) { // 20 max
278        perror("Set priority of process failed.\n");
279    }
280    CreateLocalParameterTest(512); // test max 512
281    int argcAfterShift = argvAfterShift.size();
282    benchmark::Initialize(&argcAfterShift, argvAfterShift.data());
283    benchmark::RunSpecifiedBenchmarks();
284}
285