一个简单的可移植 模块/系统动态内存 跟踪方法

来源:互联网 发布:电子工程师必备app软件 编辑:程序博客网 时间:2024/05/16 06:27

一个简单的可移植 模块/系统动态内存跟踪方法

 

 

在嵌入式系统或一些内存申请,释放频繁的场合,很难统计当前使用的动态内存,和整个软件模块运行过程中的内存使用的峰值;或者模块卸载时内存有无完全释放,还有就是动态内存的踩踏检查。针对这种需求,本文给出一种简单的解决办法,并用 c code描述出来。

 

 

感性认识

本文命名所建立的跟踪模块为mtrace

 

mtrace可以用来跟踪当前系统/模块的内存使用情况,以及估算系统/模块的占用内存的极值系统/模块卸载时的内存泄露情况。

 

模块内存跟踪只在 dbg模式下有效rom下自动变成mode 0

模块内存跟踪 0,1,2,3四种模式,其中 mode 03没有内存使用统计;mode 12有内存使用统计,具体说明如下。

 

 

Mode总开关FS_TRACE_MEM  控制

 

FS_TRACE_MEM =0(mode 0)其上所有系统/模块不使用内存跟踪

 

FS_TRACE_MEM =1(mode 1)其上所有系统/模块使用内存跟踪,只统计系统/模块占用总量大小,具体在各系统/模块声明的TRACE_MEMSTAT中,如fatfat_memstatntfsntfs_memstathfs+hfsplus_memstatdefault_memstat为其上所有系统/模块内存跟踪统计,如下图

说明:

      int calloc;   当前模块调用calloc申请的字节数

      int malloc;  当前模块调用 malloc申请的字节数

      int realloc;  当前模块调用 realloc申请的字节数

      int total;    当前模块迄今申请内存的总字节数 (以上3项总和)

      int max_once;  模块迄今一次申请最多字节数

      int max_total;  模块迄今使用最多情况下的总字节数,即峰值

 

      int calloc_cnt;  模块调用有效calloc的次数

      int malloc_cnt; 模块调用有效malloc的次数

      int realloc_cnt;  模块调用有效realloc的次数

      int total_cnt;    模块申请内存的总次数 (以上3项总和)

      int max_total_cnt; 模块迄今申请内存的总次数最大值

 

FS_TRACE_MEM =2(mode 2)其上所有系统/模块使用内存跟踪,包括统计占用总量大小,内存分配日志,具体在各系统/模块声明的TRACE_MEMSTAT;而且日志极限数目由每个系统/模块MAX_ALLOC_ENTRYS控制。如fatfat_memstatntfsntfs_memstathfs+hfsplus_memstatdefault_memstat为其上所有系统/模块内存跟踪统计,跟踪日志在memstat结构的 log结构中,如下图

说明:

      int calloc; 

      int malloc; 

      int total;   

int realloc;

      int max_once;

      int max_total; 

 

      int calloc_cnt;

      int malloc_cnt;

      int realloc_cnt;

      int total_cnt;

      int max_total_cnt; 同上

 

             int   flag;  //日志系内部标志

             u16  limit;  //系统/模块最多可容纳日志数 ,MAX_ALLOC_ENTRYS有关

             s16  max_index;        //最后一个占用日志索引

             u16  free_index;        //第一个空闲日志索引

             u16 alloc_count;        //迄今已使用日志数

             u16 max_alloc_count;    //迄今使用最多日志情况下的日志数

             char *logstr[MAX_ALLOC_ENTRYS]; //日志数组

 

如果内存申请成功 ,写入有效日志,第三个逗号后为申请内存处所在文件的文件名字符串;第二个逗号后为申请内存处该文件的所在行数;第一个逗号后为在该处申请的总有效字节数;第一个逗号内为在该处申请的总有效次数,

如果该文件对应 line的申请内存被全部释放 ,该处日志 = 0,如上图131516

 

如果系统/模块退出,所有内存释放 ,如下图,可以用来跟踪系统/模块内存的泄漏情况

 

 

 

从上图可以看看出,系统在任何调试时刻,可以看到模块占用的内存统计和申请情况。大大方便用户对模块使用内存的评估

 

 

如何使用:

举例如下:假设要评估的模块是 hfs 

 

 

1)在评估模块或系统的*.h*.c文件,  加入

#define  TRACE_MEMSTAT  & hfs_memstat      //指定跟踪模块命名

#define MAX_ALLOC_ENTRYS   128 //FS_TRACE_MEM =2有用

#include "mtarce.h"

 

DECLARE_MEMSTAT(hfs_memstat);

 

#define hfs_calloc( size)   mtrace_calloc(size)

#define hfs_malloc( size)  mtrace_malloc(size )

#define hfs_realloc(p, size) mtrace_realloc( p ,size )

#define hfs_free(p)       mtrace_free(p) 

 

确保每个c文件都会编译到

 

2)某一c文件加入

DEFINE_MEMSTAT ((hfs_memstat); //仅需一次

 

3)系统项目编译链接加入 mtrace.c文件

 

4)以后在模块(或系统)凡是使用内存分配例程的地方都替换为 mtrace_XXX hfs_XXX对应例程。

 

通过以上几步,即可应用mtrace的动态内存使用情况跟踪

 

源码说明:

mtrace2个可移植文件组成mtrace.h, mtrace.c

其中mtrace.h为算法配置和声明,mtrace.c为算法实现,详细列举如下

 

 

mtrace.h

 

 

#ifndef MTRACE_H

#define MTRACE_H

 

////////////configurations ////////////////////

 

#include "includes.h"

 

#ifndef __ROM_

  #define  FS_PRINT_ENABLE   1

  #define  FS_TRACE_MEM   2                  //0,1,2,3 可以修改

#else  //dont modify below

  #define  FS_PRINT_ENABLE   0       

  #define  FS_TRACE_MEM   0         

#endif

 

//fit to yourown requirement

#define u16   unsigned  short

#define s16   signed  short

//#define size_t   int

 

#define raw_calloc( n , size)   calloc(n ,size)

#define raw_malloc( size)       malloc(size)

#define raw_realloc(p, size)    realloc( p ,size )

#define raw_free(p)             free( p )

 

#define DECLEAR_KEY(x)          OS_EVENT  *x=NULL

#define INIT_THREAD_MUTEX()     AVSemCreate(1)

#define THREAD_LOCK(x) {INT8U err;   AVSemPend(x,0,&err); }

#define THREAD_UNLOCK(x) AVSemPost(x)  

////////////////////////////////////////////////

 

#ifndef ENOMEM

      #define   ENOMEM 12

#endif

 

#if( FS_TRACE_MEM>2)

 

#define mtrace_calloc(n, size)  raw_calloc(n,size)

#define mtrace_malloc( size)    raw_malloc(size)

#define mtrace_realloc(p, size) raw_realloc( p ,size )

#define mtrace_free(p)          raw_free( p )

 

#define  DEFINE_MEMSTAT(stat)   //

#define  DECLARE_MEMSTAT(stat)  //

 

 

#elif( FS_TRACE_MEM>0)

 

#ifndef MAX_ALLOC_ENTRYS 

      #define MAX_ALLOC_ENTRYS   128

#endif

 

 

#define memstat_log_inited   0x01

#define memstat_log_allused 0x02

 

#define memstat_log_index_invalid -1

 

typedef struct

{

      int calloc;

      int malloc;

      int realloc;

      int total;

      

      int max_once;

      int max_total;

 

      int calloc_cnt;

      int malloc_cnt;

      int realloc_cnt;

      int total_cnt;

      int max_total_cnt;

 

      struct

      {

             int   flag;

             u16  limit;

#if( FS_TRACE_MEM&2)

             s16  max_index;        //max valid index,fast find matched log        

             u16  free_index;        //first free log index,fast for next log if all exist not matched

             u16 alloc_count;        //current total used log entrys

             u16 max_alloc_count;

             char *logstr[MAX_ALLOC_ENTRYS];

//           char **logstr;

#endif

      }log;      

}memstat;

 

#define  DEFINE_MEMSTAT(hinst)   memstat hinst={0,0,0,0,0, 0,0,0,0,0,0,{0,MAX_ALLOC_ENTRYS}}

#define  DECLARE_MEMSTAT(hinst)  extern  memstat hinst

 

void *mtrace_calloc_trace(size_t size,int line, char *file,memstat *stat);

void *mtrace_malloc_trace(size_t size,int line, char *file,memstat *stat);

void *mtrace_realloc_trace(void *old,size_t size,int line, char *file,memstat *stat);

void mtrace_free_trace (void *pmen,memstat *stat);

 

 

DECLARE_MEMSTAT(default_memstat);

 

#ifndef TRACE_MEMSTAT 

 #define TRACE_MEMSTAT &default_memstat

#endif

 

#define mtrace_calloc( size)     mtrace_calloc_trace(size, __LINE__, __FILE__,TRACE_MEMSTAT)

#define mtrace_malloc( size)    mtrace_malloc_trace(size ,__LINE__, __FILE__,TRACE_MEMSTAT)

#define mtrace_realloc(p, size) mtrace_realloc_trace( p ,size ,__LINE__, __FILE__,TRACE_MEMSTAT)

#define mtrace_free(p)             mtrace_free_trace( p , TRACE_MEMSTAT)

 

 

#else

 

#define  DEFINE_MEMSTAT(stat)       //

#define  DECLARE_MEMSTAT(stat)  //

 

void *mtrace_calloc(size_t size);

void *mtrace_malloc(size_t size);

void *mtrace_realloc(void *old,size_t size);

void mtrace_free(void pmen);

 

#endif

 

#if FS_PRINT_ENABLE

 

void mtrace_log_redirect(const char *function, const char *file, int line,

      const char *FORMAT, ...);

#define mtrace_log_error(arg...)mtrace_log_redirect(__FUNCTION__,__FILE__,__LINE__,arg)

 

#else

 

#define mtrace_log_error(arg...)//           

 

#endif 

 

#endif 

 

 

mtarce.c

 

 

#include " mtarce.h"

 

#if( FS_TRACE_MEM>2)

 

#elif( FS_TRACE_MEM>0)

#define MH_MAGIC_MASK  0xFFFFFFFC

#define MH_TYPE_MASK     0x00000003

 

#define MH_MAGIC_ALLOC             0xCCCCCCCC

#define MH_MAGIC_FREE             0xEEEEEEEE

#define MH_TYPE_C 0

#define MH_TYPE_M 1

#define MH_TYPE_R 2

 

typedef struct

{

 int magic;          //0xcccc , low nibble for type

 int size;

 

#if( FS_TRACE_MEM&2)

 int logindex;

#endif

}meminfo;

 

#define meminfo_len  sizeof(meminfo)

 

DEFINE_MEMSTAT(default_memstat);           //total mem trace for all fs

 

#if( FS_TRACE_MEM&2)

 

#define MAX_ALLOC_FILNAMELEN 32

#define MEMLOG_STATEFREE   0

 

#define thisfs_memlog  stat->log

 

static void init_memlog(memstat *stat)

{

 if(thisfs_memlog.flag&memstat_log_inited)

 return ;

 

#if 0

 char *log =(char *) calloc(sizeof(char *),thisfs_memlog.limit);

 if (!log)

 {

 fs_log_error("Failed to calloc %d logs/n", (long)thisfs_memlog.limit);

 return ;

 }

 

 thisfs_memlog.logstr = (char **)log;

#endif

 

 for (int i=0;i<thisfs_memlog.limit;i++)

 {

 thisfs_memlog.logstr[i]=MEMLOG_STATEFREE;

 }

 thisfs_memlog.max_index=-1;

 thisfs_memlog.free_index=0;

 thisfs_memlog.alloc_count=0;

 thisfs_memlog.flag |= memstat_log_inited;

 

 if(default_memstat.log.flag&memstat_log_inited)

 return ;

 

// default_memstat.log.limit = MAX_ALLOC_ENTRYS;

#if 0

 log =(char *) calloc(sizeof(char *),default_memstat.log.limit);

 if (!log)

 {

 fs_log_error("Failed to calloc %d logs/n", (long)default_memstat.log.limit);

 return ;

 }

 thisfs_memlog.logstr = (char **)log;

#endif

 

 for ( i=0;i < default_memstat.log.limit;i++)

 {

 default_memstat.log.logstr[i]=MEMLOG_STATEFREE;

 }

 default_memstat.log.max_index=-1;

 default_memstat.log.free_index=0;

 default_memstat.log.alloc_count=0;

 default_memstat.log.flag |= memstat_log_inited;

 

}

 

 

#define logfmt "%d,%d,%d,%s"

static size_t o_size;

static char o_file[MAX_ALLOC_FILNAMELEN];

static int o_line;

static int o_times;

 

static int register_log(meminfo *p,int line, char *file,memstat *stat)

{

 

 int i,j;

 int retry=0;

 size_t size = p ->size;

 

 init_memlog(stat);

 

 p->logindex = memstat_log_index_invalid;

 

//use the already log entry

 for( i=0 ;i<=thisfs_memlog.max_index;i++)

 {

 

 if(thisfs_memlog.logstr[i]==MEMLOG_STATEFREE)

  continue;

 

 sscanf(thisfs_memlog.logstr[i],  logfmt, &o_times,&o_size,&o_line, o_file);

 if(/*(o_size==size)&&*/(o_line==line)&&(strcmp(o_file,file)==0)&&o_times>0)

 {

  snprintf((char*) thisfs_memlog.logstr[i] ,MAX_ALLOC_FILNAMELEN, logfmt ,o_times+1,size+o_size,line,file);

  p->logindex = i;

  return 0;

 }

 }

 

 

//create new log

 if(thisfs_memlog.flag & memstat_log_allused)

 {

 fs_log_info("all used out now/n");

 return -1;

 }

 

 j=thisfs_memlog.free_index;

 if(thisfs_memlog.logstr[j])

 {

 fs_log_error("already used now/n");

 for(i = 0 ;i<thisfs_memlog.limit;i++)

 {

  if(thisfs_memlog.logstr[i]==MEMLOG_STATEFREE)

   break;

 }

 if(i==thisfs_memlog.limit)

 {

  thisfs_memlog.flag|=memstat_log_allused;

  fs_log_error("all used out now/n");

  return -1;

 }

 j = i;

 }

 

 thisfs_memlog.logstr[j] =(char *) malloc(MAX_ALLOC_FILNAMELEN);

 if (!thisfs_memlog.logstr[j])

 {

 fs_log_error("Failed to malloc %d bytes/n", (long)size);

 thisfs_memlog.free_index = i; 

 return -1; 

 }

 snprintf((char*) thisfs_memlog.logstr[j] ,MAX_ALLOC_FILNAMELEN, logfmt, 1,size,line,file);

 thisfs_memlog.alloc_count++;

 if(thisfs_memlog.max_alloc_count<thisfs_memlog.alloc_count)

 thisfs_memlog.max_alloc_count=thisfs_memlog.alloc_count;

 

 if(thisfs_memlog.max_index< j)

 thisfs_memlog.max_index=j;

 

 i = j+1 ;

again:

 if(thisfs_memlog.alloc_count<thisfs_memlog.limit)             //find free log entry

 {

 for(  ;i<thisfs_memlog.limit;i++)

 {

  if(thisfs_memlog.logstr[i]==MEMLOG_STATEFREE)

   break;

 }

 if(i==thisfs_memlog.limit )

 {

        if(!retry)

        {

   i =  0 ;

   retry = 1;

   goto again;

        }

  thisfs_memlog.flag|=memstat_log_allused;

 }

 else  

 {

  thisfs_memlog.free_index = i;

 }

 }

 else

 {

 thisfs_memlog.flag |= memstat_log_allused; 

 }

 p->logindex = j;

 return 0;

}

 

static void unregister_log(meminfo *p,memstat *stat)

{

 int j = p->logindex;

 

 if(j<0||j>thisfs_memlog.limit)

 return ;

 

 if(thisfs_memlog.logstr[j]==MEMLOG_STATEFREE)

 {

 fs_log_error("already free /n");

 return ;

 }

 

 sscanf(thisfs_memlog.logstr[j], logfmt,  &o_times,&o_size,&o_line, o_file);

 if(o_times>1)

 {

 snprintf((char*) thisfs_memlog.logstr[j] ,MAX_ALLOC_FILNAMELEN, logfmt ,--o_times,o_size-p->size,o_line,o_file);

 return ;

 }

 

 free(thisfs_memlog.logstr[j]);

 thisfs_memlog.logstr[j]=MEMLOG_STATEFREE;

 if((thisfs_memlog.flag & memstat_log_allused) || (thisfs_memlog.free_index> j))

 thisfs_memlog.free_index =j;

 thisfs_memlog.alloc_count--;

 

 if(thisfs_memlog.max_index== j)

 {

 for( ;j>=0&&thisfs_memlog.logstr[j]==MEMLOG_STATEFREE;j--)

  thisfs_memlog.max_index--;

 }

 

 thisfs_memlog.flag &=~memstat_log_allused; 

}

 

 

#endif

 

static OS_EVENT *memstat_sem=NULL;  // perhaps  this module will run on multiple task

static int updata_meminfo(meminfo *p,int type,int size ,memstat *stat ,int line, char *file)

{

 int ret =0;

 

 if(!memstat_sem)

 {

 memstat_sem = AVSemCreate (1);

 if(!memstat_sem)

 {

  fs_log_error("memstat_sem create fail/n", p);

  return -1;

 }

 }

 

 AVSemPend(memstat_sem,0,(INT8U *) &ret);

 if(size>0)

 {

 p->magic=MH_MAGIC_ALLOC|type;

 p->size=size;

 

 if(type==MH_TYPE_C)

 {

  stat->calloc+=size;

  stat->calloc_cnt++;

 }

 else if(type==MH_TYPE_M)

 {

  stat->malloc +=size ;

  stat->malloc_cnt++;

 }

 else

 {

  stat->realloc+=size;

  stat->realloc_cnt++;

 }

  

 stat->total+=size;

 stat->total_cnt++;

 

 if(stat->max_once<size)

 stat->max_once=size;

 if(stat->max_total<stat->total)

 stat->max_total=stat->total;

 if(stat->max_total_cnt<stat->total_cnt)

 stat->max_total_cnt=stat->total_cnt;

 

 if(stat!= &default_memstat)

 {

  if(type==MH_TYPE_C)

  {

   default_memstat.calloc+=size;

   default_memstat.calloc_cnt++;

  }

  else  if(type==MH_TYPE_M)

  {

   default_memstat.malloc +=size ;

   default_memstat.malloc_cnt++;

  }  

  else

  {

   default_memstat.realloc+=size;

   default_memstat.realloc_cnt++;

  }

   

  default_memstat.total+=size; 

  default_memstat.total_cnt++;

  

  if(default_memstat.max_once<size)

  default_memstat.max_once=size;

  if(default_memstat.max_total<default_memstat.total)

  default_memstat.max_total=default_memstat.total;

  

  if(default_memstat.max_total_cnt<default_memstat.total_cnt)

  default_memstat.max_total_cnt=default_memstat.total_cnt;

 }

 

#if( FS_TRACE_MEM&2)

 ret = register_log(p,line,file,stat);

#endif

 

 }

 else

 {  

 type = p->magic&MH_TYPE_MASK;

 if(p->magic==MH_MAGIC_FREE)

 {

  fs_log_error("free 0x%x again/n", p);

  AVSemPost(memstat_sem);

  return -1;

 }

 if((p->magic&MH_MAGIC_MASK)!=MH_MAGIC_ALLOC||type>MH_TYPE_R||type<MH_TYPE_C)

 {

  fs_log_error("free 0x%x mismatch/n", p);

  AVSemPost(memstat_sem);

  return -1;

 }

 p->magic=MH_MAGIC_FREE;             //avoid free again

 

       size=p->size;

 if(type==MH_TYPE_C)

 {

  stat->calloc -=size ;

  stat->calloc_cnt--;

 }

 else if(type==MH_TYPE_M)

 {

  stat->malloc -=size ;

  stat->malloc_cnt--;

 }

 else

 {

  stat->realloc -=size ;

  stat->realloc_cnt--; 

 }

 stat->total-=size ;

 stat->total_cnt--;

 

 if(stat!= &default_memstat)

 {

  if(type==MH_TYPE_C)

  {

   default_memstat.calloc -=size ;

   default_memstat.calloc_cnt--;

  }

  else if(type==MH_TYPE_M)

  {

   default_memstat.malloc -=size ;

   default_memstat.malloc_cnt--;

  }

  else

  {

   default_memstat.realloc -=size ;

   default_memstat.realloc_cnt--;   

  }

  default_memstat.total-=size ;

  default_memstat.total_cnt--;

 }

 

#if( FS_TRACE_MEM&2)

 unregister_log(p,stat);

#endif

 

 }

 

 AVSemPost(memstat_sem);

 return ret;

}

 

/**

* ntmtrace_calloc

*

* Return a pointer to the allocated memory or NULL if the request fails.

*/

void *mtrace_calloc_trace(size_t size,int line, char *file,memstat *stat)

{

 

 meminfo *p;

 

 if(size<=0)

 return NULL;

 

 p =(meminfo *) calloc(1,size+meminfo_len);

 if (!p)

 {

 fs_log_error("Failed to calloc %d bytes/n", (long)size);

 errno = ENOMEM;

 return NULL;

 }

 

 updata_meminfo(p, MH_TYPE_C,size,stat,line,file);

 

 return (char *)p+meminfo_len;

}

 

 

void *mtrace_malloc_trace(size_t size,int line, char *file,memstat *stat)

{

 

 meminfo *p;

 

 if(size<=0)

 return NULL;

 

 p =(meminfo *) malloc(size+meminfo_len);

 if (!p)

 {

 fs_log_error("Failed to malloc %d bytes/n", (long)size); 

 errno = ENOMEM;

 return NULL;

 }

 

 updata_meminfo(p, MH_TYPE_M,size,stat,line,file);

 

 return (char *)p+meminfo_len;

}

 

 

void *mtrace_realloc_trace(void *old,size_t size,int line, char *file,memstat *stat)

{

 

 meminfo *p;

 int type ;

 

 if(size<=0)

 return NULL;

 

 if(old)

 {

 p =(meminfo *) ((char *)old -meminfo_len) ;

 

 if(updata_meminfo(p, -1,-1,stat , -1 ,NULL)!=0)

 {

  return NULL;  

 }

 type = MH_TYPE_R ;

 }

 else

 {

 p = NULL;

 type = MH_TYPE_M ;

 }

 p =(meminfo *) realloc(p,size+meminfo_len);

 if (!p)

 {

 fs_log_error("Failed to realloc %d bytes/n", (long)size);

 errno = ENOMEM;

 return NULL;

 }

 

 updata_meminfo(p, type,size,stat,line,file);

 

 return (char *)p+meminfo_len;

}

 

void mtrace_free_trace(void *pmen,memstat *stat)

{

 

 if(pmen)

 {

 meminfo *p =(meminfo *) ((char *)pmen -meminfo_len) ;

 

 if(updata_meminfo(p, -1,-1,stat, -1,NULL)==0)

 {

  free(p);

 }

 }

}

 

#else

 

 

void *mtrace_calloc(size_t size)

{

 

 void *p;

 

 if(size<=0)

 return NULL;

 

 p = calloc(1,size);

 if (!p)

 {

 fs_log_error("Failed to calloc %d bytes/n", (long)size);

 errno = ENOMEM;

 }

 

 return p;

}

 

 

void *mtrace_malloc(size_t size)

{

 void *p;

 

 if(size<=0)

 return NULL;

 

 p = malloc(size);

 if (!p)

 {

 fs_log_error("Failed to malloc %d bytes/n", (long)size);

 errno = ENOMEM;

 }

 

 return p;

}

 

 

void *mtrace_realloc(void *old,size_t size)

{

 

 void *p;

 

 if(size<=0)

 return NULL;

 

 p = realloc(old,size);

 if (!p)

 {

 fs_log_error("Failed to realloc %d bytes/n", (long)size);

 errno = ENOMEM;

 }

 

 return p;

}

 

void mtrace_free(void *pmen)

{

 if(pmen)

 {

 free(pmen);

 }

}

#endif

 

#if FS_PRINT_ENABLE

extern INT32U OSTime ;

void mtrace_log_redirect(const char *function, const char *file, int line,

      const char *FORMAT, ...)

{

 OS_CPU_SR cpu_sr;

// INT32U  tick = AVTimeGet();          //avoid  OS_EXIT_CRITICAL in printf

 va_list args;

 

 OS_ENTER_CRITICAL();

 printf("/n%s (%s,line=%d,tick=%d)/n--" ,function,file,line,OSTime);

 va_start (args, FORMAT);

 vprintf (FORMAT, args);

 va_end (args);

 OS_EXIT_CRITICAL();

   

}

#endif

 

 

原创粉丝点击