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 <fcntl.h>
17#include <pthread.h>
18#include <cstdio>
19#include <cstdlib>
20#include <string>
21#include <sys/mman.h>
22#include <sys/stat.h>
23#include <sys/syscall.h>
24#include <sys/types.h>
25#include <ctime>
26#include <unistd.h>
27#ifdef HOOK_ENABLE
28#include "memory_trace.h"
29#endif
30#include "securec.h"
31#pragma clang optimize off
32
33#define PAGE_SIZE 4096
34#define SLEEP_TIME_SEC 1
35#define RESPONSE_SPEED 300
36#define DATA_SIZE 50
37#define ALLOC_FLAG (1 << 0)
38#define MMAP_FLAG (1 << 1)
39
40namespace {
41typedef struct {
42    int data[DATA_SIZE];
43} StaticSpace;
44
45const static int STATIC_DEPTH = 5;
46
47static double g_mallocDuration = 0;
48static double g_callocDuration = 0;
49static double g_reallocDuration = 0;
50static double g_freeDuration = 0;
51
52static int g_fd = -1;
53static int g_runing = 1;
54static int g_threadNum = 1;
55static int g_mallocSize = 1;
56static const char* TEST_FILE_NAME = "./mmapTest";
57static unsigned int g_hookFlag = 0;
58
59static char* DepthMalloc(int depth, int mallocSize)
60{
61    if (mallocSize <= 0) {
62        return nullptr;
63    }
64    StaticSpace staticeData;
65    if (depth == 0) {
66        staticeData.data[0] = 1;
67        return (char*)malloc(mallocSize);
68    }
69    return (DepthMalloc(depth - 1, mallocSize));
70}
71
72static char* DepthCalloc(int depth, int callocSize)
73{
74    StaticSpace staticeData;
75    if (depth == 0) {
76        staticeData.data[0] = 1;
77        return (char*)calloc(sizeof(char), callocSize);
78    }
79    return (DepthCalloc(depth - 1, callocSize));
80}
81
82static char* DepthRealloc(int depth, void* p, int reallocSize)
83{
84    StaticSpace staticeData;
85    if (depth == 0) {
86        staticeData.data[0] = 1;
87        return (char*)realloc(p, reallocSize);
88    }
89    return (DepthRealloc(depth - 1, p, reallocSize));
90}
91
92static void DepthFree(int depth, void* p)
93{
94    StaticSpace staticeData;
95    if (depth == 0) {
96        staticeData.data[0] = 1;
97        free(p);
98        return;
99    }
100    return (DepthFree(depth - 1, p));
101}
102
103static void ApplyForMalloc(int mallocSize)
104{
105    printf("\nstart malloc apply (size = %d)\n", mallocSize);
106    clock_t timerStart = 0;
107    clock_t timerStop = 0;
108    double duration = 0;
109    timerStart = clock();
110    char* p = DepthMalloc(STATIC_DEPTH, mallocSize);
111    timerStop = clock();
112    if (!p) {
113        printf("malloc failure\n");
114        return;
115    }
116    duration += static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC;
117    g_mallocDuration += static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC;
118    printf("malloc success, malloc (%d) time is %f\n", mallocSize, duration);
119    printf("\nReady for free -- ");
120    timerStart = clock();
121    DepthFree(STATIC_DEPTH, p);
122    timerStop = clock();
123    duration += static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC;
124    g_freeDuration += static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC;
125    printf("free success, free time is %f\n", static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC);
126    printf("malloc apply success, total time is %f\n", duration);
127}
128
129static void ApplyForCalloc(int mallocSize)
130{
131    int callocSize = mallocSize / sizeof(char);
132    printf("\nstart calloc apply (size = %d)\n", callocSize);
133    clock_t timerStart = 0;
134    clock_t timerStop = 0;
135    double duration = 0;
136    timerStart = clock();
137    char* p = DepthCalloc(STATIC_DEPTH, callocSize);
138    timerStop = clock();
139    if (!p) {
140        printf("calloc failure\n");
141        return;
142    }
143    duration += static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC;
144    g_callocDuration += static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC;
145    printf("calloc success, calloc (%d) time is %f\n", callocSize, duration);
146    printf("\nReady for free -- ");
147    timerStart = clock();
148    DepthFree(STATIC_DEPTH, p);
149    timerStop = clock();
150    duration += static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC;
151    g_freeDuration += static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC;
152    printf("free success, free time is %f\n", static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC);
153    printf("calloc apply success, total time is %f\n", duration);
154}
155
156static void ApplyForRealloc(int mallocSize)
157{
158    const int defaultReallocSize = 100;
159    int reallocSize = mallocSize * defaultReallocSize;
160    printf("\nstart realloc apply (size = %d)\n", reallocSize);
161    if (mallocSize <= 0) {
162        printf("Invalid mallocSize.\n");
163        return;
164    }
165    clock_t timerStart = 0;
166    clock_t timerStop = 0;
167    double duration = 0;
168    char* p = static_cast<char*>(malloc(mallocSize));
169    if (!p) {
170        printf("malloc failure\n");
171        return;
172    }
173    timerStart = clock();
174    char* np = DepthRealloc(STATIC_DEPTH, p, reallocSize);
175    timerStop = clock();
176    if (!np) {
177        free(p);
178        printf("realloc failure\n");
179        return;
180    }
181    duration += static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC;
182    g_reallocDuration += static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC;
183    printf("realloc success, realloc (%d) time is %f\n", reallocSize, duration);
184    printf("\nReady for free -- ");
185    timerStart = clock();
186    DepthFree(STATIC_DEPTH, np);
187    timerStop = clock();
188    duration += static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC;
189    g_freeDuration += static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC;
190    printf("free success, free time is %f\n", static_cast<double>(timerStop - timerStart) / CLOCKS_PER_SEC);
191    printf("realloc apply success, total time is %f\n", duration);
192}
193
194static void NewString()
195{
196    std::string* sp = new std::string("hello world");
197    printf("string  sp = %s\n", sp->c_str());
198    delete sp;
199}
200
201static void UniqueString()
202{
203    auto pName = std::make_unique<std::string>("Hello");
204}
205
206
207static void* ThreadFuncC(void* param)
208{
209    int mallocCount = 0;
210    int callocCount = 0;
211    int reallocCount = 0;
212    int freeCount = 0;
213    int randNum = 0;
214    int tid = syscall(SYS_gettid);
215    int mallocSize = *static_cast<int*>(param);
216    printf("start thread %d\n", tid);
217    time_t tv = time(nullptr);
218    if (tv == -1) {
219        tv = 1;
220    }
221
222    unsigned int seed = static_cast<unsigned int>(tv);
223    const int testBranchNum = 3;
224    while (g_runing != 0) {
225        randNum = rand_r(&seed) % testBranchNum;
226        if (randNum == 0) {
227            ApplyForMalloc(mallocSize);
228            mallocCount++;
229        } else if (randNum == 1) {
230            ApplyForCalloc(mallocSize);
231            callocCount++;
232        } else {
233            ApplyForRealloc(mallocSize);
234            reallocCount++;
235        }
236        freeCount++;
237
238        NewString();
239        UniqueString();
240        sleep(SLEEP_TIME_SEC);
241    }
242
243    printf("thread %d  malloc count[%d] totalTime[%f] meanTime[%f].\n", tid,
244        mallocCount, g_mallocDuration, g_mallocDuration / mallocCount);
245    printf("thread %d  calloc count[%d] totalTime[%f] meanTime[%f].\n", tid,
246        callocCount, g_callocDuration, g_callocDuration / callocCount);
247    printf("thread %d realloc count[%d] totalTime[%f] meanTime[%f].\n", tid,
248        reallocCount, g_reallocDuration, g_reallocDuration / reallocCount);
249    printf("thread %d    free count[%d] totalTime[%f] meanTime[%f].\n", tid,
250        freeCount, g_freeDuration, g_freeDuration / freeCount);
251    printf("finish thread %d\n", tid);
252
253    return nullptr;
254}
255
256// 打开文件到内存中
257static int OpenFile(const char* fileName)
258{
259    int fd = open(fileName, O_RDWR | O_CREAT, static_cast<mode_t>(0644)); // 0644 rw-r--r--
260    if (fd == -1) {
261        printf("can not open the file\n");
262        return -1;
263    }
264    return fd;
265}
266
267// 关闭文件
268static void CloseFile(void)
269{
270    if (g_fd > 0) {
271        close(g_fd);
272        g_fd = -1;
273    }
274}
275
276// 给文件建立内存映射
277static char* CreateMmap(void)
278{
279    if (g_fd == -1) {
280        return nullptr;
281    }
282
283    int size = PAGE_SIZE;
284    lseek(g_fd, size + 1, SEEK_SET);
285    write(g_fd, "", 1);
286
287    char* pMap = static_cast<char*>(mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, g_fd, 0));
288
289#ifdef HOOK_ENABLE
290    const char *tag = "memtesttag";
291    memtrace(pMap, PAGE_SIZE, tag, true);
292#endif
293    if (pMap == MAP_FAILED) {
294        printf("mmap fail\n");
295        CloseFile();
296    }
297    return pMap;
298}
299
300// 关闭文件内存映射
301static void RemoveMmap(char* pMap)
302{
303    munmap(pMap, PAGE_SIZE);
304}
305
306// 给文件映射中写入
307static void MmapWriteFile(char* pMap, int length, const char* data)
308{
309    if (memcpy_s(pMap, length, data, length) != EOK) {
310        printf("memcpy_s type fail\n");
311        return;
312    }
313    msync(pMap, length, MS_SYNC);
314}
315
316// 从文件映射中读取
317static char* MmapReadFile(const char* pMap, int length)
318{
319    if (length <= 0) {
320        printf("fail:malloc %d memory", length);
321        return nullptr;
322    }
323    char* data = static_cast<char*>(malloc(length + 1));
324    if (data != nullptr) {
325        memcpy_s(data, length, pMap, length);
326        data[length] = '\0';
327    }
328    return data;
329}
330
331static void RandSrand(void)
332{
333    srand(static_cast<unsigned>(time(nullptr)));
334}
335
336// 10 ~ 4096
337static int RandInt(int max, int min)
338{
339    int value = (rand() % (max - min)) + min;
340    return value;
341}
342
343// 生成一个随机字符 (0x20 ~ 0x7E)
344static char RandChar(void)
345{
346    // 可显示字符的范围
347    int section = '~' - ' ';
348    int randSection = RandInt(0, section);
349    char randChar = '~' + static_cast<unsigned int>(randSection);
350    return randChar;
351}
352
353// 获取随机长度的字符串
354static char* RandString(int maxLength)
355{
356    int strLength = RandInt(10, maxLength);
357    if (strLength <= 0) {
358        printf("fail:malloc %d memory", strLength);
359        return nullptr;
360    }
361    char* data = static_cast<char*>(malloc(strLength + 1));
362    if (data != nullptr) {
363        for (int i = 0; i < strLength; i++) {
364            data[i] = RandChar();
365        }
366    data[strLength] = '\0';
367    }
368    return data;
369}
370
371// 初始化函数
372static void MmapInit(void)
373{
374    // 设置随机种子
375    RandSrand();
376    // 设置全局映射的目标文件
377    g_fd = OpenFile(TEST_FILE_NAME);
378}
379
380// 写映射
381static void WriteMmap(const char* data)
382{
383    // 建立映射
384    char* pMap = CreateMmap();
385    if (data == nullptr || strlen(data) == 0) {
386        return;
387    }
388    // 写入
389    MmapWriteFile(pMap, strlen(data), data);
390    // 关闭映射
391    RemoveMmap(pMap);
392}
393
394// 读映射
395static char* ReadMmap(int length)
396{
397    // 建立映射
398    char* pMap = CreateMmap();
399
400    // 写入
401    char* outTestchar = MmapReadFile(pMap, length);
402
403    // 关闭映射
404    RemoveMmap(pMap);
405
406    return outTestchar;
407}
408
409static void* ThreadMmap(void* param)
410{
411    while (g_runing != 0) {
412        // 获取随机字符
413        char* randString = RandString(PAGE_SIZE);
414
415        // 写入映射
416        WriteMmap(randString);
417
418        // 从映射中读取
419        char* outchar = ReadMmap(strlen(randString));
420        printf("thread %ld : Mmap test OK! \n", syscall(SYS_gettid));
421        free(randString);
422        free(outchar);
423        sleep(SLEEP_TIME_SEC);
424    }
425    return nullptr;
426}
427
428// 维护hook test类型管理
429static int BitMapNum(unsigned int data)
430{
431    unsigned int tmp = data;
432    int num = 0;
433    while (tmp != 0) {
434        if ((tmp & 1) != 0) {
435            num++;
436        }
437        tmp >>= 1;
438    }
439    return num;
440}
441
442// 参数解析
443static int CommandParse(int argc, char** argv)
444{
445    int result;
446    opterr = 0;
447    while ((result = getopt(argc, argv, "t:s:n:o:h:")) != -1) {
448        switch (result) {
449            case 't':
450                // hook test的类型
451                if (!strcmp("mmap", optarg)) {
452                    printf("Type: %s \n", optarg);
453                    g_hookFlag |= MMAP_FLAG;
454                } else if (!strcmp("alloc", optarg)) {
455                    printf("Type: %s \n", optarg);
456                    g_hookFlag |= ALLOC_FLAG;
457                } else if (!strcmp("all", optarg)) {
458                    printf("Type: %s \n", optarg);
459                    g_hookFlag |= ALLOC_FLAG;
460                    g_hookFlag |= MMAP_FLAG;
461                }
462                break;
463            case 's':
464                // 栈大小
465                g_mallocSize = atoi(optarg);
466                if (g_mallocSize <= 0) {
467                    printf("Invalid mallocSize\n");
468                    return -1;
469                }
470                break;
471            case 'n':
472                // 线程数
473                g_threadNum = atoi(optarg);
474                if (g_threadNum <= 0) {
475                    printf("Invalid threadNum.\n");
476                    return -1;
477                }
478                break;
479            case 'o':
480                TEST_FILE_NAME = optarg;
481                break;
482            case 'h':
483            default:
484                printf("%s -t <alloc/mmap>\n", argv[0]);
485                printf("\talloc : -s [alloc mallocSize] -n [thread Num]\n");
486                printf("\t mmap : -o [mmap datafile]\n");
487                return -1;
488        }
489    }
490    return opterr;
491}
492} // namespace
493
494int main(int argc, char* argv[])
495{
496    // 参数解析
497    int ret = CommandParse(argc, argv);
498    if (ret == -1) {
499        return 0;
500    }
501    int typeNum = BitMapNum(g_hookFlag);
502    printf(" g_hookFlag =  [%u] \n", g_hookFlag);
503    if (typeNum == 0) {
504        // 未设置type时默认启动alloc
505        g_hookFlag |= ALLOC_FLAG;
506        typeNum++;
507    }
508
509    pthread_t** thrArrayList = static_cast<pthread_t**>(malloc(sizeof(pthread_t*) * typeNum));
510    if (thrArrayList == nullptr) {
511        printf("malloc thrArrayList fail\n");
512        return 0;
513    }
514    int type = 0;
515    if ((g_hookFlag & ALLOC_FLAG) != 0) {
516        int threadNum = g_threadNum;
517        int mallocSize = g_mallocSize;
518
519        pid_t pid = getpid();
520        printf("Process pid %d, Test start %d thread, malloc %d size\n", pid, threadNum, mallocSize);
521
522        thrArrayList[type] = static_cast<pthread_t*>(malloc(sizeof(pthread_t) * threadNum));
523        // pthread_t* thrArray
524        if (thrArrayList[type] == nullptr) {
525            printf("new thread failed.\n");
526        }
527        int idx;
528        for (idx = 0; idx < threadNum; ++idx) {
529            int result = pthread_create((thrArrayList[type]) + idx, nullptr,
530                ThreadFuncC, static_cast<void*>(&mallocSize));
531            if (result != 0) {
532                printf("Creating thread failed.\n");
533            }
534        }
535        type++;
536    }
537
538    if ((g_hookFlag & MMAP_FLAG) != 0) {
539        int threadNum = g_threadNum;
540        // 初始化
541        MmapInit();
542
543        thrArrayList[type] = static_cast<pthread_t*>(malloc(sizeof(pthread_t) * threadNum));
544        if (thrArrayList[type] == nullptr) {
545            printf("new thread failed.\n");
546        }
547
548        int idx;
549        for (idx = 0; idx < threadNum; ++idx) {
550            int result = pthread_create((thrArrayList[type]) + idx,
551                nullptr, ThreadMmap, nullptr);
552            if (result != 0) {
553                printf("Creating thread failed.\n");
554            }
555        }
556    }
557
558    while (getchar() != '\n') {
559        usleep(RESPONSE_SPEED);
560    };
561    g_runing = 0;
562    int idx;
563    for (type = 0; type < typeNum; type++) {
564        for (idx = 0; idx < g_threadNum; ++idx) {
565            pthread_join((thrArrayList[type])[idx], nullptr);
566        }
567        if (thrArrayList[type] != nullptr) {
568            free(thrArrayList[type]);
569            thrArrayList[type] = nullptr;
570        }
571    }
572    if (thrArrayList != nullptr) {
573        free(thrArrayList);
574        thrArrayList = nullptr;
575    }
576    CloseFile();
577    return 0;
578}
579
580#pragma clang optimize on
581