内存泄露调试笔记一例

发布于 2015-01-14 17:44:35
刚好碰到一内存泄露问题,但不知道是谁泄露了,翻出之前调试用过的代码改了一下,很快找到泄露者了。

先是对mem.c进行改造,给每个node加入更多信息,以方便追溯。
 src/mem.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 53 insertions(+), 2 deletions(-)

diff --git a/src/mem.c b/src/mem.c
index 004de01..5f83700 100644
--- a/src/mem.c
+++ b/src/mem.c
@@ -112,6 +112,11 @@ struct heap_mem
rt_uint16_t used;

rt_size_t next, prev;
+
+ rt_tick_t tick;
+ char thread_name[12];
+ char file_name[128];
+ int line;
};

/** pointer to the heap: for alignment, heap_ptr is now a pointer instead of an array */
@@ -230,6 +235,45 @@ void rt_system_heap_init(void *begin_addr, void *end_addr)
lfree = (struct heap_mem *)heap_ptr;
}

+static void mem_dump(void)
+{
+ const struct heap_mem * mem;
+ int count = 0;
+ rt_tick_t tick;
+
+ rt_enter_critical();
+
+ tick = rt_tick_get();
+
+ mem = (struct heap_mem *)heap_ptr;
+
+ rt_kprintf("mem_dump heap_ptr:0x%08X heap_end:0x%08X
", (int)heap_ptr, (int)heap_end);
+ while(mem < heap_end)
+ {
+ rt_kprintf("mem #%d: 0x%08X
", count, (int)mem);
+ rt_kprintf("magic: %04X %s
", mem->magic, (mem->used)?"used":"free");
+ //rt_kprintf("size %d
", (int)());
+
+ if(mem->used)
+ {
+ rt_kprintf("thread: %s, age %d
", mem->thread_name, tick - mem->tick);
+ rt_kprintf("%s L:%d
", mem->file_name, mem->line);
+ }
+
+ mem = (struct heap_mem *)&heap_ptr[mem->next];
+
+ count++;
+ rt_kprintf("
");
+ }
+ rt_kprintf("mem_dump end
");
+
+ rt_exit_critical();
+}
+#ifdef RT_USING_FINSH
+#include
+FINSH_FUNCTION_EXPORT_ALIAS(mem_dump, mem_dump, webclient multiple download test);
+#endif /* RT_USING_FINSH */
+
/**
* @addtogroup MM
*/
@@ -243,7 +287,7 @@ void rt_system_heap_init(void *begin_addr, void *end_addr)
*
* @return pointer to allocated memory or NULL if no free memory was found.
*/
-void *rt_malloc(rt_size_t size)
+void *rt_malloc_debug(rt_size_t size, const char * file, int line)
{
rt_size_t ptr, ptr2;
struct heap_mem *mem, *mem2;
@@ -363,6 +407,13 @@ void *rt_malloc(rt_size_t size)
RT_OBJECT_HOOK_CALL(rt_malloc_hook,
(((void *)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM)), size));

+ //debug
+ mem->tick = rt_tick_get();
+ strncpy(mem->thread_name, rt_thread_self()->name, RT_NAME_MAX);
+ strncpy(mem->file_name, file, sizeof(mem->file_name) - 1);
+ mem->file_name = '';
+ mem-&gt;line = line;
+
/* return the memory data except mem struct */
return (rt_uint8_t *)mem + SIZEOF_STRUCT_MEM;
}
@@ -372,7 +423,7 @@ void *rt_malloc(rt_size_t size)

return RT_NULL;
}
-RTM_EXPORT(rt_malloc);
+//RTM_EXPORT(rt_malloc);

/**
* This function will change the previously allocated memory block.


然后把rtthread.h中的rt_malloc指向新rt_malloc_debug
 include/rtthread.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/include/rtthread.h b/include/rtthread.h
index edd207b..94830a7 100644
--- a/include/rtthread.h
+++ b/include/rtthread.h
@@ -222,7 +222,8 @@ void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block));
*/
void rt_system_heap_init(void *begin_addr, void *end_addr);

-void *rt_malloc(rt_size_t nbytes);
+void *rt_malloc_debug(rt_size_t size, const char * file, int line);
+#define rt_malloc(size) rt_malloc_debug(size, __FILE__, __LINE__)
void rt_free(void *ptr);
void *rt_realloc(void *ptr, rt_size_t nbytes);
void *rt_calloc(rt_size_t count, rt_size_t size);


然后执行测试程序,让内存多泄露一些,并让时间久一点,然后mem_dump()就能很明显找出泄露者了。

查看更多

关注者
0
被浏览
2.9k
5 个回答
aozima
aozima 2015-01-14
拒绝白嫖,拒绝键盘侠!
dump出来的信息格式如下
mem_dump()
mem_dump heap_ptr:0x10005AF0 heap_end:0x1000FF60

mem #0: 0x10005AF0
magic: 0000 free

mem #1: 0x100064B8
magic: 1EA0 used
thread: init, age 17779
..............
t-thread_git_forkcomponents
etlwip-1.4.1src
etifethernetif.c L:187

mem #2: 0x1000658C
magic: 1EA0 used
thread: init, age 17779
..............
t-thread_git_forkcomponentsfinshshell.c L:640

mem #3: 0x100068DC
magic: 1EA0 used
thread: init, age 17779
..............
t-thread_git_forksrcobject.c L:294


如果发现自己写的源文件名或线程名出现在上面,就可能需要仔细排查了。
不过一般泄露原因并不是申请的地方,具体还需要分析源代码。

实际排查时发现有很多app线程在kservice.c L:495申请的内存没有释放。
然后调试时等,运行到app运行就在kservice.c L:495下断点,发现有3次strdup操作,有2次内存有close函数中有释放,一个没有,泄露点顺利找到。
fighter
fighter 2015-01-21
这功能好啊,产品测试时很实用。 有合并到主分支吗?
aozima
aozima 2015-01-21
拒绝白嫖,拒绝键盘侠!
这功能好啊,产品测试时很实用。 有合并到主分支吗?

怎么可能把这堆调试代码提交上去,自己调试时查查就可以了。
aozima
aozima 2015-11-04
拒绝白嫖,拒绝键盘侠!
今天再用这个代码时,提示内存用光了。
检查发现每个item里面有128字节放文件名实在是太傻了,
把传入的 __FILE__ 地址保存起来就OK了。

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览