From 63e7612ece0337c8e78364392988b41f90fbb65f Mon Sep 17 00:00:00 2001 From: Sung-jae Park Date: Fri, 23 Aug 2013 17:40:23 +0900 Subject: [PATCH] Replace malloc_hook with preload Change-Id: Icc6979bc5e64b6cfd98e862d44b28a580749963b --- CMakeLists.txt | 16 +- include/allocator.h | 25 ++ include/debug.h | 99 +++++ include/heap-monitor.h | 6 +- include/heap-monitor_internal.h | 22 ++ packaging/libheap-monitor.spec | 5 +- src/allocator.c | 283 +++++++++++++++ src/dlist.c | 7 +- src/heap-monitor.c | 780 ++++++++++++++++++++-------------------- 9 files changed, 840 insertions(+), 403 deletions(-) create mode 100644 include/allocator.h create mode 100644 include/debug.h create mode 100644 include/heap-monitor_internal.h create mode 100644 src/allocator.c diff --git a/CMakeLists.txt b/CMakeLists.txt index e0469e8..78549d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,32 +13,26 @@ set(CMAKE_SKIP_BUILD_RPATH true) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) -INCLUDE(FindPkgConfig) -pkg_check_modules(pkgs REQUIRED - dlog -) - -FOREACH(flag ${pkgs_CFLAGS}) - SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") -ENDFOREACH(flag) - SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden -Wall -Werror -Winline -fno-builtin-malloc -O3 -g -Wno-error=deprecated-declarations") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wno-error=deprecated-declarations") ADD_DEFINITIONS("-DPREFIX=\"${PREFIX}\"") ADD_DEFINITIONS("-DNDEBUG") +ADD_DEFINITIONS("-DFLOG") ADD_DEFINITIONS("-DLOG_TAG=\"HEAP_MONITOR\"") ADD_DEFINITIONS("-D_THREAD_SAFETY") +ADD_DEFINITIONS("-D_ESTIMATE_PERFORMANCE") ADD_LIBRARY(${PROJECT_NAME} SHARED - src/heap-monitor.c + src/allocator.c src/dlist.c + src/heap-monitor.c ) SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES SOVERSION ${VERSION_MAJOR}) SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES VERSION ${VERSION}) -TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} "-lpthread") +TARGET_LINK_LIBRARIES(${PROJECT_NAME} "-lpthread -ldl") CONFIGURE_FILE(${PROJECT_NAME}.pc.in ${PROJECT_NAME}.pc @ONLY) SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${PROJECT_NAME}.pc") diff --git a/include/allocator.h b/include/allocator.h new file mode 100644 index 0000000..406ec06 --- /dev/null +++ b/include/allocator.h @@ -0,0 +1,25 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +struct allocator; +extern struct allocator *allocator_init(unsigned int base, unsigned int size); +extern int allocator_fini(struct allocator *handle); +extern void *allocator_alloc(struct allocator *handle, int size); +extern void allocator_free(struct allocator *handle, void *ptr); +extern unsigned int allocator_allocated_size(void *ptr); +extern int allocator_in_scope(struct allocator *handle, void *addr); + +/* End of a file */ diff --git a/include/debug.h b/include/debug.h new file mode 100644 index 0000000..ac0cb53 --- /dev/null +++ b/include/debug.h @@ -0,0 +1,99 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#if !defined(FLOG) +#define DbgPrint(format, arg...) +#define ErrPrint(format, arg...) +#define WarnPrint(format, arg...) +#else +extern FILE *__file_log_fp; +#define DbgPrint(format, arg...) do { if (__file_log_fp) { fprintf(__file_log_fp, "[LOG] [%s/%s:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } } while (0) +#define ErrPrint(format, arg...) do { if (__file_log_fp) { fprintf(__file_log_fp, "[ERR] [%s/%s:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } } while (0) +#define WarnPrint(format, arg...) do { if (__file_log_fp) { fprintf(__file_log_fp, "[WRN] [%s/%s:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } } while (0) +#endif + +#if defined(_ESTIMATE_PERFORMANCE) +#define ESTIMATE_START() \ +do { \ + struct timeval stv; \ + struct timeval etv; \ + struct timeval rtv; \ + static struct timeval ttv; \ + static int initialized = 0; \ + if (!initialized) { \ + timerclear(&ttv); \ + initialized = 1; \ + } \ + DbgPrint("Start\n"); \ + gettimeofday(&stv, NULL) + +#define ESTIMATE_END() \ + gettimeofday(&etv, NULL); \ + gettimeofday(&etv, NULL); \ + timersub(&etv, &stv, &rtv); \ + timeradd(&rtv, &ttv, &ttv); \ + DbgPrint("Elapsed time: %lu.%lf\n", rtv.tv_sec, (double)rtv.tv_usec / 1000000.0f); \ + DbgPrint("Total: %lu.%lf\n", ttv.tv_sec, (double)ttv.tv_usec / 1000000.0f); \ +} while (0) + + +#else +#define ESTIMATE_START() // pthread_mutex_lock(&s_info.mutex) +#define ESTIMATE_END() // pthread_mutex_unlock(&s_info.mutex) +#endif + +#if defined(_THREAD_SAFETY) +#define CRITICAL_SECTION_BEGIN(handle) \ +do { \ + int ret; \ + ret = pthread_mutex_lock(handle); \ + if (ret != 0) { \ + ErrPrint("Failed to lock: %s\n", strerror(ret)); \ + } \ +} while (0) + +#define CRITICAL_SECTION_END(handle) \ +do { \ + int ret; \ + ret = pthread_mutex_unlock(handle); \ + if (ret != 0) { \ + ErrPrint("Failed to unlock: %s\n", strerror(ret)); \ + } \ +} while (0) + +#define CANCEL_SECTION_BEGIN() do { \ + int ret; \ + ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); \ + if (ret != 0) { \ + ErrPrint("Unable to set cancelate state: %s\n", strerror(ret)); \ + } \ +} while (0) + +#define CANCEL_SECTION_END() do { \ + int ret; \ + ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); \ + if (ret != 0) { \ + ErrPrint("Unable to set cancelate state: %s\n", strerror(ret)); \ + } \ +} while (0) + +#else +#define CRITICAL_SECTION_BEGIN(handle) +#define CRITICAL_SECTION_END(handle) +#define CANCEL_SECTION_BEGIN(handle) +#define CANCEL_SECTION_END(handle) +#endif + diff --git a/include/heap-monitor.h b/include/heap-monitor.h index e8a460b..2de6adf 100644 --- a/include/heap-monitor.h +++ b/include/heap-monitor.h @@ -15,12 +15,10 @@ */ extern int heap_monitor_initialized(void); -extern void heap_monitor_init(void); -extern void heap_monitor_start(void); -extern void heap_monitor_stop(void); extern size_t heap_monitor_target_usage(const char *name); extern int heap_monitor_add_target(const char *name); extern int heap_monitor_del_target(const char *name); -extern void heap_monitor_set_stack_boundary(unsigned long stack_boundary); +extern void heap_monitor_fini(void); +extern void heap_monitor_init(void); /* End of a file */ diff --git a/include/heap-monitor_internal.h b/include/heap-monitor_internal.h new file mode 100644 index 0000000..699eaf1 --- /dev/null +++ b/include/heap-monitor_internal.h @@ -0,0 +1,22 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +extern void *malloc_wrapper(size_t size); +extern void *realloc_wrapper(void *ptr, size_t size); +extern void free_wrapper(void *ptr); +extern void *memalign_wrapper(size_t align, size_t size); + +/* End of all */ diff --git a/packaging/libheap-monitor.spec b/packaging/libheap-monitor.spec index d6227cf..8c1e010 100644 --- a/packaging/libheap-monitor.spec +++ b/packaging/libheap-monitor.spec @@ -1,12 +1,11 @@ Name: libheap-monitor Summary: Library for monitoring the heap usage -Version: 0.0.15 +Version: 0.0.16 Release: 1 Group: HomeTF/Livebox License: Flora License Source0: %{name}-%{version}.tar.gz BuildRequires: cmake, gettext-tools, coreutils -BuildRequires: pkgconfig(dlog) %description Monitoring the heap usage to manage them safely. @@ -28,7 +27,7 @@ export CFLAGS="${CFLAGS} -DTIZEN_ENGINEER_MODE" export CXXFLAGS="${CXXFLAGS} -DTIZEN_ENGINEER_MODE" export FFLAGS="${FFLAGS} -DTIZEN_ENGINEER_MODE" %endif -%cmake . +cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} CFLAGS+="${CFLAGS} -fvisibility=hidden -Wall -Werror -Winline -fno-builtin-malloc" make %{?jobs:-j%jobs} %install diff --git a/src/allocator.c b/src/allocator.c new file mode 100644 index 0000000..347aa0c --- /dev/null +++ b/src/allocator.c @@ -0,0 +1,283 @@ +/* + * Copyright 2013 Samsung Electronics Co., Ltd + * + * Licensed under the Flora License, Version 1.1 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include "debug.h" + +int errno; + +struct chunk { + struct chunk *prev; + struct chunk *next; + + unsigned int size; + char data[]; +}; + +struct allocator { + pthread_mutex_t mutex; + unsigned int size; + struct chunk *used; + struct chunk *freed; + char data[]; +}; + +struct allocator *allocator_init(unsigned int base, unsigned int size) +{ + struct allocator *handle = (struct allocator *)base; + struct chunk *chunk; + + handle->size = size - sizeof(*handle); + + chunk = (struct chunk *)handle->data; + + pthread_mutex_init(&handle->mutex, NULL); + + handle->freed = chunk; + handle->used = NULL; + + chunk->size = handle->size - sizeof(*chunk); + chunk->next = chunk; + chunk->prev = chunk; + + return handle; +} + +int allocator_fini(struct allocator *handle) +{ + pthread_mutex_destroy(&handle->mutex); + return 0; +} + +unsigned int allocator_allocated_size(void *ptr) +{ + struct chunk *tmp = (struct chunk *)ptr; + return tmp->size; +} + +void *allocator_alloc(struct allocator *handle, int size) +{ + struct chunk *tmp; + struct chunk *chunk; + void *ret = NULL; + + if (!handle) { + return NULL; + } + + CRITICAL_SECTION_BEGIN(&handle->mutex); + tmp = handle->freed; + if (!tmp) { + goto out; + } + + do { + if (size > tmp->size) { + continue; + } else if (tmp->size <= size + sizeof(*tmp)) { + /*! + * \note + * If the remained free size is more smaller than "chunk structure" size, + * Allocating a new chunk does not need make a new free node. + * It cannot be used anymore, in this case, + * Don't divide it into more smaller chunks. + * + * We call this "INTERNAL FRAGMENTATION". + */ + chunk = tmp; + + chunk->prev->next = chunk->next; + chunk->next->prev = chunk->prev; + + if (chunk == handle->freed) { + handle->freed = chunk->next; + + if (handle->freed == chunk) { + handle->freed = NULL; + } + } + } else { + tmp->size -= size + sizeof(*chunk); + + chunk = (struct chunk *)(tmp->data + tmp->size); + chunk->size = size; + } + + if (!handle->used) { + handle->used = chunk; + ret = (void *)chunk->data; + + chunk->next = chunk; + chunk->prev = chunk; + break; + } + + /*! + * \note + * Now, we can re-use the "tmp". + * Because we will not take outer loop anymore. + * So re-use the "tmp" for insertion sorting. + */ + tmp = handle->used; + if (tmp > chunk) { + /*! + * \note + * Reassign the "chunk" to handle->used. + */ + handle->used = chunk; + } else { + do { + if (tmp > chunk) { + break; + } + } while ((tmp = tmp->next) && tmp != handle->used); + } + + tmp->prev->next = chunk; + chunk->prev = tmp->prev; + chunk->next = tmp; + tmp->prev = chunk; + ret = (void *)chunk->data; + break; + } while ((tmp = tmp->next) && tmp != handle->freed); + +out: + CRITICAL_SECTION_END(&handle->mutex); + return ret; +} + +void allocator_free(struct allocator *handle, void *ptr) +{ + struct chunk *tmp; + struct chunk *chunk; + + if (!ptr) { + return; + } + + chunk = (struct chunk *)(((char *)ptr) - (unsigned int)(&((struct chunk *)0)->data)); + + CRITICAL_SECTION_BEGIN(&handle->mutex); + /*! + * \note + * Disconnecting from the used list. + */ + chunk->prev->next = chunk->next; + chunk->next->prev = chunk->prev; + + if (chunk == handle->used) { + handle->used = chunk->next; + if (handle->used == chunk) { + handle->used = NULL; + } + } + + /*! + * \note + * Insert a chunk to the free'd list. + */ + if (!handle->freed) { + handle->freed = chunk; + chunk->next = chunk; + chunk->prev = chunk; + goto out; + } + + tmp = handle->freed; + + do { + if (tmp > chunk) { + break; + } + } while ((tmp = tmp->next) && tmp != handle->freed); + + if ((unsigned int)(chunk->data + chunk->size) == (unsigned int)tmp) { + /*! + * \note + * tmp is merged with the chunk. + * and if the tmp is same with handle->freed, + * assign chunk to handle->freed. + */ + chunk->size += tmp->size + sizeof(*tmp); + + chunk->next = tmp->next; + chunk->prev = tmp->prev; + chunk->next->prev = chunk; + chunk->prev->next = chunk; + + if (handle->freed == tmp) { + handle->freed = chunk; + } + + /* Previous node is changed. try to merge with prev node again */ + tmp = chunk->prev; + if ((unsigned int)(tmp->data + tmp->size) == (unsigned int)chunk) { + tmp->size += chunk->size + sizeof(*chunk); + + tmp->next = chunk->next; + chunk->next->prev = tmp; + + if (handle->freed == chunk) { + handle->freed = tmp; + } + } + } else if ((unsigned int)(tmp->data + tmp->size) == (unsigned int)chunk) { + /*! + * \note + * chunk is merged with the tmp. + * and if the chunk is same with handle->freed. + * assign tmp to handle->freed. + */ + tmp->size += chunk->size + sizeof(*chunk); + + /* Size of the node is changed. try to merge with next node again */ + chunk = tmp->next; + if ((unsigned int)(tmp->data + tmp->size) == (unsigned int)chunk) { + tmp->size += chunk->size + sizeof(*chunk); + + tmp->next = chunk->next; + chunk->next->prev = tmp; + } + } else { + chunk->prev = tmp->prev; + tmp->prev->next = chunk; + + chunk->next = tmp; + tmp->prev = chunk; + } + +out: + CRITICAL_SECTION_END(&handle->mutex); +} + +int allocator_in_scope(struct allocator *handle, void *addr) +{ + unsigned int base = (unsigned int)handle; + unsigned int size = handle->size; + int valid; + + valid = (base <= (unsigned int)addr && (unsigned int)addr < (base + size)); + DbgPrint("Validate: %p (%d)\n", addr, valid); + return valid; +} + +/* End of a file */ diff --git a/src/dlist.c b/src/dlist.c index 3ae571b..f947edc 100644 --- a/src/dlist.c +++ b/src/dlist.c @@ -19,6 +19,7 @@ #include #include "dlist.h" +#include "heap-monitor_internal.h" /*! * \brief @@ -44,7 +45,7 @@ struct dlist *dlist_append(struct dlist *list, void *data) { struct dlist *item; - item = malloc(sizeof(*item)); + item = malloc_wrapper(sizeof(*item)); if (!item) { return NULL; } @@ -71,7 +72,7 @@ struct dlist *dlist_prepend(struct dlist *list, void *data) { struct dlist *item; - item = malloc(sizeof(*item)); + item = malloc_wrapper(sizeof(*item)); if (!item) { return NULL; } @@ -123,7 +124,7 @@ struct dlist *dlist_remove(struct dlist *list, struct dlist *l) list->prev = l->prev; } - free(l); + free_wrapper(l); return list; } diff --git a/src/heap-monitor.c b/src/heap-monitor.c index 054a76a..5e91cec 100644 --- a/src/heap-monitor.c +++ b/src/heap-monitor.c @@ -27,101 +27,48 @@ #include #include #include +#include +#include #include -#include - #include "dlist.h" #include "heap-monitor.h" - -#if !defined(SECURE_LOGD) -#define SECURE_LOGD LOGD -#endif - -#if !defined(SECURE_LOGE) -#define SECURE_LOGE LOGE -#endif - -#if !defined(SECURE_LOGW) -#define SECURE_LOGW LOGW -#endif - -#if !defined(FLOG) -#define DbgPrint(format, arg...) SECURE_LOGD("[%s/%s:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg) -#define ErrPrint(format, arg...) SECURE_LOGE("[%s/%s:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg) -#else -extern FILE *__file_log_fp; -#define DbgPrint(format, arg...) do { fprintf(__file_log_fp, "[LOG] [%s/%s:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0) - -#define ErrPrint(format, arg...) do { fprintf(__file_log_fp, "[ERR] [%s/%s:%d] " format, basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0) -#endif -/* End of a file */ +#include "allocator.h" +#include "debug.h" +#include "heap-monitor_internal.h" #define container_of(ptr, type, member) \ ({ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) + (type *)( (char *)__mptr - offsetof(type, member) );}) #define API __attribute__((visibility("default"))) #define DUMP_DEPTH 80000 -#if defined(_ESTIMATE_PERFORMANCE) -#define ESTIMATE_START() \ -do { \ - struct timeval stv; \ - struct timeval etv; \ - struct timeval rtv; \ - static struct timeval ttv; \ - static int initialized = 0; \ - if (!initialized) { \ - timerclear(&ttv); \ - initialized = 1; \ - } \ - gettimeofday(&stv, NULL) - -#define ESTIMATE_END() \ - gettimeofday(&etv, NULL); \ - gettimeofday(&etv, NULL); \ - timersub(&etv, &stv, &rtv); \ - timeradd(&rtv, &ttv, &ttv); \ - DbgPrint("Elapsed time: %lu.%lf\n", rtv.tv_sec, (double)rtv.tv_usec / 1000000.0f); \ - DbgPrint("Total: %lu.%lf\n", ttv.tv_sec, (double)ttv.tv_usec / 1000000.0f); \ -} while (0) -#else -#define ESTIMATE_START() // pthread_mutex_lock(&s_info.mutex) -#define ESTIMATE_END() // pthread_mutex_unlock(&s_info.mutex) -#endif - -#if defined(_THREAD_SAFETY) -#define LOCK() pthread_mutex_lock(&s_info.mutex) -#define UNLOCK() pthread_mutex_unlock(&s_info.mutex) -#else -#define LOCK() -#define UNLOCK() -#endif +typedef void *(*malloc_t)(size_t size); +typedef void *(*realloc_t)(void *ptr, size_t size); +typedef void (*free_t)(void *ptr); +typedef void *(*memalign_t)(size_t align, size_t size); +typedef void *(*calloc_t)(size_t nmemb, size_t size); -typedef void *(*malloc_t)(size_t size, const void *caller); -typedef void *(*realloc_t)(void *ptr, size_t size, const void *caller); -typedef void (*free_t)(void *ptr, const void *caller); -typedef void *(*memalign_t)(size_t align, size_t size, const void *caller); +extern char etext, edata, end; +FILE *__file_log_fp; int errno; +enum state { + VALID = 0xbeefbeef, + INVALID = 0xdeaddead, + STACK = 0x00beef00, +}; + struct chunk { struct target *info; int size; int pad; - enum { - VALID = 0xbeefbeef, - INVALID = 0xdeaddead - } state; + int state; char data[]; }; -#define SET_PAD(ptr, pad) (*(((int *)ptr) - 2) = (pad)) -#define SET_STATE(ptr, state) (*(((int *)ptr) - 1) = (state)) -#define PAD(ptr) (*(((int *)ptr) - 2)) -#define STATE(ptr) (*(((int *)ptr) - 1)) - struct target { char *name; int usage; @@ -137,15 +84,19 @@ static struct { struct dlist *target_list; int hook; int dump_depth; - unsigned long stack_boundary; - unsigned long stack_base; size_t stack_size; - pthread_mutex_t mutex; + unsigned long stack_base; int debugger; int target_cnt; pthread_t main_tid; int initialized; - int m_check; + int hooked; + struct allocator *alloc_handle; + char stack_memory[1048576]; + unsigned int bloom_filter; +#if defined(_THREAD_SAFETY) + pthread_mutex_t mutex; +#endif } s_info = { ._malloc = NULL, ._realloc = NULL, @@ -154,43 +105,55 @@ static struct { .target_list = NULL, .hook = 0, .dump_depth = DUMP_DEPTH, - .stack_boundary = 0, - .mutex = PTHREAD_MUTEX_INITIALIZER, + .stack_size = 0, .debugger = 0, .target_cnt = 0, .initialized = 0, - .m_check = 0, + .hooked = 0, + .alloc_handle = NULL, + .stack_memory = { 0, }, + .bloom_filter = 0, +#if defined(_THREAD_SAFETY) + .mutex = PTHREAD_MUTEX_INITIALIZER, +#endif }; -static void unhook(void); -static void hook(void); - static inline struct target *find_target_by_name(const char *name) { struct dlist *l; - struct target *target; + struct target *target = NULL; dlist_foreach(s_info.target_list, l, target) { if (!strcmp(target->name, name)) { - return target; + break; } + target = NULL; } - - return NULL; + return target; } static inline struct target *find_target_by_addr(unsigned long addr) { struct dlist *l; - struct target *target; + struct target *target = NULL; + CRITICAL_SECTION_BEGIN(&s_info.mutex); dlist_foreach(s_info.target_list, l, target) { if (target->begin <= addr && addr < target->end) { - return target; + break; } + target = NULL; } + CRITICAL_SECTION_END(&s_info.mutex); - return NULL; + return target; +} + +static inline int MPROBE(void *ptr) +{ + enum mcheck_status status; + status = mprobe(ptr); + return status == MCHECK_DISABLED || status == MCHECK_OK; } /*! @@ -215,7 +178,7 @@ static struct target *find_target_info(void) stack = (unsigned long *)&ret; - if (s_info.stack_boundary) { + if (s_info.stack_size) { pthread_t tid; unsigned long base; unsigned long boundary; @@ -234,15 +197,14 @@ static struct target *find_target_info(void) } } else { base = s_info.stack_base; - boundary = s_info.stack_boundary; + boundary = s_info.stack_base + s_info.stack_size; } i = 0; while ((unsigned long)stack >= base && (unsigned long)stack < boundary) { i++; if (s_info.debugger) { - DbgPrint("FP: %p, boundary: %p, INDEX: %d, ret: 0x%X\n", - stack, boundary, i, *stack); + DbgPrint("FP: %p, boundary: %p, INDEX: %d, ret: 0x%X\n", stack, (void *)boundary, i, (unsigned int)*stack); } target = find_target_by_addr(*stack++); @@ -266,7 +228,6 @@ static struct target *find_target_info(void) if (s_info.debugger) { DbgPrint("[%d] Target[%s]: %d\n", i, target->name, target->usage); } - return target; } } @@ -274,85 +235,97 @@ static struct target *find_target_info(void) return NULL; } -static void mcheck_cb(enum mcheck_status status) +void *malloc_wrapper(size_t size) { - struct target *target; - switch (status) { - case MCHECK_DISABLED: - ErrPrint("mcheck is disabled\n"); - break; - case MCHECK_OK: - ErrPrint("Consitency is ok\n"); - break; - case MCHECK_HEAD: - target = find_target_info(); - if (target) { - ErrPrint("[HEAD] Inconsistency: %s\n", target->name); - } - break; - case MCHECK_TAIL: - target = find_target_info(); - if (target) { - ErrPrint("[HEAD] Inconsistency: %s\n", target->name); - } - break; - case MCHECK_FREE: - target = find_target_info(); - if (target) { - ErrPrint("[FREE] Inconsistency: %s\n", target->name); - } - break; - default: - break; - } + return s_info._malloc ? s_info._malloc(size) : NULL; } -static inline int MPROBE(void *ptr) +void *realloc_wrapper(void *ptr, size_t size) { - enum mcheck_status status; + return s_info._realloc ? s_info._realloc(ptr, size) : NULL; +} - status = mprobe(ptr); - if (status == MCHECK_DISABLED) { - return 1; +void free_wrapper(void *ptr) +{ + if (!s_info._free) { + s_info._free = dlsym(RTLD_NEXT, "free"); + if (!s_info._free) { + ErrPrint("Failed to find \"free\"\n"); + return; + } } - if (status != MCHECK_OK) { - mcheck_cb(status); - return 0; - } + s_info._free(ptr); +} - return 1; +void *memalign_wrapper(size_t align, size_t size) +{ + return s_info._memalign ? s_info._memalign(align, size) : NULL; } -static void *heap_monitor_malloc(size_t size, const void *caller) +API void *malloc(size_t size) { struct chunk *chunk; void *ptr = NULL; - LOCK(); - unhook(); ESTIMATE_START(); - chunk = malloc(size + sizeof(*chunk)); - if (chunk) { - chunk->info = find_target_info(); - chunk->size = size; + if (s_info.initialized) { + chunk = malloc_wrapper(size + sizeof(*chunk)); + if (!chunk) { + goto out; + } - ptr = chunk->data; - SET_PAD(ptr, 0); - SET_STATE(ptr, VALID); + chunk->state = VALID; + } else { + CRITICAL_SECTION_BEGIN(&s_info.mutex); + if (!s_info.alloc_handle) { + s_info.alloc_handle = allocator_init((unsigned int)s_info.stack_memory, sizeof(s_info.stack_memory)); + } + CRITICAL_SECTION_END(&s_info.mutex); - if (chunk->info) { - chunk->info->usage += size; + chunk = allocator_alloc(s_info.alloc_handle, size + sizeof(*chunk)); + if (!chunk) { + DbgPrint("Failed stack alloc\n"); + goto out; } + + chunk->state = STACK; + } + + CRITICAL_SECTION_BEGIN(&s_info.mutex); + chunk->info = find_target_info(); + if (chunk->info) { + chunk->info->usage += size; } + CRITICAL_SECTION_END(&s_info.mutex); + chunk->size = size; + chunk->pad = 0; + ptr = chunk->data; + +out: + CRITICAL_SECTION_BEGIN(&s_info.mutex); + s_info.bloom_filter |= (unsigned int)ptr; + CRITICAL_SECTION_END(&s_info.mutex); ESTIMATE_END(); - hook(); - UNLOCK(); return ptr; } -static void heap_monitor_free(void *ptr, const void *caller) +API void *calloc(size_t nmemb, size_t size) +{ + void *ptr; + + size *= nmemb; + + ptr = malloc(size); + if (ptr) { + memset(ptr, 0, size); + } + + return ptr; +} + +API void free(void *ptr) { struct chunk *chunk; @@ -360,169 +333,154 @@ static void heap_monitor_free(void *ptr, const void *caller) return; } - LOCK(); - unhook(); ESTIMATE_START(); - - if (STATE(ptr) != VALID) { - DbgPrint("Unrecognizable chunk, do default operation\n"); - chunk = ptr; - goto out; - } - - chunk = container_of((void *)(((char *)ptr) - PAD(ptr)), struct chunk, data); - if (chunk->info) { - chunk->info->usage -= chunk->size; + if (!s_info.initialized) { + if (!s_info._free) { + /*! + * \note + * If the "free" is called before initiate the heap-monitor, + * Bind the "free" from here + */ + s_info._free = dlsym(RTLD_NEXT, "free"); + if (!s_info._free) { + exit(-EINVAL); + } + } } - if (MPROBE(chunk)) { - SET_STATE(ptr, INVALID); + if (!s_info.alloc_handle) { + /*! + * \note + * If the allocator is not initiated, + * We don't need to validate the address. + * Just "free" it. + */ + DbgPrint("Allocator is not initiated\n"); + free_wrapper(ptr); + } else if ((s_info.bloom_filter & (unsigned int)ptr) != (unsigned int)ptr) { + DbgPrint("Bloom filtered\n"); + free_wrapper(ptr); } else { - chunk = ptr; - if (MPROBE(chunk)) { - DbgPrint("Successfully recovered\n"); + chunk = container_of(ptr, struct chunk, data); + if(chunk->state == VALID) { + if (MPROBE(chunk)) { + if (chunk->info) { + chunk->info->usage -= chunk->size; + } + + chunk->state = INVALID; + ptr = (char *)chunk - chunk->pad; + } else { + DbgPrint("native free\n"); + ptr = chunk; + } + + free_wrapper(ptr); + } else if (chunk->state == STACK || allocator_in_scope(s_info.alloc_handle, ptr)) { + chunk->state = INVALID; + ptr = (char *)chunk - chunk->pad; + allocator_free(s_info.alloc_handle, ptr); + } else if (chunk->state == INVALID) { + struct target *target; + + CRITICAL_SECTION_BEGIN(&s_info.mutex); + target = find_target_info(); + ErrPrint("====== %p, %s\n", ptr, target->name); + CRITICAL_SECTION_END(&s_info.mutex); } else { - ErrPrint("Failed to recover\n"); - chunk = NULL; /* Do nothing */ - goto out; + DbgPrint("native free\n"); + free_wrapper(ptr); } } -out: - free(chunk); ESTIMATE_END(); - hook(); - UNLOCK(); return; } -static void *heap_monitor_realloc(void *__ptr, size_t size, const void *caller) +API void *realloc(void *__ptr, size_t size) { void *ptr = NULL; struct chunk *chunk; - LOCK(); - unhook(); + if (!size) { + free(__ptr); + } else if (!__ptr) { + return malloc(size); + } + ESTIMATE_START(); - if (!__ptr) { - if (!size) { + /* Re-allocation */ + if ((s_info.bloom_filter & (unsigned int)__ptr) != (unsigned int)__ptr) { + /* Move this to our boundary */ + + ptr = malloc(size); + if (!ptr) { goto out; } - /* Allocation */ - chunk = realloc(__ptr, size + sizeof(*chunk)); - if (chunk) { - chunk->info = find_target_info(); - chunk->size = size; - ptr = chunk->data; + memcpy(ptr, __ptr, size); - SET_PAD(ptr, 0); - SET_STATE(ptr, VALID); + free_wrapper(__ptr); + goto out; + } - if (chunk->info) { - chunk->info->usage += size; - } + if (!s_info.initialized) { + CRITICAL_SECTION_BEGIN(&s_info.mutex); + /*! + * We can do this. because allocator_free function has "void" return type. + * This is a small trick. ;) just for fun. Don't be mad with this trick. + */ + if (!s_info.alloc_handle) { + s_info.alloc_handle = allocator_init((unsigned int)s_info.stack_memory, sizeof(s_info.stack_memory)); } - } else if (size == 0) { - /* Free */ - if (STATE(__ptr) != VALID) { - DbgPrint("Unrecognizable chunk, do default operation\n"); - ptr = realloc(__ptr, size); - } else { - chunk = container_of((void *)(((char *)__ptr) - PAD(__ptr)), struct chunk, data); - if (chunk->info) { - chunk->info->usage -= chunk->size; - } + CRITICAL_SECTION_END(&s_info.mutex); + } - if (MPROBE(chunk)) { - SET_STATE(__ptr, INVALID); - } else { - chunk = __ptr; - if (MPROBE(chunk)) { - DbgPrint("Successfully recovered\n"); - } else { - ErrPrint("Failed to recover\n"); - ptr = NULL; - goto out; - } - } + chunk = container_of(__ptr, struct chunk, data); + if (chunk->state == VALID) { + if (MPROBE((unsigned char *)chunk - chunk->pad)) { + ptr = (unsigned char *)chunk - chunk->pad; - ptr = realloc(chunk, size); - } - } else { - if (STATE(__ptr) != VALID) { - DbgPrint("Unrecognizable chunk, do default operation\n"); - ptr = realloc(__ptr, size); - } else { - int pad = PAD(__ptr); + ptr = realloc_wrapper(ptr, size + sizeof(*chunk)); + if (ptr) { + chunk = ptr; - chunk = container_of((void *)(((char *)__ptr) - pad), struct chunk, data); - if (MPROBE(chunk)) { - ptr = realloc(chunk, size + sizeof(*chunk) + pad); - if (ptr) { - struct chunk *new_chunk = ptr; - - if (new_chunk->info) { - new_chunk->info->usage -= new_chunk->size; - new_chunk->info->usage += size; - } - new_chunk->size = size; - ptr = new_chunk->data + pad; - - SET_STATE(ptr, VALID); - SET_PAD(ptr, pad); - /* Consider this, do we need to keep the alignment for realloc? */ - } - } else { - chunk = __ptr; - if (!MPROBE(chunk)) { - ErrPrint("Failed to recover\n"); - ptr = NULL; - goto out; + if (chunk->info) { + /* Update allocation info */ + chunk->info->usage -= chunk->size; + chunk->info->usage += size; } + chunk->size = size; + chunk->pad = 0; + chunk->state = VALID; - ptr = realloc(chunk, size + sizeof(*chunk)); - if (ptr) { - void *tmp; - struct chunk *new_chunk; - - tmp = malloc(size); - if (!tmp) { - ErrPrint("Heap: %s\n", strerror(errno)); - ptr = NULL; - goto out; - } - - memcpy(tmp, ptr, size); - memcpy(((char *)ptr) + sizeof(*chunk), tmp, size); - free(tmp); - - new_chunk = ptr; - new_chunk->info = find_target_info(); - if (new_chunk->info) { - new_chunk->info->usage += size; - } - new_chunk->size = size; - ptr = new_chunk->data; - SET_STATE(ptr, VALID); - SET_PAD(ptr, 0); - - DbgPrint("Successfully recovered\n"); - } else { - DbgPrint("Failed to recover\n"); - } + ptr = chunk->data; } } + } else if (chunk->state == STACK || allocator_in_scope(s_info.alloc_handle, __ptr)) { + /*! + * Try allocate heap first. + * At last, we will replace all stack based chunk with heap based chunk via "realloc". + */ + ptr = malloc(size); + if (!ptr) { + goto out; + } + + memcpy(ptr, __ptr, size); + allocator_free(s_info.alloc_handle, (char *)chunk - chunk->pad); } + CRITICAL_SECTION_BEGIN(&s_info.mutex); + s_info.bloom_filter |= (unsigned int)ptr; + CRITICAL_SECTION_END(&s_info.mutex); + out: ESTIMATE_END(); - hook(); - UNLOCK(); return ptr; } -static void *heap_monitor_memalign(size_t align, size_t __size, const void *caller) +API void *memalign(size_t align, size_t __size) { void *ptr = NULL; struct chunk *chunk; @@ -533,66 +491,77 @@ static void *heap_monitor_memalign(size_t align, size_t __size, const void *call return NULL; } - LOCK(); - unhook(); - ESTIMATE_START(); - pad = align - (sizeof(*chunk) % align); size = sizeof(*chunk) + pad; - chunk = memalign(align, size + __size); - if (!chunk) { - goto out; + + ESTIMATE_START(); + + if (!s_info.initialized) { + unsigned int aligned_offset; + + CRITICAL_SECTION_BEGIN(&s_info.mutex); + if (!s_info.alloc_handle) { + s_info.alloc_handle = allocator_init((unsigned int)s_info.stack_memory, sizeof(s_info.stack_memory)); + } + CRITICAL_SECTION_END(&s_info.mutex); + + /* Make more space for alignment the address */ + size += align; + ptr = allocator_alloc(s_info.alloc_handle, size + __size); + + aligned_offset = align - (((unsigned int)ptr) % align); + chunk = (struct chunk *)((char *)ptr + aligned_offset + pad); + + chunk->pad = pad + aligned_offset; + chunk->state = STACK; + + DbgPrint("Aligned stack: %p size: %d, pad: %d, offset: %d\n", ptr, size + __size, pad, aligned_offset); + } else { + ptr = memalign_wrapper(align, size + __size); + if (!ptr) { + goto out; + } + + chunk = (struct chunk *)((char *)ptr + pad); + + chunk->pad = pad; + chunk->state = VALID; } + CRITICAL_SECTION_BEGIN(&s_info.mutex); chunk->info = find_target_info(); if (chunk->info) { chunk->info->usage += __size; } + CRITICAL_SECTION_END(&s_info.mutex); chunk->size = __size; - - ptr = chunk->data + pad; - SET_PAD(ptr, pad); - SET_STATE(ptr, VALID); + ptr = chunk->data; out: + CRITICAL_SECTION_BEGIN(&s_info.mutex); + s_info.bloom_filter |= (unsigned int)ptr; + CRITICAL_SECTION_END(&s_info.mutex); ESTIMATE_END(); - hook(); - UNLOCK(); return ptr; } -static void hook(void) +API int posix_memalign(void **memptr, size_t alignment, size_t size) { - s_info.hook++; - if (s_info.hook == 1) { - __malloc_hook = heap_monitor_malloc; - __realloc_hook = heap_monitor_realloc; - __memalign_hook = heap_monitor_memalign; - __free_hook = heap_monitor_free; + if (!memptr) { + return -EINVAL; } + + *memptr = memalign(alignment, size); + + return *memptr ? 0 : -ENOMEM; } -static void unhook(void) +API void *vmalloc(size_t size) { - s_info.hook--; - if (s_info.hook == 0) { - __malloc_hook = s_info._malloc; - __realloc_hook = s_info._realloc; - __memalign_hook = s_info._memalign; - __free_hook = s_info._free; - } + return memalign(sysconf(_SC_PAGESIZE), size); } -/* From GNU libc 2.14 this macro is defined, to declare - hook variables as volatile. Define it as empty for - older glibc versions */ -#ifndef __MALLOC_HOOK_VOLATILE - #define __MALLOC_HOOK_VOLATILE -#endif - -void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook)(void) = heap_monitor_init; - static int iterator_cb(struct dl_phdr_info *info, size_t size, void *data) { struct target *target; @@ -602,6 +571,7 @@ static int iterator_cb(struct dl_phdr_info *info, size_t size, void *data) target = data; if (strcmp(info->dlpi_name, target->name)) { + DbgPrint("\n"); return 0; } @@ -611,8 +581,7 @@ static int iterator_cb(struct dl_phdr_info *info, size_t size, void *data) target->begin = info->dlpi_addr + (unsigned long)phdr->p_vaddr; target->end = target->begin + phdr->p_memsz; if (s_info.debugger) { - DbgPrint("Target: %s - [%s] 0x%lX - 0x%lX\n", - target->name, info->dlpi_name, target->begin, target->end); + DbgPrint("Target: %s - [%s] 0x%lX - 0x%lX\n", target->name, info->dlpi_name, target->begin, target->end); } break; } @@ -625,80 +594,88 @@ API int heap_monitor_del_target(const char *name) { struct dlist *l; struct dlist *n; - struct target *target; - int ret; + struct target *target = NULL; - LOCK(); - unhook(); - ret = -ENOENT; + if (!s_info.initialized) { + return -EINVAL; + } + + CRITICAL_SECTION_BEGIN(&s_info.mutex); dlist_foreach_safe(s_info.target_list, l, n, target) { if (!strcmp(target->name, name)) { s_info.target_list = dlist_remove(s_info.target_list, l); - free(target->name); - free(target); s_info.target_cnt--; - ret = 0; break; } + target = NULL; + } + CRITICAL_SECTION_END(&s_info.mutex); + + if (target) { + DbgPrint("%s <> %s\n", target->name, name); + free_wrapper(target->name); + free_wrapper(target); } - hook(); - UNLOCK(); - return ret; + return target ? 0 : -ENOENT; } API int heap_monitor_add_target(const char *name) { struct target *target; - int ret; - LOCK(); - unhook(); + if (!s_info.initialized) { + return -EINVAL; + } - ret = 0; + CRITICAL_SECTION_BEGIN(&s_info.mutex); target = find_target_by_name(name); if (target) { - goto out; + ErrPrint("Target[%s] is already exists\n", name); + CRITICAL_SECTION_END(&s_info.mutex); + return 0; } + CRITICAL_SECTION_END(&s_info.mutex); - target = malloc(sizeof(*target)); + target = malloc_wrapper(sizeof(*target)); if (!target) { DbgPrint("Heap: %s\n", strerror(errno)); - ret = -ENOMEM; - goto out; + return -ENOMEM; } - target->name = strdup(name); + target->name = malloc_wrapper(strlen(name)); if (!target->name) { DbgPrint("Heap: %s\n", strerror(errno)); - free(target); - ret = -ENOMEM; - goto out; + free_wrapper(target); + return -ENOMEM; } + strcpy(target->name, name); target->usage = 0; target->begin = 0; target->end = 0; dl_iterate_phdr(iterator_cb, target); + CRITICAL_SECTION_BEGIN(&s_info.mutex); s_info.target_cnt++; s_info.target_list = dlist_append(s_info.target_list, target); -out: - hook(); - UNLOCK(); - return ret; + CRITICAL_SECTION_END(&s_info.mutex); + + DbgPrint("Target[%s] is added\n", name); + return 0; } API size_t heap_monitor_target_usage(const char *name) { struct target *target; - LOCK(); - unhook(); + size_t usage = 0; + + CRITICAL_SECTION_BEGIN(&s_info.mutex); target = find_target_by_name(name); - hook(); - UNLOCK(); - return target ? target->usage : 0; + usage = target ? target->usage: 0; + CRITICAL_SECTION_END(&s_info.mutex); + return usage; } API int heap_monitor_initialized(void) @@ -706,33 +683,58 @@ API int heap_monitor_initialized(void) return s_info.initialized; } -API void heap_monitor_set_stack_boundary(unsigned long stack_boundary) -{ - s_info.stack_boundary = stack_boundary; -} - API void heap_monitor_init(void) { const char *var; pthread_attr_t attr; + int ret; if (s_info.initialized) { return; } - -#if defined(_ENABLE_MCHECK) - s_info.m_check = mcheck(mcheck_cb); - if (s_info.m_check < 0) { - ErrPrint("Failed to install mcheck[%d]\n", s_info.m_check); - } else { - DbgPrint("mcheck installed: %d\n", s_info.m_check); + + __file_log_fp = fopen("/tmp/heap_log.tmp", "w+"); + + if (!s_info.alloc_handle) { + s_info.alloc_handle = allocator_init((unsigned int)s_info.stack_memory, sizeof(s_info.stack_memory)); + if (!s_info.alloc_handle) { + exit(-EFAULT); + } } -#endif - s_info._malloc = __malloc_hook; - s_info._realloc = __realloc_hook; - s_info._free = __free_hook; - s_info._memalign = __memalign_hook; + if (!s_info._malloc) { + s_info._malloc = dlsym(RTLD_NEXT, "malloc"); + if (!s_info._malloc) { + ErrPrint("malloc: %s\n", dlerror()); + exit(-EINVAL); + } + } + DbgPrint("malloc: %p & %p\n", s_info._malloc, malloc); + + if (!s_info._free) { + s_info._free = dlsym(RTLD_NEXT, "free"); + if (!s_info._free) { + ErrPrint("free: %s\n", dlerror()); + exit(-EINVAL); + } + } + DbgPrint("free: %p & %p\n", s_info._free, free); + + s_info._realloc = dlsym(RTLD_NEXT, "realloc"); + if (!s_info._realloc) { + ErrPrint("realloc: %s\n", dlerror()); + exit(-EINVAL); + } + DbgPrint("realloc: %p & %p\n", s_info._realloc, realloc); + + s_info._memalign = dlsym(RTLD_NEXT, "memalign"); + if (!s_info._memalign) { + ErrPrint("memalign: %s\n", dlerror()); + exit(-EINVAL); + } + DbgPrint("memalign: %p & %p\n", s_info._memalign, memalign); + + s_info.initialized = 1; var = getenv("HEAP_MONITOR_DUMP_DEPTH"); if (var) { @@ -745,39 +747,53 @@ API void heap_monitor_init(void) } s_info.main_tid = pthread_self(); - s_info.initialized = 1; - if (pthread_getattr_np(s_info.main_tid, &attr)) { - return; + ret = pthread_getattr_np(s_info.main_tid, &attr); + if (ret != 0) { + ErrPrint("destroy: %s\n", strerror(ret)); + exit(-EINVAL); } - if (pthread_attr_getstack(&attr, (void *)&s_info.stack_base, &s_info.stack_size)) { + ret = pthread_attr_getstack(&attr, (void *)&s_info.stack_base, &s_info.stack_size); + if (ret) { s_info.stack_base = 0; s_info.stack_size = 0; - } else { - s_info.stack_boundary = s_info.stack_base + s_info.stack_size; } - pthread_attr_destroy(&attr); - DbgPrint("Initialized\n"); + ret = pthread_attr_destroy(&attr); + if (ret != 0) { + ErrPrint("destroy: %s\n", strerror(ret)); + } + + heap_monitor_add_target(""); + + DbgPrint("program text (etext): %p, " + "initialized data (edata): %p, " + "uninitialized data (end): %p, " + "break: %p, stack: %lu, stack_size: %u\n", + &etext, &edata, &end, sbrk(0), s_info.stack_base, s_info.stack_size); + + return; } -API void heap_monitor_start(void) +API void heap_monitor_fini(void) { - if (!s_info.initialized) { - return; + DbgPrint("Usage: %d\n", heap_monitor_target_usage("")); + if (__file_log_fp) { + fclose(__file_log_fp); } - - hook(); } -API void heap_monitor_stop(void) +API __attribute__((destructor)) int destructor(void) { - if (!s_info.initialized) { - return; - } + heap_monitor_fini(); + return 0; +} - unhook(); +API __attribute__((constructor)) int constructor(void) +{ + heap_monitor_init(); + return 0; } /* End of a file */ -- 2.7.4