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
16#include <cstdio>
17#include <cstring>
18#include <unistd.h>
19#include <cstdlib>
20#include <pthread.h>
21#include <vector>
22#include <ctime>
23#include <csignal>
24#include <atomic>
25#include <memory>
26
27namespace {
28static int g_duration;
29static int g_memSize;
30static std::atomic<long long> g_times;
31static std::atomic<long long> g_mallocTotalTime;
32static std::atomic<long long> g_freeTotalTime;
33static constexpr int N_INSTALL_MALLOC_HOOK_SIGNAL = 36;
34static constexpr int DURATION_TIME = 30 * 60;
35static constexpr int MEMORY_BUFFER_SIZE = 19;
36
37void* UserThread(void* param)
38{
39    int idx = *static_cast<int*>(param);
40    struct timespec beginTime;
41    struct timespec endTime;
42    struct timespec beginTimeMalloc;
43    struct timespec endTimeMalloc;
44    struct timespec beginTimeFree;
45    struct timespec endTimeFree;
46
47    clock_gettime(CLOCK_REALTIME, &beginTime);
48    int displaySize = g_memSize > 5 ? 5 : g_memSize;
49    do {
50        clock_gettime(CLOCK_REALTIME, &endTime);
51        if (endTime.tv_sec - beginTime.tv_sec >= g_duration) {
52            break;
53        }
54        clock_gettime(CLOCK_REALTIME, &beginTimeMalloc);
55        char* mem = static_cast<char *>(malloc(g_memSize));
56        if (mem == nullptr) {
57            printf("Error: malloc mem memory failed.\n");
58            return nullptr;
59        }
60        clock_gettime(CLOCK_REALTIME, &endTimeMalloc);
61        std::atomic_fetch_add_explicit(
62            &g_mallocTotalTime,
63            static_cast<long long>((endTimeMalloc.tv_sec - beginTimeMalloc.tv_sec) * 1000000000L +
64                                   (endTimeMalloc.tv_nsec - beginTimeMalloc.tv_nsec)),
65            std::memory_order_relaxed);
66        long long currentTimes = ++g_times;
67        long long timeBase = 10000;
68        if (currentTimes % timeBase == 0) {
69            printf("Thread %d, %lld: ", idx + 1, currentTimes);
70            for (int i = 0 ; i < displaySize ; i++) {
71                printf("%d ", mem[i]);
72            }
73            printf("\n");
74        }
75        clock_gettime(CLOCK_REALTIME, &beginTimeFree);
76        if (mem != nullptr) {
77            free(mem);
78        }
79        clock_gettime(CLOCK_REALTIME, &endTimeFree);
80        std::atomic_fetch_add_explicit(
81            &g_freeTotalTime,
82            static_cast<long long>((endTimeFree.tv_sec - beginTimeFree.tv_sec) * 1000000000L +
83                                   (endTimeFree.tv_nsec - beginTimeFree.tv_nsec)),
84            std::memory_order_relaxed);
85    }
86    while (true);
87    return nullptr;
88}
89#define PRINTF_DATA(fileptr, fmt, ...) \
90{                              \
91    do { \
92        fprintf(stderr, fmt, ## __VA_ARGS__); \
93        fprintf(fileptr, fmt, ## __VA_ARGS__); \
94        fflush(fileptr); \
95    } while (0); \
96}
97
98void Usage()
99{
100    printf("Usage: perf_test_data <-o output_file_name> [-t threadNum[] [-d g_duration] [-s g_memSize]\n");
101}
102
103void FileClose(FILE* fp)
104{
105    if (fp != nullptr) {
106        fclose(fp);
107    }
108}
109
110} // namespace
111
112int main(int argc, char *argv[])
113{
114    int threadNum = 1;
115    g_duration = DURATION_TIME;
116    g_memSize = MEMORY_BUFFER_SIZE;
117    std::unique_ptr<FILE, void (*)(FILE*)> outFp(nullptr, nullptr);
118    for (int idx = 1; idx < argc; ++idx) {
119        if (strcmp(argv[idx], "-o") == 0) {
120            if (idx + 1 >= argc) {
121                Usage();
122                return 1;
123            } else {
124                ++idx;
125                outFp = std::unique_ptr<FILE, void (*)(FILE*)>(fopen(argv[idx], "w"), FileClose);
126                if (outFp == nullptr) {
127                    printf("File '%s' can't be opened.\n", argv[idx]);
128                    return 1;
129                }
130            }
131        } else if (strcmp(argv[idx], "-t") == 0) {
132            if (idx + 1 >= argc) {
133                Usage();
134                return 1;
135            } else {
136                ++idx;
137                threadNum = atoi(argv[idx]);
138                if (threadNum <= 0) {
139                    printf("Thread number can't be less or equal zero.\n");
140                    return 1;
141                }
142            }
143        } else if (strcmp(argv[idx], "-d") == 0) {
144            if (idx + 1 >= argc) {
145                Usage();
146                return 1;
147            } else {
148                ++idx;
149                g_duration = atoi(argv[idx]);
150                if (g_duration <= 0) {
151                    printf("g_duration can't be less or equal zero.\n");
152                    return 1;
153                }
154            }
155        } else if (strcmp(argv[idx], "-s") == 0) {
156            if (idx + 1 >= argc) {
157                Usage();
158                return 1;
159            } else {
160                ++idx;
161                g_memSize = atoi(argv[idx]);
162                if (g_memSize <= 0) {
163                    printf("memory size can't be less or equal zero.\n");
164                    return 1;
165                }
166            }
167        }
168    }
169    if (outFp == nullptr) {
170        Usage();
171        return 1;
172    }
173    pthread_t* thrArray = static_cast<pthread_t*>(malloc(sizeof(pthread_t) * threadNum));
174    if (thrArray == nullptr) {
175        printf("malloc thrArray memory failed.\n");
176        return 1;
177    }
178    int idxSituation;
179    int idxSituationMax = 2;
180    int pid = static_cast<int>(getpid());
181    PRINTF_DATA(outFp.get(), "PID: %d, file: %d.nativehook\n", pid, pid);
182    PRINTF_DATA(outFp.get(),
183        "Thread number: %d, duration: %d seconds, memory size: %d bytes\n",
184        threadNum,
185        g_duration,
186        g_memSize);
187    for (idxSituation = 0; idxSituation < idxSituationMax; ++idxSituation) {
188        if (idxSituation == 0) {
189            PRINTF_DATA(outFp.get(), "No hook situation\n");
190        } else {
191            PRINTF_DATA(outFp.get(), "\nWith hook situation\n");
192            raise(N_INSTALL_MALLOC_HOOK_SIGNAL);
193        }
194        int idx;
195
196        std::atomic_store_explicit(&g_times, static_cast<long long>(0), std::memory_order_seq_cst);
197        std::atomic_store_explicit(&g_mallocTotalTime, static_cast<long long>(0), std::memory_order_seq_cst);
198        std::atomic_store_explicit(&g_freeTotalTime, static_cast<long long>(0), std::memory_order_seq_cst);
199
200        for (idx = 0; idx < threadNum; ++idx) {
201            if (pthread_create(thrArray + idx, nullptr, UserThread, reinterpret_cast<void*>(&idx)) != 0) {
202                printf("Creating thread failed.\n");
203            }
204        }
205
206        for (idx = 0; idx < threadNum; ++idx) {
207            pthread_join(thrArray[idx], nullptr);
208        }
209        long long totalTimes = g_times.load(std::memory_order_relaxed);
210        PRINTF_DATA(outFp.get(), "The total g_times(malloc/free): %lld\n", totalTimes);
211    }
212    if (thrArray != nullptr) {
213        free(thrArray);
214    }
215    printf("Exit\n");
216}
217