1 #define _GNU_SOURCE
2 #include <errno.h>
3 #include <string.h>
4 #include <stdbool.h>
5 #include <debug.h>
6 #include <fcntl.h>
7 #include <dlfcn.h>
8 #include <unistd.h>
9 #include "libc.h"
10 #include "atomic.h"
11 #include "pthread_impl.h"
12 #include "malloc_impl.h"
13
14 #if defined(__GNUC__) && defined(__PIC__)
15 #define inline inline __attribute__((always_inline))
16 #endif
17
18 bool g_enable_check = false;
19 int g_recycle_num;
20 size_t g_recycle_size;
21 int g_mem_lock[2];
22 static struct chunk recycle_list;
23 static struct heap_block block_list;
24
25 static struct {
26 struct stat_bin bins[PTHREAD_NUM_MAX];
27 struct stat_bin free_list;
28 size_t p_total_size;
29 size_t peak_size;
30 char *f_path;
31 char f_path_buf[PATH_MAX];
32 int fd;
33 bool verbose;
34 } mem_stat;
35
lock(volatile int * lk)36 static inline void lock(volatile int *lk)
37 {
38 if (libc.threads_minus_1)
39 while(a_swap(lk, 1)) __wait(lk, lk+1, 1, 1);
40 }
41
unlock(volatile int * lk)42 static inline void unlock(volatile int *lk)
43 {
44 if (lk[0]) {
45 a_store(lk, 0);
46 if (lk[1]) __wake(lk, 1, 1);
47 }
48 }
49
lock_stat_bin(int tid)50 static inline void lock_stat_bin(int tid)
51 {
52 lock(mem_stat.bins[tid].lock);
53 if (!mem_stat.bins[tid].head.next)
54 mem_stat.bins[tid].head.next = mem_stat.bins[tid].head.prev = &mem_stat.bins[tid].head;
55 }
56
unlock_stat_bin(int tid)57 static inline void unlock_stat_bin(int tid)
58 {
59 unlock(mem_stat.bins[tid].lock);
60 }
61
insert_free_list(struct node * node)62 static void insert_free_list(struct node *node)
63 {
64 struct list *list = NULL;
65
66 list = mem_stat.free_list.head.prev;
67 node->list.prev = list;
68 node->list.next = list->next;
69 list->next = &node->list;
70 node->list.next->prev = &node->list;
71 }
72
try_delete_node(int tid,void * ptr)73 static int try_delete_node(int tid, void *ptr)
74 {
75 struct list *list = NULL;
76 struct node *node = NULL;
77
78 lock_stat_bin(tid);
79 for (list = mem_stat.bins[tid].head.next; list != &mem_stat.bins[tid].head; list = list->next) {
80 node = (struct node *)((uintptr_t)list - (uint32_t)&((struct node *)0)->list);
81 if (node->ptr != ptr) {
82 continue;
83 }
84 list->prev->next = list->next;
85 list->next->prev = list->prev;
86 mem_stat.bins[tid].t_total_size -= node->size;
87 insert_free_list(node);
88 mem_stat.p_total_size -= node->size;
89 unlock_stat_bin(tid);
90 return 0;
91 }
92 unlock_stat_bin(tid);
93 return -1;
94 }
95
delete_node(void * ptr)96 int delete_node(void *ptr)
97 {
98 int tid = ((struct pthread *)pthread_self())->tid;
99 int status, i;
100
101 lock(g_mem_lock);
102 status = try_delete_node(tid, ptr);
103 if (status == 0) {
104 unlock(g_mem_lock);
105 return 0;
106 }
107
108 for (i = 0; i < PTHREAD_NUM_MAX; ++i) {
109 if (i == tid) {
110 continue;
111 }
112 status = try_delete_node(i, ptr);
113 if (status == 0) {
114 unlock(g_mem_lock);
115 return 0;
116 }
117 }
118 unlock(g_mem_lock);
119 return -1;
120 }
121
expand_mem(void)122 static struct node *expand_mem(void)
123 {
124 struct node *ptr = NULL;
125 struct node *node = NULL;
126 size_t node_len = sizeof(struct node);
127 int n_node = PAGE_SIZE / node_len;
128 int i;
129
130 ptr = __mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
131 if (ptr == MAP_FAILED) {
132 printf("%s %d, map failed, err: %s\n", __func__, __LINE__, strerror(errno));
133 return NULL;
134 }
135
136 for (i = 1; i < n_node; ++i) {
137 node = (struct node *)((uintptr_t)ptr + i * node_len);
138 insert_free_list(node);
139 }
140
141 return ptr;
142 }
143
alloc_node(void)144 static struct node *alloc_node(void)
145 {
146 struct list *list = NULL;
147 struct node *node = NULL;
148 int ret;
149
150 if (!mem_stat.free_list.head.next) {
151 mem_stat.free_list.head.next = mem_stat.free_list.head.prev = &mem_stat.free_list.head;
152 }
153
154 for (list = mem_stat.free_list.head.next; list != &mem_stat.free_list.head; list = list->next) {
155 node = (struct node *)((uintptr_t)list - (uint32_t)&((struct node *)0)->list);
156 list->prev->next = list->next;
157 list->next->prev = list->prev;
158 return node;
159 }
160
161 return expand_mem();
162 }
163
create_node(int tid,void * ptr,size_t size)164 static struct node *create_node(int tid, void *ptr, size_t size)
165 {
166 pid_t pid = getpid();
167 struct node *node = NULL;
168 void *lr[BACKTRACE_DEPTH_MAX + BACKTRACE_OFFSET + 1] = { 0 };
169 int nptr;
170
171 node = alloc_node();
172 if (node == NULL) {
173 return NULL;
174 }
175 mem_stat.p_total_size += size;
176 mem_stat.peak_size = (mem_stat.peak_size < mem_stat.p_total_size) ? mem_stat.p_total_size : mem_stat.peak_size;
177 node->tid = tid;
178 node->pid = pid;
179 node->ptr = ptr;
180 node->size = size;
181 nptr = backtrace(lr, BACKTRACE_DEPTH_MAX + BACKTRACE_OFFSET + 1);
182 memcpy(node->lr, lr + BACKTRACE_OFFSET + 1, BACKTRACE_DEPTH_MAX * sizeof(void *));
183 return node;
184 }
185
insert_node(void * ptr,size_t size)186 void insert_node(void *ptr, size_t size)
187 {
188 int tid = ((struct pthread *)pthread_self())->tid;
189 struct list *list = NULL;
190 struct node *node = NULL;
191
192 lock(g_mem_lock);
193 node = create_node(tid, ptr, size);
194 if (node == NULL) {
195 unlock(g_mem_lock);
196 return;
197 }
198
199 lock_stat_bin(tid);
200 mem_stat.bins[tid].t_total_size += size;
201 list = mem_stat.bins[tid].head.prev;
202 node->list.prev = list;
203 node->list.next = list->next;
204 list->next = &node->list;
205 node->list.next->prev = &node->list;
206 unlock_stat_bin(tid);
207 unlock(g_mem_lock);
208 }
209
file_path_init(void)210 static void file_path_init(void)
211 {
212 char *pos = NULL;
213 int len;
214
215 if (!mem_stat.f_path) {
216 return;
217 }
218
219 pos = strrchr(mem_stat.f_path, '/');
220 if (pos) {
221 len = pos - mem_stat.f_path + 1;
222 strncpy(mem_stat.f_path_buf, mem_stat.f_path, PATH_MAX - 1);
223 snprintf(mem_stat.f_path_buf + len, PATH_MAX - len, "pid(%d)_%s", getpid(), pos + 1);
224 } else {
225 snprintf(mem_stat.f_path_buf, PATH_MAX, "pid(%d)_%s", getpid(), mem_stat.f_path);
226 }
227 }
228
get_file(void)229 static bool get_file(void)
230 {
231 if (!g_enable_check) {
232 printf("You should call mem_check_init(char *) or use command line parameters, "
233 "--mwatch or --mrecord <full path>, to call mem_check_init(char *) "
234 "when executing your program.\n");
235 return false;
236 }
237
238 if (mem_stat.verbose) {
239 return true;
240 }
241
242 file_path_init();
243 if (!access(mem_stat.f_path_buf, 0)) {
244 return true;
245 }
246 mem_stat.fd = open(mem_stat.f_path_buf, O_RDWR | O_CREAT);
247 if (mem_stat.fd < 0) {
248 printf("err: %s create failed, memory info will output from serial port!\n", mem_stat.f_path_buf);
249 mem_stat.verbose = true;
250 }
251 return true;
252 }
253
get_backtrace_info(void ** buffer,int nptr,int fd,bool verbose,bool checkpoint)254 static int get_backtrace_info(void **buffer, int nptr, int fd, bool verbose, bool checkpoint)
255 {
256 int i, ret;
257 char str_buf[ITEM_BUFFER_SIZE];
258 Dl_info info = { 0 };
259 bool checkpoint_head = false;
260 int checkpoint_trace_num = 0;
261 bool symbol_found;
262
263 for (i = 0; i < nptr; ++i) {
264 symbol_found = true;
265 dladdr((void *)buffer[i], &info);
266 if ((info.dli_fname == NULL) || (info.dli_fname[0] == '\0')) {
267 symbol_found = false;
268 }
269
270 if (checkpoint && !checkpoint_head) {
271 checkpoint_head = true;
272 if (verbose) {
273 printf(" [Check point]:\n");
274 } else {
275 snprintf(str_buf, ITEM_BUFFER_SIZE, " [Check point]:\n");
276 ret = write(fd, str_buf, strlen(str_buf));
277 if (ret != strlen(str_buf)) {
278 goto err;
279 }
280 }
281 }
282 if (verbose) {
283 symbol_found ?
284 printf("\t#%02d: <%s+%#x>[%#x] -> %s\n", i, info.dli_sname, (uintptr_t)buffer[i] -
285 (uintptr_t)info.dli_saddr, (uintptr_t)buffer[i] - (uintptr_t)info.dli_fbase, info.dli_fname) :
286 printf("\t#%02d: %#x\n", i, buffer[i]);
287 } else {
288 symbol_found ?
289 snprintf(str_buf, ITEM_BUFFER_SIZE, "\t#%02d: <%s+%#x>[%#x] -> %s\n", i, info.dli_sname,
290 (uintptr_t)buffer[i] - (uintptr_t)info.dli_saddr, (uintptr_t)buffer[i] - (uintptr_t)info.dli_fbase,
291 info.dli_fname) :
292 snprintf(str_buf, ITEM_BUFFER_SIZE, "\t#%02d: %#x\n", i, buffer[i]);
293 ret = write(fd, str_buf, strlen(str_buf));
294 if (ret != strlen(str_buf)) {
295 goto err;
296 }
297 }
298 if (checkpoint) {
299 checkpoint_trace_num++;
300 if (checkpoint_trace_num == CHECK_POINT_TRACE_MAX) {
301 break;
302 }
303 }
304 }
305 return 0;
306 err:
307 printf("Write failed, err: %s\n", strerror(errno));
308 return ret;
309 }
310
print_integrity_info(struct node * node)311 static int print_integrity_info(struct node *node)
312 {
313 int ret;
314 char buffer[ITEM_BUFFER_SIZE];
315 char *str = "The possible attacker was allocated from:";
316
317 if (mem_stat.verbose) {
318 printf("\n==PID:%d== Memory integrity information:\n", getpid());
319 printf(" [TID:%d PID:%d allocated addr: %#x, size: %#x] %s\n", node->tid, node->pid, node->ptr, node->size,
320 str);
321 } else {
322 snprintf(buffer, ITEM_BUFFER_SIZE, "\n==PID:%d== Memory integrity information:\n", getpid());
323 ret = write(mem_stat.fd, buffer, strlen(buffer));
324 if (ret != strlen(buffer)) {
325 goto err;
326 }
327 snprintf(buffer, ITEM_BUFFER_SIZE, " [TID:%d PID:%d allocated addr: %#x, size: %#x] %s\n", node->tid, node->pid,
328 node->ptr, node->size, str);
329 ret = write(mem_stat.fd, buffer, strlen(buffer));
330 if (ret != strlen(buffer)) {
331 goto err;
332 }
333 }
334 return 0;
335 err:
336 printf("Write failed, err: %s\n", strerror(errno));
337 return ret;
338 }
339
check_mem_integrity(int tid,void * ptr)340 static int check_mem_integrity(int tid, void *ptr)
341 {
342 struct list *list = NULL;
343 struct node *node = NULL;
344 int nptr = 0;
345
346 lock_stat_bin(tid);
347 for (list = mem_stat.bins[tid].head.next; list != &mem_stat.bins[tid].head; list = list->next) {
348 node = (struct node *)((uintptr_t)list - (uint32_t)&((struct node *)0)->list);
349 if (node->ptr != ptr) {
350 continue;
351 }
352 if (print_integrity_info(node) != 0) {
353 unlock_stat_bin(tid);
354 printf("Memory integrity check failed!\n");
355 return -1;
356 }
357 while (node->lr[nptr] != NULL) {
358 ++nptr;
359 if (nptr == BACKTRACE_DEPTH_MAX) {
360 break;
361 }
362 }
363 if ((nptr == 0) || (get_backtrace_info(node->lr, nptr, mem_stat.fd, mem_stat.verbose, false) != 0)) {
364 unlock_stat_bin(tid);
365 printf("get backtrace failed!\n");
366 return -1;
367 }
368 if (!mem_stat.verbose) {
369 printf("Memory integrity information saved in %s\n", mem_stat.f_path_buf);
370 }
371 unlock_stat_bin(tid);
372 return 0;
373 }
374 unlock_stat_bin(tid);
375 return 1;
376 }
377
get_integrity_info(void * ptr)378 static void get_integrity_info(void *ptr)
379 {
380 int i, status;
381 int tid = ((struct pthread *)pthread_self())->tid;
382
383 status = check_mem_integrity(tid, ptr);
384 if (status != 1) {
385 return;
386 }
387
388 for (i = 0; i < PTHREAD_NUM_MAX; ++i) {
389 if (i == tid) {
390 continue;
391 }
392 status = check_mem_integrity(i, ptr);
393 if (status != 1) {
394 return;
395 }
396 }
397 }
398
is_invalid(struct chunk * self)399 bool is_invalid(struct chunk *self)
400 {
401 uint32_t checksum;
402 checksum = CHUNK_SIZE(self) ^ CHUNK_PSIZE(self) ^ NODE_MAGIC;
403 if (checksum != self->checksum) {
404 return true;
405 } else {
406 return false;
407 }
408 }
409
calculate_checksum(struct chunk * cur,struct chunk * next)410 void calculate_checksum(struct chunk *cur, struct chunk *next)
411 {
412 if (cur) {
413 cur->checksum = CHUNK_SIZE(cur) ^ CHUNK_PSIZE(cur) ^ NODE_MAGIC;
414 }
415
416 if (next) {
417 next->checksum = CHUNK_SIZE(next) ^ CHUNK_PSIZE(next) ^ NODE_MAGIC;
418 }
419 }
420
check_heap_integrity(void)421 void check_heap_integrity(void)
422 {
423 struct chunk *cur = NULL;
424 struct chunk *next = NULL;
425 struct heap_block *block = NULL;
426
427 if (!block_list.next) {
428 return;
429 }
430
431 lock(g_mem_lock);
432 if (!get_file()) {
433 unlock(g_mem_lock);
434 return;
435 }
436 block = block_list.next;
437 while (block != &block_list) {
438 cur = BLOCK_TO_CHUNK(block);
439 do {
440 next = NEXT_CHUNK(cur);
441 if (is_invalid(next)) {
442 get_integrity_info(CHUNK_TO_MEM(cur));
443 unlock(g_mem_lock);
444 a_crash();
445 }
446 cur = next;
447 } while (CHUNK_SIZE(next));
448 block = block->next;
449 }
450 unlock(g_mem_lock);
451 printf("\nCheck heap integrity ok!\n");
452 }
453
check_chunk_integrity(struct chunk * cur)454 void check_chunk_integrity(struct chunk *cur)
455 {
456 struct chunk *next = NULL;
457
458 if (is_invalid(cur)) {
459 check_heap_integrity();
460 }
461
462 lock(g_mem_lock);
463 next = NEXT_CHUNK(cur);
464 if ((CHUNK_SIZE(next)) && is_invalid(next)) {
465 get_integrity_info(CHUNK_TO_MEM(cur));
466 unlock(g_mem_lock);
467 a_crash();
468 }
469 unlock(g_mem_lock);
470 }
471
insert_block_list(struct chunk * self)472 void insert_block_list(struct chunk *self)
473 {
474 struct heap_block *block = CHUNK_TO_BLOCK(self);
475 struct heap_block *cur = NULL;
476
477 if (!block_list.next) {
478 block_list.next = block_list.prev = &block_list;
479 }
480
481 cur = block_list.prev;
482 block->next = cur->next;
483 block->prev = cur;
484 cur->next = block;
485 block_list.prev = block;
486 }
487
get_free_trace(void * ptr)488 void get_free_trace(void *ptr)
489 {
490 void *lr[BACKTRACE_DEPTH_MAX + BACKTRACE_OFFSET] = { 0 };
491 int tid = ((struct pthread *)pthread_self())->tid;
492 char buffer[ITEM_BUFFER_SIZE];
493 int nptr, ret;
494
495 lock(g_mem_lock);
496 if (!get_file()) {
497 unlock(g_mem_lock);
498 return;
499 }
500 if (mem_stat.verbose) {
501 printf("\n==PID:%d== double free\n", getpid());
502 printf(" [TID:%d freed addr: %#x]:\n", tid, ptr);
503 } else {
504 snprintf(buffer, ITEM_BUFFER_SIZE, "\n==PID:%d== double free\n", getpid());
505 ret = write(mem_stat.fd, buffer, strlen(buffer));
506 if (ret != strlen(buffer)) {
507 goto err;
508 }
509 snprintf(buffer, ITEM_BUFFER_SIZE, " [TID:%d freed addr: %#x]:\n", tid, ptr);
510 ret = write(mem_stat.fd, buffer, strlen(buffer));
511 if (ret != strlen(buffer)) {
512 goto err;
513 }
514 }
515
516 nptr = backtrace(lr, BACKTRACE_DEPTH_MAX + BACKTRACE_OFFSET);
517 if (get_backtrace_info(lr + BACKTRACE_OFFSET, nptr - BACKTRACE_OFFSET, mem_stat.fd, mem_stat.verbose, false) != 0) {
518 printf("Trace failed\n");
519 }
520
521 unlock(g_mem_lock);
522 return;
523 err:
524 printf("Write failed, err: %s\n", strerror(errno));
525 unlock(g_mem_lock);
526 return;
527 }
528
watch_mem(void)529 void watch_mem(void)
530 {
531 int tid, ret;
532 char buffer[ITEM_BUFFER_SIZE];
533 void *lr[BACKTRACE_DEPTH_MAX + BACKTRACE_OFFSET] = { 0 };
534 pid_t pid = getpid();
535 int nptr;
536
537 lock(g_mem_lock);
538 if (!get_file()) {
539 unlock(g_mem_lock);
540 return;
541 }
542 if (mem_stat.verbose) {
543 printf("\n==PID:%d== Heap memory statistics(bytes):\n", pid);
544 } else {
545 snprintf(buffer, ITEM_BUFFER_SIZE, "\n==PID:%d== Heap memory statistics(bytes):\n", pid);
546 ret = write(mem_stat.fd, buffer, strlen(buffer));
547 if (ret != strlen(buffer)) {
548 goto err2;
549 }
550 }
551 nptr = backtrace(lr, BACKTRACE_DEPTH_MAX + BACKTRACE_OFFSET);
552 if (get_backtrace_info(lr + BACKTRACE_OFFSET, nptr - BACKTRACE_OFFSET, mem_stat.fd, mem_stat.verbose, true) != 0) {
553 printf("Check failed\n");
554 unlock(g_mem_lock);
555 return;
556 }
557 for (tid = 0; tid < PTHREAD_NUM_MAX; ++tid) {
558 lock_stat_bin(tid);
559 if (mem_stat.bins[tid].t_total_size == 0) {
560 unlock_stat_bin(tid);
561 continue;
562 }
563 if (mem_stat.verbose) {
564 printf("\n [TID: %d, Used: %#x]", tid, mem_stat.bins[tid].t_total_size);
565 } else {
566 snprintf(buffer, ITEM_BUFFER_SIZE, "\n [TID: %d, Used: %#x]", tid, mem_stat.bins[tid].t_total_size);
567 ret = write(mem_stat.fd, buffer, strlen(buffer));
568 if (ret != strlen(buffer)) {
569 goto err1;
570 }
571 }
572 unlock_stat_bin(tid);
573 }
574 if (mem_stat.verbose) {
575 printf("\n\n==PID:%d== Total heap: %#x byte(s), Peak: %#x byte(s)\n", pid,
576 mem_stat.p_total_size, mem_stat.peak_size);
577 } else {
578 snprintf(buffer, ITEM_BUFFER_SIZE, "\n\n==PID:%d== Total heap: %#x byte(s), Peak: %#x byte(s)\n", pid,
579 mem_stat.p_total_size, mem_stat.peak_size);
580 ret = write(mem_stat.fd, buffer, strlen(buffer));
581 if (ret != strlen(buffer)) {
582 goto err2;
583 }
584 }
585 if (!mem_stat.verbose) {
586 printf("Memory statistics information saved in %s\n", mem_stat.f_path_buf);
587 }
588 unlock(g_mem_lock);
589 return;
590 err1:
591 unlock_stat_bin(tid);
592 err2:
593 printf("Write failed, err: %s\n", strerror(errno));
594 unlock(g_mem_lock);
595 }
596
get_node_info(struct node * node,int fd,bool verbose,bool mem_leak_exist)597 static int get_node_info(struct node *node, int fd, bool verbose, bool mem_leak_exist)
598 {
599 char buffer[ITEM_BUFFER_SIZE];
600 void *lr[BACKTRACE_DEPTH_MAX + BACKTRACE_OFFSET] = { 0 };
601 int nptr, ret;
602
603 if (!mem_leak_exist) {
604 if (verbose) {
605 printf("\n==PID:%d== Detected memory leak(s):\n", getpid());
606 } else {
607 snprintf(buffer, ITEM_BUFFER_SIZE, "\n==PID:%d== Detected memory leak(s):\n", getpid());
608 ret = write(fd, buffer, strlen(buffer));
609 if (ret != strlen(buffer)) {
610 goto err;
611 }
612 }
613 nptr = backtrace(lr, BACKTRACE_DEPTH_MAX + BACKTRACE_OFFSET);
614 if (get_backtrace_info(lr + BACKTRACE_OFFSET, nptr - BACKTRACE_OFFSET, mem_stat.fd, mem_stat.verbose, true) != 0) {
615 printf("Check failed\n");
616 goto err;
617 }
618 }
619
620 if (verbose) {
621 printf("\n [TID:%d Leak:%#x byte(s)] Allocated from:\n", node->tid, node->size);
622 } else {
623 snprintf(buffer, ITEM_BUFFER_SIZE, "\n [TID:%d Leak:%#x byte(s)] Allocated from:\n", node->tid, node->size);
624 ret = write(fd, buffer, strlen(buffer));
625 if (ret != strlen(buffer)) {
626 goto err;
627 }
628 }
629 return 0;
630 err:
631 printf("Write failed, err: %s\n", strerror(errno));
632 return ret;
633
634 }
635
print_summary_info(size_t leak_size,size_t allocs,int fd,bool verbose,bool mem_leak_exist)636 static void print_summary_info(size_t leak_size, size_t allocs, int fd, bool verbose, bool mem_leak_exist)
637 {
638 char buffer[ITEM_BUFFER_SIZE];
639 int ret;
640
641 if (!mem_leak_exist) {
642 if (verbose) {
643 printf("\nNo memory leak!\n");
644 return;
645 } else {
646 snprintf(buffer, ITEM_BUFFER_SIZE, "\nNo memory leak!\n");
647 ret = write(fd, buffer, strlen(buffer));
648 if (ret != strlen(buffer)) {
649 printf("Write failed, err: %s\n", strerror(errno));
650 }
651 return;
652 }
653 }
654
655 if (verbose) {
656 printf("\n==PID:%d== SUMMARY: %#x byte(s) leaked in %d allocation(s).\n", getpid(), leak_size, allocs);
657 } else {
658 snprintf(buffer, ITEM_BUFFER_SIZE, "\n==PID:%d== SUMMARY: %#x byte(s) leaked in %d allocation(s).\n", getpid(),
659 leak_size, allocs);
660 ret = write(fd, buffer, strlen(buffer));
661 if (ret != strlen(buffer)) {
662 printf("Write failed, err: %s\n", strerror(errno));
663 }
664 }
665 }
666
check_leak(void)667 void check_leak(void)
668 {
669 struct list *list = NULL;
670 struct node *node = NULL;
671 int tid, nptr;
672 size_t leak_size = 0;
673 size_t allocs = 0;
674 bool mem_leak_exist = false;
675 pid_t pid = getpid();
676
677 lock(g_mem_lock);
678 if (!get_file()) {
679 unlock(g_mem_lock);
680 return;
681 }
682 for (tid = 0; tid < PTHREAD_NUM_MAX; ++tid) {
683 lock_stat_bin(tid);
684 for (list = mem_stat.bins[tid].head.next; list != &mem_stat.bins[tid].head; list = list->next) {
685 node = (struct node *)((uintptr_t)list - (uint32_t)&((struct node *)0)->list);
686 if (node->pid != pid) {
687 continue;
688 }
689 if (get_node_info(node, mem_stat.fd, mem_stat.verbose, mem_leak_exist) != 0) {
690 unlock_stat_bin(tid);
691 unlock(g_mem_lock);
692 printf("Check failed\n");
693 return;
694 }
695 ++allocs;
696 leak_size += node->size;
697 mem_leak_exist = true;
698 nptr = 0;
699 while (node->lr[nptr] != NULL) {
700 ++nptr;
701 if (nptr == BACKTRACE_DEPTH_MAX) {
702 break;
703 }
704 }
705 if (nptr == 0) {
706 continue;
707 }
708 if (get_backtrace_info(node->lr, nptr, mem_stat.fd, mem_stat.verbose, false) != 0) {
709 unlock_stat_bin(tid);
710 unlock(g_mem_lock);
711 printf("Check failed\n");
712 return;
713 }
714 }
715 unlock_stat_bin(tid);
716 }
717 print_summary_info(leak_size, allocs, mem_stat.fd, mem_stat.verbose, mem_leak_exist);
718 if (!mem_stat.verbose) {
719 printf("Leak check information saved in %s\n", mem_stat.f_path_buf);
720 }
721 unlock(g_mem_lock);
722 }
723
mem_check_init(char * f_path)724 void mem_check_init(char *f_path)
725 {
726 signal(35, watch_mem);
727 signal(36, check_leak);
728 signal(37, check_heap_integrity);
729 g_enable_check = true;
730 mem_stat.fd = -1;
731 const char *string = "memory info will print to serial port!";
732
733 if (!f_path) {
734 goto out;
735 }
736
737 if (strlen(f_path) > (PATH_MAX - PREFIX_PLACE_HOLDER - 1)) {
738 printf("file name: %s is too long, %s\n", f_path, string);
739 goto out;
740 }
741 mem_stat.f_path = f_path;
742 file_path_init();
743 mem_stat.fd = open(mem_stat.f_path_buf, O_RDWR | O_CREAT | O_EXCL);
744 if (mem_stat.fd < 0) {
745 switch (errno) {
746 case EEXIST:
747 printf("file: %s is exist, %s\n", mem_stat.f_path_buf, string);
748 goto out;
749 default:
750 printf("path: %s create failed, %s\n", mem_stat.f_path_buf, string);
751 goto out;
752 }
753 }
754 mem_stat.verbose = false;
755 return;
756
757 out:
758 mem_stat.verbose = true;
759 }
760
mem_check_deinit()761 void mem_check_deinit()
762 {
763 if (mem_stat.fd > 0) {
764 close(mem_stat.fd);
765 }
766 }
767
parse_argv(int argc,char * argv[])768 void parse_argv(int argc, char *argv[])
769 {
770
771 if (argc <= 1) {
772 return;
773 }
774
775 if (!strcmp(argv[argc - 1], "--mwatch")) {
776 mem_check_init(NULL);
777 } else if ((argc > 2) && (!strcmp(argv[argc - 2], "--mrecord"))) {
778 mem_check_init(argv[argc - 1]);
779 } else if (!strcmp(argv[argc - 1], "--mrecord")) {
780 printf("usage: --mrecord filepath\n");
781 }
782 }
783
insert_free_tail(struct chunk * self)784 void insert_free_tail(struct chunk *self)
785 {
786 volatile struct chunk *cur = NULL;
787 lock(g_mem_lock);
788 if (!recycle_list.next) {
789 recycle_list.next = recycle_list.prev = &recycle_list;
790 }
791 cur = recycle_list.prev;
792 self->next = cur->next;
793 self->prev = cur;
794 cur->next = self;
795 recycle_list.prev = self;
796 memset(CHUNK_TO_MEM(self), FREE_MAGIC, CHUNK_SIZE(self) - OVERHEAD);
797 ++g_recycle_num;
798 g_recycle_size += CHUNK_SIZE(self);
799 unlock(g_mem_lock);
800 }
801
get_free_head(void)802 struct chunk *get_free_head(void)
803 {
804 struct chunk *cur = NULL;
805 lock(g_mem_lock);
806 cur = recycle_list.next;
807 if ((cur == NULL) || (cur == &recycle_list)) {
808 unlock(g_mem_lock);
809 return NULL;
810 }
811 recycle_list.next = cur->next;
812 cur->next->prev = cur->prev;
813 --g_recycle_num;
814 g_recycle_size -= CHUNK_SIZE(cur);
815 unlock(g_mem_lock);
816 return cur;
817 }
818
clean_recycle_list(bool clean_all)819 void clean_recycle_list(bool clean_all)
820 {
821 struct chunk *self = NULL;
822 self = get_free_head();
823 while (self) {
824 __bin_chunk(self);
825 if ((!clean_all) && (g_recycle_size < RECYCLE_SIZE_MAX)) {
826 break;
827 }
828 self = get_free_head();
829 }
830 }
831
832