2 * Copyright 2013 Samsung Electronics Co., Ltd
4 * Licensed under the Flora License, Version 1.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://floralicense.org
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
35 #include "heap-monitor.h"
38 #define DbgPrint(format, arg...) LOGD("[
\e[32m%s/%s
\e[0m:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg)
39 #define ErrPrint(format, arg...) LOGE("[
\e[32m%s/%s
\e[0m:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg)
41 extern FILE *__file_log_fp;
42 #define DbgPrint(format, arg...) do { fprintf(__file_log_fp, "[LOG] [
\e[32m%s/%s
\e[0m:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0)
44 #define ErrPrint(format, arg...) do { fprintf(__file_log_fp, "[ERR] [
\e[32m%s/%s
\e[0m:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0)
48 #define container_of(ptr, type, member) \
49 ({ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
50 (type *)( (char *)__mptr - offsetof(type,member) );})
52 #define API __attribute__((visibility("default")))
53 #define DUMP_DEPTH 80000
55 #if defined(_ESTIMATE_PERFORMANCE)
56 #define ESTIMATE_START() \
61 static struct timeval ttv; \
62 static int initialized = 0; \
67 gettimeofday(&stv, NULL)
69 #define ESTIMATE_END() \
70 gettimeofday(&etv, NULL); \
71 gettimeofday(&etv, NULL); \
72 timersub(&etv, &stv, &rtv); \
73 timeradd(&rtv, &ttv, &ttv); \
74 DbgPrint("Elapsed time: %lu.%lf\n", rtv.tv_sec, (double)rtv.tv_usec / 1000000.0f); \
75 DbgPrint("Total: %lu.%lf\n", ttv.tv_sec, (double)ttv.tv_usec / 1000000.0f); \
78 #define ESTIMATE_START() // pthread_mutex_lock(&s_info.mutex)
79 #define ESTIMATE_END() // pthread_mutex_unlock(&s_info.mutex)
82 #if defined(_THREAD_SAFETY)
83 #define LOCK() pthread_mutex_lock(&s_info.mutex)
84 #define UNLOCK() pthread_mutex_unlock(&s_info.mutex)
90 typedef void *(*malloc_t)(size_t size, const void *caller);
91 typedef void *(*realloc_t)(void *ptr, size_t size, const void *caller);
92 typedef void (*free_t)(void *ptr, const void *caller);
93 typedef void *(*memalign_t)(size_t align, size_t size, const void *caller);
103 INVALID = 0xdeaddead,
108 #define SET_PAD(ptr, pad) (*(((int *)ptr) - 2) = (pad))
109 #define SET_STATE(ptr, state) (*(((int *)ptr) - 1) = (state))
110 #define PAD(ptr) (*(((int *)ptr) - 2))
111 #define STATE(ptr) (*(((int *)ptr) - 1))
124 memalign_t _memalign;
125 struct dlist *target_list;
128 unsigned long stack_boundary;
129 unsigned long stack_base;
131 pthread_mutex_t mutex;
144 .dump_depth = DUMP_DEPTH,
146 .mutex = PTHREAD_MUTEX_INITIALIZER,
153 static void unhook(void);
154 static void hook(void);
156 static inline struct target *find_target_by_name(const char *name)
159 struct target *target;
161 dlist_foreach(s_info.target_list, l, target) {
162 if (!strcmp(target->name, name))
169 static inline struct target *find_target_by_addr(unsigned long addr)
172 struct target *target;
174 dlist_foreach(s_info.target_list, l, target) {
175 if (target->begin <= addr && addr < target->end)
185 ret = dladdr((void *)*stack, &info);
189 target = find_target_by_name(info.dli_fname);
191 static struct target *find_target_info(void)
194 register struct target *target;
195 register unsigned long *stack;
198 if (s_info.target_cnt <= 0)
201 stack = (unsigned long *)&ret;
203 if (s_info.stack_boundary) {
206 unsigned long boundary;
208 tid = pthread_self();
209 if (!pthread_equal(tid, s_info.main_tid)) {
214 if (!pthread_getattr_np(tid, &attr)) {
215 if (!pthread_attr_getstack(&attr, (void *)&base, &size))
216 boundary = base + size;
217 pthread_attr_destroy(&attr);
220 base = s_info.stack_base;
221 boundary = s_info.stack_boundary;
225 while ((unsigned long)stack >= base && (unsigned long)stack < boundary) {
227 if (s_info.debugger) {
228 DbgPrint("FP: %p, boundary: %p, INDEX: %d, ret: 0x%X\n",
229 stack, boundary, i, *stack);
232 target = find_target_by_addr(*stack++);
237 DbgPrint("Target[%s]: %d\n", target->name, target->usage);
242 for (i = 0; i < s_info.dump_depth; i++) {
243 target = find_target_by_addr(*(stack++));
248 DbgPrint("[%d] Target[%s]: %d\n", i, target->name, target->usage);
257 static void mcheck_cb(enum mcheck_status status)
259 struct target *target;
261 case MCHECK_DISABLED:
262 ErrPrint("mcheck is disabled\n");
265 ErrPrint("Consitency is ok\n");
268 target = find_target_info();
270 ErrPrint("[HEAD] Inconsistency: %s\n", target->name);
273 target = find_target_info();
275 ErrPrint("[HEAD] Inconsistency: %s\n", target->name);
278 target = find_target_info();
280 ErrPrint("[FREE] Inconsistency: %s\n", target->name);
287 static inline int MPROBE(void *ptr)
289 enum mcheck_status status;
291 status = mprobe(ptr);
292 if (status == MCHECK_DISABLED)
295 if (status != MCHECK_OK) {
303 static void *heap_monitor_malloc(size_t size, const void *caller)
311 chunk = malloc(size + sizeof(*chunk));
313 chunk->info = find_target_info();
318 SET_STATE(ptr, VALID);
321 chunk->info->usage += size;
330 static void heap_monitor_free(void *ptr, const void *caller)
341 if (STATE(ptr) != VALID) {
342 DbgPrint("Unrecognizable chunk, do default operation\n");
347 chunk = container_of((void *)(((char *)ptr) - PAD(ptr)), struct chunk, data);
349 chunk->info->usage -= chunk->size;
352 SET_STATE(ptr, INVALID);
356 DbgPrint("Successfully recovered\n");
358 ErrPrint("Failed to recover\n");
359 chunk = NULL; /* Do nothing */
372 static void *heap_monitor_realloc(void *__ptr, size_t size, const void *caller)
385 chunk = realloc(__ptr, size + sizeof(*chunk));
387 chunk->info = find_target_info();
392 SET_STATE(ptr, VALID);
395 chunk->info->usage += size;
397 } else if (size == 0) {
399 if (STATE(__ptr) != VALID) {
400 DbgPrint("Unrecognizable chunk, do default operation\n");
401 ptr = realloc(__ptr, size);
403 chunk = container_of((void *)(((char *)__ptr) - PAD(__ptr)), struct chunk, data);
405 chunk->info->usage -= chunk->size;
408 SET_STATE(__ptr, INVALID);
412 DbgPrint("Successfully recovered\n");
414 ErrPrint("Failed to recover\n");
420 ptr = realloc(chunk, size);
423 if (STATE(__ptr) != VALID) {
424 DbgPrint("Unrecognizable chunk, do default operation\n");
425 ptr = realloc(__ptr, size);
427 int pad = PAD(__ptr);
429 chunk = container_of((void *)(((char *)__ptr) - pad), struct chunk, data);
431 ptr = realloc(chunk, size + sizeof(*chunk) + pad);
433 struct chunk *new_chunk = ptr;
435 if (new_chunk->info) {
436 new_chunk->info->usage -= new_chunk->size;
437 new_chunk->info->usage += size;
439 new_chunk->size = size;
440 ptr = new_chunk->data + pad;
442 SET_STATE(ptr, VALID);
444 /* Consider this, do we need to keep the alignment for realloc? */
448 if (!MPROBE(chunk)) {
449 ErrPrint("Failed to recover\n");
454 ptr = realloc(chunk, size + sizeof(*chunk));
457 struct chunk *new_chunk;
461 ErrPrint("Heap: %s\n", strerror(errno));
466 memcpy(tmp, ptr, size);
467 memcpy(((char *)ptr) + sizeof(*chunk), tmp, size);
471 new_chunk->info = find_target_info();
473 new_chunk->info->usage += size;
474 new_chunk->size = size;
475 ptr = new_chunk->data;
476 SET_STATE(ptr, VALID);
479 DbgPrint("Successfully recovered\n");
481 DbgPrint("Failed to recover\n");
494 static void *heap_monitor_memalign(size_t align, size_t __size, const void *caller)
508 pad = align - (sizeof(*chunk) % align);
509 size = sizeof(*chunk) + pad;
510 chunk = memalign(align, size + __size);
514 chunk->info = find_target_info();
516 chunk->info->usage += __size;
518 chunk->size = __size;
520 ptr = chunk->data + pad;
522 SET_STATE(ptr, VALID);
531 static void hook(void)
534 if (s_info.hook == 1) {
535 __malloc_hook = heap_monitor_malloc;
536 __realloc_hook = heap_monitor_realloc;
537 __memalign_hook = heap_monitor_memalign;
538 __free_hook = heap_monitor_free;
542 static void unhook(void)
545 if (s_info.hook == 0) {
546 __malloc_hook = s_info._malloc;
547 __realloc_hook = s_info._realloc;
548 __memalign_hook = s_info._memalign;
549 __free_hook = s_info._free;
553 void (*__malloc_initialize_hook)(void) = heap_monitor_init;
555 static int iterator_cb(struct dl_phdr_info *info, size_t size, void *data)
557 struct target *target;
559 const ElfW(Phdr) *phdr;
563 if (strcmp(info->dlpi_name, target->name))
566 for (i = 0; i < info->dlpi_phnum; i++) {
567 phdr = info->dlpi_phdr + i;
568 if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_X)) {
569 target->begin = info->dlpi_addr + (unsigned long)phdr->p_vaddr;
570 target->end = target->begin + phdr->p_memsz;
572 DbgPrint("Target: %s - [%s] 0x%lX - 0x%lX\n",
573 target->name, info->dlpi_name, target->begin, target->end);
581 API int heap_monitor_del_target(const char *name)
585 struct target *target;
591 dlist_foreach_safe(s_info.target_list, l, n, target) {
592 if (!strcmp(target->name, name)) {
593 s_info.target_list = dlist_remove(s_info.target_list, l);
607 API int heap_monitor_add_target(const char *name)
609 struct target *target;
616 target = find_target_by_name(name);
620 target = malloc(sizeof(*target));
622 DbgPrint("Heap: %s\n", strerror(errno));
627 target->name = strdup(name);
629 DbgPrint("Heap: %s\n", strerror(errno));
639 dl_iterate_phdr(iterator_cb, target);
642 s_info.target_list = dlist_append(s_info.target_list, target);
649 API size_t heap_monitor_target_usage(const char *name)
651 struct target *target;
654 target = find_target_by_name(name);
657 return target ? target->usage : 0;
660 API int heap_monitor_initialized(void)
662 return s_info.initialized;
665 API void heap_monitor_set_stack_boundary(unsigned long stack_boundary)
667 s_info.stack_boundary = stack_boundary;
670 API void heap_monitor_init(void)
675 if (s_info.initialized)
678 #if defined(_ENABLE_MCHECK)
679 s_info.m_check = mcheck(mcheck_cb);
680 if (s_info.m_check < 0)
681 ErrPrint("Failed to install mcheck[%d]\n", s_info.m_check);
683 DbgPrint("mcheck installed: %d\n", s_info.m_check);
686 s_info._malloc = __malloc_hook;
687 s_info._realloc = __realloc_hook;
688 s_info._free = __free_hook;
689 s_info._memalign = __memalign_hook;
691 var = getenv("HEAP_MONITOR_DUMP_DEPTH");
693 s_info.dump_depth = atoi(var);
695 var = getenv("HEAP_MONITOR_DEBUGGER");
697 s_info.debugger = atoi(var);
699 s_info.main_tid = pthread_self();
700 s_info.initialized = 1;
702 if (pthread_getattr_np(s_info.main_tid, &attr))
705 if (pthread_attr_getstack(&attr, (void *)&s_info.stack_base, &s_info.stack_size)) {
706 s_info.stack_base = 0;
707 s_info.stack_size = 0;
709 s_info.stack_boundary = s_info.stack_base + s_info.stack_size;
712 pthread_attr_destroy(&attr);
713 DbgPrint("Initialized\n");
716 API void heap_monitor_start(void)
718 if (!s_info.initialized)
724 API void heap_monitor_stop(void)
726 if (!s_info.initialized)