1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
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 <chrono>
16#include <cstdio>
17#include <cstdlib>
18#include <new>
19#include <thread>
20#include <dlfcn.h>
21#include <pthread.h>
22#include <unistd.h>
23#include <sys/prctl.h>
24#include <sys/syscall.h>
25
26#pragma clang optimize off
27
28namespace {
29typedef char* (*DepthMallocSo)(int depth, int mallocSize);
30typedef void (*DepthFreeSo)(int depth, char *p);
31constexpr int MALLOC_SIZE = 1000;
32constexpr int DATA_SIZE = 200;
33constexpr int SLEEP_TIME = 200;
34constexpr int ARGC_NUM_MAX = 4;
35constexpr int ARGC_NUM_MUST = 3;
36constexpr int ARGC_MALLOC_TIMES = 2;
37constexpr int ARGC_STICK_DEPTH = 3;
38unsigned int g_stickDepth = 100;
39
40const static char SO_PATH[] = "libnativetest_so.z.so";
41using StaticSpace = struct {
42    int data[DATA_SIZE];
43};
44
45class Timer {
46public:
47    using Clock = std::chrono::steady_clock;
48    using TimePoint = Clock::time_point;
49
50    Timer() : startTime_(Now()) {}
51
52    ~Timer() {}
53
54    long ElapsedUs()
55    {
56        auto currentTime = Now();
57        return std::chrono::duration_cast<std::chrono::microseconds>(currentTime - startTime_).count();
58    }
59
60    void Reset()
61    {
62        startTime_ = Now();
63    }
64
65protected:
66    TimePoint Now()
67    {
68        return Clock::now();
69    }
70
71private:
72    TimePoint startTime_;
73};
74
75char *DepthMalloc(int depth, int mallocSize)
76{
77    if (mallocSize <= 0) {
78        return nullptr;
79    }
80    StaticSpace staticeData;
81    if (depth == 0) {
82        staticeData.data[0] = 1;
83        return new char[mallocSize];
84    }
85    return (DepthMalloc(depth - 1, mallocSize));
86}
87
88void DepthFree(int depth, char *p)
89{
90    StaticSpace staticeData;
91    if (depth == 0) {
92        staticeData.data[0] = 1;
93        delete []p;
94        return;
95    }
96    return (DepthFree(depth - 1, p));
97}
98
99void* ThreadFuncCpp(void* param)
100{
101    char *p = nullptr;
102    long tid = syscall(SYS_gettid);
103    printf("start thread %ld\n", tid);
104    int times = *static_cast<int*>(param);
105    int idx = 0;
106    std::string name = "thread";
107    name = name + std::to_string(times);
108    prctl(PR_SET_NAME, name.c_str());
109
110    constexpr int timeBase = 100;
111    while (idx < times) {
112        p = DepthMalloc(g_stickDepth, MALLOC_SIZE);
113        if (idx % timeBase == 0) {
114            printf("thread %ld malloc %d times\n", tid, idx);
115        }
116        if (p) {
117            DepthFree(g_stickDepth, p);
118        }
119        idx++;
120    }
121    return nullptr;
122}
123
124void* ThreadFuncCppHook(void* param)
125{
126    char *p = nullptr;
127    long tid = syscall(SYS_gettid);
128    printf("start thread %ld\n", tid);
129    int times = *static_cast<int*>(param);
130    int idx = 0;
131    std::string name = "thread";
132    name = name + std::to_string(times);
133    prctl(PR_SET_NAME, name.c_str());
134    constexpr int timeBase = 1000;
135    void* handle = nullptr;
136    DepthMallocSo mallocFunc = DepthMalloc;
137    DepthFreeSo freeFunc = DepthFree;
138
139    constexpr unsigned int dlopenTrigger = 30000;
140    while (idx < times) {
141        if (idx == static_cast<int>(dlopenTrigger)) {
142            printf("dlopen!!!\n");
143            handle = dlopen(SO_PATH, RTLD_LAZY);
144            if (handle == nullptr) {
145                printf("library not exist!\n");
146                exit(0);
147            }
148            mallocFunc = (DepthMallocSo)dlsym(handle, "DepthMallocSo");
149            freeFunc = (DepthFreeSo)dlsym(handle, "DepthFreeSo");
150            if (mallocFunc == nullptr || freeFunc == nullptr) {
151                printf("function not exist!\n");
152                exit(0);
153            }
154        }
155        p = mallocFunc(g_stickDepth, MALLOC_SIZE);
156        if (idx % timeBase == 0) {
157            printf("thread %ld malloc %d times\n", tid, idx);
158        }
159        if (p) {
160            freeFunc(g_stickDepth, p);
161        }
162        idx++;
163    }
164    if (handle != nullptr) {
165        dlclose(handle);
166    }
167    return nullptr;
168}
169
170int ThreadTimeCost(int threadNum, int mallocTimes)
171{
172    Timer timer = {};
173    if (threadNum <= 0) {
174        printf("threadNum less than or equal to 0.\n");
175        return 1;
176    }
177    pthread_t* thrArray = new (std::nothrow) pthread_t[threadNum];
178    if (!thrArray) {
179        printf("new thread array failed.\n");
180        return 1;
181    }
182    int idx;
183    for (idx = 0; idx < threadNum; ++idx) {
184        if (pthread_create(thrArray + idx, nullptr, ThreadFuncCpp, static_cast<void*>(&mallocTimes)) != 0) {
185            printf("Creating thread failed.\n");
186        }
187    }
188    for (idx = 0; idx < threadNum; ++idx) {
189        pthread_join(thrArray[idx], nullptr);
190    }
191    delete []thrArray;
192    auto timeCost = timer.ElapsedUs();
193    printf("Before hook, time cost %ldus.\nAfter hook test sleeping 200 ......., please send signal\n", timeCost);
194    sleep(SLEEP_TIME);
195    printf("Hook test start\n");
196    Timer hooktimer = {};
197    pthread_t* thrArrayHook = new (std::nothrow) pthread_t[threadNum];
198    if (!thrArrayHook) {
199        printf("new thread lock array failed.\n");
200        return 1;
201    }
202    for (idx = 0; idx < threadNum; ++idx) {
203        if (pthread_create(thrArrayHook + idx, nullptr, ThreadFuncCppHook, static_cast<void*>(&mallocTimes)) !=
204            0) {
205            printf("Creating thread failed.\n");
206        }
207    }
208    for (idx = 0; idx < threadNum; ++idx) {
209        pthread_join(thrArrayHook[idx], nullptr);
210    }
211    delete []thrArrayHook;
212    auto hookCost = hooktimer.ElapsedUs();
213    printf("After hook, time cost %ldus.\nPerformance test finish!", hookCost);
214    return 0;
215}
216} // namespace
217
218int main(int argc, char *argv[])
219{
220    int threadNum = 1;
221    int mallocTimes = 0;
222    if  (argc >= ARGC_NUM_MUST) {
223        if (atoi(argv[1]) > 0) {
224            threadNum = atoi(argv[1]);
225        }
226        mallocTimes = atoi(argv[ARGC_MALLOC_TIMES]);
227        if (argc == ARGC_NUM_MAX) {
228            g_stickDepth = static_cast<unsigned int>(atoi(argv[ARGC_STICK_DEPTH]));
229        }
230    } else {
231        printf("command error\n");
232        return 0;
233    }
234    printf("Test start %d thread, malloc %d times\n", threadNum, mallocTimes);
235    if (!ThreadTimeCost(threadNum, mallocTimes)) {
236        printf("Test success end!\n");
237    } else {
238        printf("Test failure end!\n");
239    }
240}
241
242#pragma clang optimize on
243