• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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