1/*
2 * Copyright (c) 2021-2022 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#include <algorithm>
16#include <cctype>
17#include <chrono>
18#include <cstring>
19#include <iostream>
20#include <random>
21#include <string>
22#include <thread>
23#include <vector>
24
25#include <sys/mman.h>
26#include <sys/syscall.h>
27#include <unistd.h>
28
29using namespace std::chrono;
30using namespace std::chrono_literals;
31
32namespace {
33#define USED_FUNCTION __attribute__((__used__)) __attribute__((optnone))
34constexpr milliseconds eachStackFunRunTime = 100ms;
35constexpr milliseconds msDuartion = 1000ms;
36const ssize_t ERRINFOLEN = 512;
37
38extern USED_FUNCTION void LoopBranch0(std::default_random_engine &rnd, int level);
39extern USED_FUNCTION void LoopBranch1(std::default_random_engine &rnd, int level);
40
41struct Option {
42    int numThreads {5};
43    int second {36000};
44    int stack {5};
45    bool noWait = false;
46    bool dynamicStack = false;
47    bool mmap = false;
48    bool iowait = false;
49    bool branch = false;
50    bool nonew = false;
51    bool nofunc = false;
52    int boundCpu {-1};
53    int sleepms {0};
54};
55
56inline int GetTid()
57{
58    int res = static_cast<int>(syscall(SYS_gettid));
59    return res;
60}
61
62USED_FUNCTION void LoopDummy(milliseconds anything)
63{
64    if (anything.count() > 0) {
65        printf("");
66    }
67}
68
69USED_FUNCTION void LoopBranch0(std::default_random_engine &rnd, int level)
70{
71    constexpr int two {2};
72    if (level == 0) {
73        printf("");
74        return;
75    }
76    if (rnd() % two == 0) {
77        LoopBranch0(rnd, --level);
78    } else {
79        LoopBranch1(rnd, --level);
80    }
81}
82
83USED_FUNCTION void LoopBranch1(std::default_random_engine &rnd, int level)
84{
85    constexpr int two {2};
86    if (level == 0) {
87        printf("");
88        return;
89    }
90    if (rnd() % two == 0) {
91        LoopBranch0(rnd, --level);
92    } else {
93        LoopBranch1(rnd, --level);
94    }
95}
96
97USED_FUNCTION void LoopBranch()
98{
99    constexpr int two {2};
100    int branchLevel = 10;
101    std::default_random_engine rnd;
102    if (rnd() % two == 0) {
103        LoopBranch0(rnd, branchLevel);
104    } else {
105        LoopBranch1(rnd, branchLevel);
106    }
107}
108
109USED_FUNCTION void LoopIowait()
110{
111    std::default_random_engine rnd;
112    FILE *fp = fopen("temp", "rw");
113    if (fp == nullptr) {
114        return;
115    }
116
117    std::unique_ptr<FILE, decltype(&fclose)> fd {fp, &fclose};
118    if (fd != nullptr) {
119        const std::string tempBuf = std::to_string(rnd());
120        fwrite(tempBuf.c_str(), tempBuf.size(), 1, fd.get());
121    }
122}
123
124USED_FUNCTION void LoopMmap()
125{
126    int *arr = static_cast<int *>(
127        mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, 0, 0));
128
129    int *ptr = arr;
130    int someVal1 {10};
131    int someVal2 {20};
132    int someVal3 {30};
133    *ptr = someVal1;
134    ++ptr;
135    *ptr = someVal2;
136    ++ptr;
137    *ptr = someVal3;
138    munmap(arr, getpagesize());
139    return;
140}
141
142USED_FUNCTION void LoopFunction(milliseconds timeOutMS, const Option &option)
143{
144    auto now = std::chrono::steady_clock::now();
145    auto sleepTime = now + seconds(1);
146    int count = 0;
147    while (std::chrono::steady_clock::now() < (now + timeOutMS)) {
148        if (option.sleepms > 0) {
149            if (std::chrono::steady_clock::now() >= sleepTime) {
150                sleepTime = std::chrono::steady_clock::now() + seconds(1);
151                std::this_thread::sleep_for(std::chrono::milliseconds(option.sleepms));
152            }
153        }
154        if (option.mmap) {
155            LoopMmap();
156        }
157        if (option.iowait) {
158            LoopIowait();
159        }
160        if (option.branch) {
161            LoopBranch();
162        }
163
164        std::default_random_engine rnd;
165        int a = rnd();
166        int b = rnd();
167        int c = rnd();
168        a = (a++) * (b++) * (c++);
169
170        if (a == 0) {
171            continue;
172        }
173
174        if (!option.nonew) {
175            auto p = new unsigned int;
176            *p = count++;
177            delete p;
178            p = nullptr;
179        }
180
181        if (!option.nofunc) {
182            LoopDummy(timeOutMS);
183        }
184    }
185    return;
186}
187
188inline void Loop(milliseconds timeOutMS, const Option &option)
189{
190    printf("loop at %s\n", __func__);
191    LoopFunction(timeOutMS, option);
192    return;
193}
194
195USED_FUNCTION void CallStack10(int currentStack, const Option &option)
196{
197    if (option.stack > 0) {
198        Loop(option.second * msDuartion, option);
199    }
200}
201
202USED_FUNCTION void CallStack9(int currentStack, const Option &option)
203{
204    if (option.stack > 0) {
205        if (option.dynamicStack) {
206            Loop(eachStackFunRunTime, option); // loop 100 ms
207        }
208        CallStack10(currentStack - 1, option);
209    } else {
210        Loop(option.second * msDuartion, option);
211    }
212}
213
214USED_FUNCTION void CallStack8(int currentStack, const Option &option)
215{
216    if (option.stack > 0) {
217        if (option.dynamicStack) {
218            Loop(eachStackFunRunTime, option); // loop 100 ms
219        }
220        CallStack9(currentStack - 1, option);
221    } else {
222        Loop(option.second * msDuartion, option);
223    }
224}
225
226USED_FUNCTION void CallStack7(int currentStack, const Option &option)
227{
228    if (option.stack > 0) {
229        if (option.dynamicStack) {
230            Loop(eachStackFunRunTime, option); // loop 100 ms
231        }
232        CallStack8(currentStack - 1, option);
233    } else {
234        Loop(option.second * msDuartion, option);
235    }
236}
237
238USED_FUNCTION void CallStack6(int currentStack, const Option &option)
239{
240    if (option.stack > 0) {
241        if (option.dynamicStack) {
242            Loop(eachStackFunRunTime, option); // loop 100 ms
243        }
244        CallStack7(currentStack - 1, option);
245    } else {
246        Loop(option.second * msDuartion, option);
247    }
248}
249
250USED_FUNCTION void CallStack5(int currentStack, const Option &option)
251{
252    if (option.stack > 0) {
253        if (option.dynamicStack) {
254            Loop(eachStackFunRunTime, option); // loop 100 ms
255        }
256        CallStack6(currentStack - 1, option);
257    } else {
258        Loop(option.second * msDuartion, option);
259    }
260}
261
262USED_FUNCTION void CallStack4(int currentStack, const Option &option)
263{
264    if (option.stack > 0) {
265        if (option.dynamicStack) {
266            Loop(eachStackFunRunTime, option); // loop 100 ms
267        }
268        CallStack5(currentStack - 1, option);
269    } else {
270        Loop(option.second * msDuartion, option);
271    }
272}
273
274USED_FUNCTION void CallStack3(int currentStack, const Option &option)
275{
276    if (option.stack > 0) {
277        if (option.dynamicStack) {
278            Loop(eachStackFunRunTime, option); // loop 100 ms
279        }
280        CallStack4(currentStack - 1, option);
281    } else {
282        Loop(option.second * msDuartion, option);
283    }
284}
285
286USED_FUNCTION void CallStack2(int currentStack, const Option &option)
287{
288    if (option.stack > 0) {
289        if (option.dynamicStack) {
290            Loop(eachStackFunRunTime, option); // loop 100 ms
291        }
292        CallStack3(currentStack - 1, option);
293    } else {
294        Loop(option.second * msDuartion, option);
295    }
296}
297
298USED_FUNCTION void CallStack1(int currentStack, const Option &option)
299{
300    if (option.stack > 0) {
301        if (option.dynamicStack) {
302            Loop(eachStackFunRunTime, option); // loop 100 ms
303        }
304        CallStack2(currentStack - 1, option);
305    } else {
306        Loop(option.second * msDuartion, option);
307    }
308}
309
310USED_FUNCTION void CallStack0(int currentStack, const Option &option)
311{
312    if (option.stack > 0) {
313        if (option.dynamicStack) {
314            Loop(eachStackFunRunTime, option); // loop 100 ms
315        }
316        CallStack1(currentStack - 1, option);
317    } else {
318        Loop(option.second * msDuartion, option);
319    }
320}
321
322USED_FUNCTION void ExampleThread(const Option &option)
323{
324    printf("thread %d ++ with %d %d \n", GetTid(), option.second, option.stack);
325    CallStack0(option.stack, option);
326    printf("thread %d --\n", GetTid());
327
328    return;
329}
330
331USED_FUNCTION void RunSampleThread(const Option &option)
332{
333    printf("run %d threads for %d second with %d stack level\n", option.numThreads, option.second,
334           option.stack);
335    printf("main thread %d\n", GetTid());
336
337    std::thread threads[option.numThreads];
338    for (int count = 0; count < option.numThreads; ++count) {
339        threads[count] = std::thread(ExampleThread, option);
340    }
341    for (int count = 0; count < option.numThreads; ++count) {
342        threads[count].join();
343    }
344    printf("all thread exit\n");
345}
346
347void WaitStart()
348{
349    std::string startArg;
350    std::cout << "Please input 'start' to begin the subthread: \n";
351    while (true) {
352        std::cin >> startArg;
353        if (startArg.compare("start") == 0) {
354            break;
355        } else {
356            continue;
357        }
358    }
359}
360
361void Help()
362{
363    printf("this is a demo test command\n");
364    printf("  Use the following commands to simulate different scenarios\n");
365    printf("  --help\n");
366    printf("    this page\n");
367    printf("  --thread <number>\n");
368    printf("    setup the thread number, default is 5 second\n");
369    printf("  --time <time>\n");
370    printf("    setup run sec, default is 36000 second\n");
371    printf("  --stack <level>\n");
372    printf("    setup stack level, default is 5\n");
373    printf("  --nowait\n");
374    printf("    setup skip the start, default wait the start\n");
375    printf("  --dynamic\n");
376    printf("    will run some code in each stack level\n");
377    printf("  --mmap\n");
378    printf("    will run mmap code in the loop\n");
379    printf("  --iowait\n");
380    printf("    will run iowait code in the loop\n");
381    printf("  --branch\n");
382    printf("    will run branch code in the loop\n");
383    printf("  --nonew\n");
384    printf("    will not new memory in the loop\n");
385    printf("  --nofunc\n");
386    printf("    will not call dummy func in the loop\n");
387    printf("  --boundcpu <cpu>\n");
388    printf("    the process will bound to <cpu>\n");
389    printf("  --sleep <milliseconds>\n");
390    printf("    threads will sleep <milliseconds> per second, default is 0.\n");
391}
392
393bool GetIntFromArg(std::vector<std::string> &args, int &value)
394{
395    if (!args.empty()) {
396        if (std::all_of(args.begin()->begin(), args.begin()->end(), ::isdigit)) {
397            value = std::stoi(args[0]);
398            args.erase(args.begin());
399        } else {
400            printf("unknown format '%s'\n", args[0].c_str());
401            return false;
402        }
403    }
404    return true;
405}
406
407bool MatchArgs(std::vector<std::string> &args, const std::string &option)
408{
409    if (args[0] == option) {
410        args.erase(args.begin());
411        return true;
412    }
413    return false;
414}
415bool GetBoolFromArg(std::vector<std::string> &args, const std::string &option, bool &value)
416{
417    if (MatchArgs(args, option)) {
418        value = true;
419        return true;
420    } else {
421        return false;
422    }
423}
424} // namespace
425
426USED_FUNCTION int main(int argc, char *argv[])
427{
428    std::vector<std::string> args;
429    for (int i = 1; i < argc; i++) {
430        args.push_back(argv[i]);
431    }
432    Option option;
433
434    while (!args.empty()) {
435        if (MatchArgs(args, "--help")) {
436            Help();
437            return 0;
438        } else if (MatchArgs(args, "--boundcpu")) {
439            if (!GetIntFromArg(args, option.boundCpu)) {
440                return -1;
441            }
442        } else if (MatchArgs(args, "--sleep")) {
443            if (!GetIntFromArg(args, option.sleepms)) {
444                return -1;
445            }
446        } else if (MatchArgs(args, "--thread")) {
447            if (!GetIntFromArg(args, option.numThreads)) {
448                return -1;
449            }
450        } else if (MatchArgs(args, "--time")) {
451            if (!GetIntFromArg(args, option.second)) {
452                return -1;
453            }
454        } else if (MatchArgs(args, "--stack")) {
455            if (!GetIntFromArg(args, option.stack)) {
456                return -1;
457            }
458        } else if (GetBoolFromArg(args, "--dynamic", option.dynamicStack)) {
459            continue;
460        } else if (GetBoolFromArg(args, "--nowait", option.noWait)) {
461            continue;
462        } else if (GetBoolFromArg(args, "--mmap", option.mmap)) {
463            continue;
464        } else if (GetBoolFromArg(args, "--iowait", option.iowait)) {
465            continue;
466        } else if (GetBoolFromArg(args, "--branch", option.branch)) {
467            continue;
468        } else if (GetBoolFromArg(args, "--nonew", option.nonew)) {
469            continue;
470        } else if (GetBoolFromArg(args, "--nofunc", option.nofunc)) {
471            continue;
472        } else {
473            printf("unknown format '%s'\n", args[0].c_str());
474            return -1;
475        }
476    }
477
478    if (option.boundCpu > -1) {
479        cpu_set_t mask;
480        CPU_ZERO(&mask);
481        CPU_SET(option.boundCpu, &mask);
482        if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
483            char errInfo[ERRINFOLEN] = { 0 };
484            strerror_r(errno, errInfo, ERRINFOLEN);
485            printf("Set CPU(%d) affinity failure, ERROR:%s\n", option.boundCpu, errInfo);
486        }
487    }
488    if (!option.noWait) {
489        WaitStart();
490    }
491
492    RunSampleThread(option);
493    return 0;
494}
495