嵌入式系統內存泄漏檢測


很多人喜歡抱怨,嵌入式系統什么調試工具都沒提供。這是事實,嵌入式操作系統,除了vxWorks還算強大外,其它系統能提供的東西真的少的可憐。哥倒是挺喜歡這樣,時不時做點小工具,調節下神經,算是個開心的事。內存泄漏的檢測就是蠻好玩的,原理簡單,應用簡單,且容易看到成果。

內存泄漏,就是忘記釋放之前分配的堆內存,malloc, realloc少做了free操作。內存泄漏工具的基本原理就是捕獲每一次malloc,realloc和free操作,在分配時將分配信息保存在一個列表里,釋放時再從列表里清除。工具在設計的關鍵在於如何設計這個表,做到能夠適應大量分配、釋放操作的非常高效的頻繁更新。當然,這些操作本身還不能調用外部(本C文件外)一個內嵌了mallc,free的操作,比如printf就應該避免,否則會形成遞歸錯誤。

還有一點需要提前說明的,內存是不是泄漏,軟件本身並不能確認的,要靠人工根據實際代碼功能進行分析。工具只能提供一個誰的嫌疑最大,讓分析者能更快的發現並找到問題。

至於高效的存取列表,可以用zlib里的hash和avl。它們的實例是提前配置的,不存在動態分配內存問題。當然,自己搞一個也不錯,列表是高效的就行。

http://files.cnblogs.com/files/hhao020/cshell_prj_with_memleak_util.rar
鏈接包含zlib,memleak等,下載軟件包,解壓后在linux下編譯。

要讓程序附加內存泄漏檢測功能,需要在公共頭文件里重定義malloc,realloc和free操作,如下:

#define malloc(size) tml_malloc(size, __FUNCTION__, __LINE__)
#define realloc(mem, size) tml_malloc(mem, size, __FUNCTION__, __LINE__)
#define free(mem) tml_free(mem)
IMPORT void* tml_malloc(int size, const char* function, int line);
IMPORT void tml_free(void* mem);
IMPORT void* tml_realloc(void* mem, int size, const char* function, int line);

在我提供的代碼中,這個是加入到zType_Def.h中的,因為我的代碼里,只有zType_Def.h是應用必須引用的。當然,如果有不引用的,memory leak檢測是沒辦法的。

這段程序,先把malloc等函數編程宏操作。因為編譯器總是先做預編譯,這個階段是不管看到的東西是函數還是變量,只要字符串跟宏定義相同,就會被替換。等預編譯完成,所有代碼里就只有tml_malloc,而沒malloc調用了;當然,memleak.c除外,它里面還是要調用真實的malloc,free的。

memleak.c實現如下:

/*----------------------------------------------------------
File Name  : xxx.c
Description: 
Author     : hhao020@gmail.com (bug fixing and consulting)
Date       : 2007-05-15
------------------------------------------------------------*/  
#include "zType_Def.h"

#ifdef malloc
#undef malloc
#endif

#ifdef free
#undef free
#endif

#ifdef realloc
#undef realloc
#endif


/** copy below lines to a common header file

#define malloc(size) tml_malloc(size, __FUNCTION__, __LINE__)
#define realloc(mem, size) tml_malloc(mem, size, __FUNCTION__, __LINE__)
#define free(mem)      tml_free(mem)
IMPORT void* tml_malloc(int size, const char* function, int line);
IMPORT void  tml_free(void* mem);
IMPORT void* tml_realloc(void* mem, int size, const char* function, int line);

** copy ends*/

//#include "zTraceApi.h"   //replace zlib with stdlib, to make the tool be lighter
//#include "zSalOS.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "memleak.h"

typedef struct MEMORY_LEAK_TYPE
{
  void* mem;
  int   size;
  const char* function;
  int   line;
  int   tAlloc;
} MemLeak_t;

static MemLeak_t g_memleaks[128] = { {0,}, };



static int tml_insert(void* mem, int size, const char* function, int line)
{
  int i;
  for(i=0; i<TBL_SIZE(g_memleaks); i++)
  {
    if(g_memleaks[i].mem) continue;
    g_memleaks[i].mem = mem;
    g_memleaks[i].size = size;
    g_memleaks[i].function = function;
    g_memleaks[i].line = line;
    g_memleaks[i].tAlloc = time(0); //zTime(0);

    return 1;
  }

  return 0;
}

static int tml_remove(void* mem)
{
  int i;
  for(i=0; i<TBL_SIZE(g_memleaks); i++)
  {
    if(g_memleaks[i].mem != mem) continue;
    g_memleaks[i].mem = 0;

    return 1;
  }
  
  return 0;
}

void* tml_realloc(void* mem, int size, const char* function, int line)
{
  if(mem) //note, a null memory must cause exception and crash
  {
    tml_remove(mem);
  }

  mem = realloc(mem, size);

  if(mem)
  {
    tml_insert(mem, size, function, line);
  }

  return mem;
}

void* tml_malloc(int size, const char* function, int line)
{
  void *mem = malloc(size);
  
  if(mem) tml_insert(mem, size, function, line);
  
  return mem;
}

void  tml_free(void* mem)
{
  if(mem) //note, a null memory must cause exception and crash
  {
    tml_remove(mem);
  }

  free(mem);

  return;
}

int tml_cleanall()
{
  memset(&g_memleaks[0], 0, sizeof(g_memleaks));
  return 0;
}

int tml_show(int ref_seconds) //only those memory allocated before ref_seconds are leak candidates
{
  int now = time(0);
  
  int i;
  for(i=0; i<TBL_SIZE(g_memleaks); i++)
  {
    if(!g_memleaks[i].mem) continue;
    if(now - g_memleaks[i].tAlloc <= ref_seconds) continue;

    printf("mem: %p size: %6d  @%s:%d  tAlloc: -%d\n", g_memleaks[i].mem, g_memleaks[i].size, g_memleaks[i].function, g_memleaks[i].line, now-g_memleaks[i].tAlloc);
  }
  return 1;
}
View Code

看,undef又將剛才的宏全部去掉了,這時候看到的malloc和free,就是真實的系統內存操作函數。這里必須要注意,如果剛剛的宏定義所在文件被引用,則必須放在undef前。否則undef就不生效了。

再接着,就是tml_malloc等函數的實現。我給的例子,用了個簡單的數組,只是拋磚引玉,實際不可以這樣,因為malloc這類操作可能很頻繁(否則也就不會內存泄漏了)。如此低效的插入刪除,系統早累趴下了!


當然,哥寫的程序,幾乎不在運行期調用任何malloc或是free;內存泄漏檢測這玩意,就是給一般項目用的。

哦,差點忘記了,光記下當前函數是不夠的,還應該從當前TCB獲取task信息和棧頂附近的幾個調用,不會的話,翻前一篇系統掛起檢測文章。這樣就能找到准確的誰調用的,在做什么。Linux下做這個有點困難,嵌入式軟件就很容易做到。不過也沒啥,有了一個調用者信息,還有相對應的內存也可以dump出來分析,基本上問題都能解決了。

 

測試示例:

cshell_prj $ bin/target_a.linux.i32.exe 
->p=0

$1/> p=0

 = 0 (0x0) <:0x3d :61 :'=' : size=0>
->p=testMalloc(100)

$2/> p=testMalloc(100)

 = 150846704 (0x8FDBCF0) <:0x3d :61 :'=' : size=0>
->tml_show()

$3/> tml_show()

mem: 0x8fdbcf0 size:    100  @testMalloc:35  tAlloc: -4
 = 1 (0x1) <FUNCALL : size=0>
->testFree(p)

$4/> testFree(p)

 = 115480  (0x1C318) <FUNCALL : size=4>
->tml_show()

$5/> tml_show()

 = 1 (0x1) <FUNCALL : size=0>
->

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM