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