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 
40 namespace {
41 typedef struct {
42     int data[DATA_SIZE];
43 } StaticSpace;
44 
45 const static int STATIC_DEPTH = 5;
46 
47 static double g_mallocDuration = 0;
48 static double g_callocDuration = 0;
49 static double g_reallocDuration = 0;
50 static double g_freeDuration = 0;
51 
52 static int g_fd = -1;
53 static int g_runing = 1;
54 static int g_threadNum = 1;
55 static int g_mallocSize = 1;
56 static const char* TEST_FILE_NAME = "./mmapTest";
57 static unsigned int g_hookFlag = 0;
58 
DepthMalloc(int depth, int mallocSize)59 static 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 
DepthCalloc(int depth, int callocSize)72 static 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 
DepthRealloc(int depth, void* p, int reallocSize)82 static 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 
DepthFree(int depth, void* p)92 static 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 
ApplyForMalloc(int mallocSize)103 static 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 
ApplyForCalloc(int mallocSize)129 static 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 
ApplyForRealloc(int mallocSize)156 static 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 
NewString()194 static 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 
UniqueString()201 static void UniqueString()
202 {
203     auto pName = std::make_unique<std::string>("Hello");
204 }
205 
206 
ThreadFuncC(void* param)207 static 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 // 打开文件到内存中
OpenFile(const char* fileName)257 static 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 // 关闭文件
CloseFile(void)268 static void CloseFile(void)
269 {
270     if (g_fd > 0) {
271         close(g_fd);
272         g_fd = -1;
273     }
274 }
275 
276 // 给文件建立内存映射
CreateMmap(void)277 static 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 // 关闭文件内存映射
RemoveMmap(char* pMap)301 static void RemoveMmap(char* pMap)
302 {
303     munmap(pMap, PAGE_SIZE);
304 }
305 
306 // 给文件映射中写入
MmapWriteFile(char* pMap, int length, const char* data)307 static 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 // 从文件映射中读取
MmapReadFile(const char* pMap, int length)317 static 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 
RandSrand(void)331 static void RandSrand(void)
332 {
333     srand(static_cast<unsigned>(time(nullptr)));
334 }
335 
336 // 10 ~ 4096
RandInt(int max, int min)337 static int RandInt(int max, int min)
338 {
339     int value = (rand() % (max - min)) + min;
340     return value;
341 }
342 
343 // 生成一个随机字符 (0x20 ~ 0x7E)
RandChar(void)344 static 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 // 获取随机长度的字符串
RandString(int maxLength)354 static 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 // 初始化函数
MmapInit(void)372 static void MmapInit(void)
373 {
374     // 设置随机种子
375     RandSrand();
376     // 设置全局映射的目标文件
377     g_fd = OpenFile(TEST_FILE_NAME);
378 }
379 
380 // 写映射
WriteMmap(const char* data)381 static 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 // 读映射
ReadMmap(int length)395 static 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 
ThreadMmap(void* param)409 static 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类型管理
BitMapNum(unsigned int data)429 static 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 // 参数解析
CommandParse(int argc, char** argv)443 static 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 
main(int argc, char* argv[])494 int 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