一种通用的终端设备内存泄漏检查方案

来源:互联网 发布:网络攻防本质 编辑:程序博客网 时间:2024/05/11 16:36

前言

   目前许多手机、数据卡终端厂商产品普遍还是基于C语言进行开发的,由于C语言动态申请的内存需要程序员负责释放,所以产品中势必会存在内存泄漏。鉴于嵌入式产品内存资源宝贵,积累的泄漏会导致手机无规律死机重启白屏等问题,给产品维护带来了极大的难度。

   本文采取的方案使用于目前主流的高通BREW平台(WCDMA、CDMA),大唐联芯平台(TDSCDMA),英飞凌平台(GSM),具有较强的商用价值。

   本文以联芯平台为例,其平台的内存申请函数为tp_os_mem_malloc,释放函数为tp_os_mem_free。

 

内存管理函数的重新定义

  目前项目的内存管理采用的C语言Malloc分配内存、Free释放内存的管理方式。针对这一内存管理方式,我们考虑使用一个链表记录每一个Malloc分配的内存块信息,在正常释放后则删除该内存块信息。在监测结束后链表中剩下的内存块便为没有释放的内存块,从而检查该模块的内存泄露情况。内存块的记录结构体主要包括文件名、行号、内存块大小以及Malloc返回的地址。具体采用下面代码进行实现:

 

typedef struct DTMEMLEAK_BLOCK
{
    void    *addr;
    char    filename[100];
    int  codeline;
    unsigned long  size;   //在手机上使用考虑16位。
    struct DTMEMLEAK_BLOCK   *next;
}_DTMEMLEAK_BLOCK;

  

在该结构体中以Malloc返回的地址为标志,用来判别不同的内存块。然后重新定义Malloc函数,在每次调用Malloc分配内存的同时使用上面的结构体记录信息,Malloc函数重新定义代码如下:

 

void * Memoryleak_Malloc_zx ( unsigned long s, char *filename, int line)
  {
        _DTMEMLEAK_BLOCK *p_temp = NULL;
        p_temp = (_DTMEMLEAK_BLOCK *)tp_os_mem_malloc(sizeof(_DTMEMLEAK_BLOCK));
        p_temp->addr = tp_os_mem_malloc(s);
        p_temp->codeline = line;
         strncpy(p_temp->filename,filename, 100);
        p_temp->size = s;
        p_temp->next = g_dtmemoryleaktest_head->next;
        g_dtmemoryleaktest_head->next = p_temp;
        return (p_temp->addr);

}

 

通过上面的函数便可以在检测模块调用内存时,将调用信息用链表的形式进行存储。其中的g_zxmemoryleaktest_head为全局变量,用来记录链表起始位置的。在调用的内存块使用完毕正常结束后,将内存块进行释放的同时比较待释放内存的地址,如果该地址存在与记录链表中,则从链表删除其信息。Free函数重新定义代码如下: 

 

 void Memoryleak_Free_zx (void * p)
    {
    _DTMEMLEAK_BLOCK *p_temp = g_dtmemoryleaktest_head->next;
    _DTMEMLEAK_BLOCK *pre = g_dtmemoryleaktest_head;

       while(NULL != p_temp)
         {
        
   
            if (p_temp->addr == p)
            {
                pre->next = p_temp->next;
                tp_os_mem_free( p_temp);
                p_temp = NULL;

         break;
            }
            else
            {
                 pre = p_temp;
                 p_temp = p_temp->next;
   
             }

  }
         tp_os_mem_free(p);
         p = NULL;
         return;

}

 

 

内存管理接口的定义 

 

在完成重新定义后完成后,需要将检测模块的内存管理和非检测模块的内存管理进行一个逻辑选择。检测模块的内存管理使用我们重新定义的内存管理函数进行检测,而非检测模块的内存管理管理依然使用原来的内存管理函数。这样一方面可以减少检测机制对原有程序效率的影响,另一方面可以缩小检测范围提高检测信息的有用率。具体实现代码如下:

void * Memoryleak_Malloc (unsigned long s, char *filename, int line)
   {
    void * p_temp = NULL;
 
    if (!g_dtmemoryleak_isbegin)
        {
               p_temp = tp_os_mem_malloc(s);
         }
    else
        {
       
       //return Memoryleak_Malloc_zx (s, filename, line);
          p_temp = Memoryleak_Malloc_zx (s, filename, line);
        }
 
    if (p_temp != NULL)
        {
         memset(p_temp, 0, s );  /*将内存初始化*/
        }
   
    return (p_temp);
   
  }

 

void Memoryleak_Free (void * p)
{
  
 if (p != NULL)
 {
   if (!g_dtmemoryleak_isbegin)  //control
      {
         tp_os_mem_free(p);
         p = NULL;
         return;
      }
   else
     {
        Memoryleak_Free_zx(p);
        return;
      }  
    }
   return; 
}

 

在模块中所有内存管理都统一调用上面两个函数,由该函数根据情况进行逻辑分配。代码中g_zxmemoryleak_isbegin为一个全局变量,在进入检测模块前赋值为TURE,这样保证检测区域均采用的重定义的内存管理函数;在离开检测区域是赋值为FALSE,保证了其他模块的运行效率。

 

 

 Begin函数中主要是对g_zxmemoryleak_isbegin赋值为TRUE,然后对记录链表进行初始化。而End函数主要是输出最后的文件名、行号等检测信息,并将释放记录链表所占用的内存空间同时对g_zxmemoryleak_isbegin赋值为FALSE。在上面代码中注意第119144行代码,该行代码的作用主要是为了避免重复的BeginEnd。主要因为模块的入口和出口往往不只一个,我们需要在多处添加BeginEnd,这样可能会重复连续两次Begin造成死机。

检测范围的设定

 

最后是对检测模块的设置,在代码中我们采用两个函数实现。在进入模块前添加Begin函数,在结束模块时添加End函数。这样,整个模块便设定为检测模块。函数实现代码如下:

 

void Test_Memoryleak_Begin()
{
 Trace(0,0,"Test_Memoryleak_Begin begin !!!!!!!!!!!"); 
 if(NULL != g_dtmemoryleaktest_head)
{
   return;
}
  g_dtmemoryleaktest_head = (_DTMEMLEAK_BLOCK *)tp_os_mem_malloc(sizeof( _DTMEMLEAK_BLOCK));
  g_dtmemoryleaktest_head->next = NULL;
  g_dtmemoryleaktest_head->addr = NULL;
  g_dtmemoryleaktest_head->codeline = 0;
  memset(g_dtmemoryleaktest_head->filename,0,100*sizeof(char));
  g_dtmemoryleaktest_head->size = 0;
 
  g_dtmemoryleak_isbegin = TRUE;
 
  return ;
}

void Test_Memoryleak_End()
{
  _DTMEMLEAK_BLOCK *p_temp = g_dtmemoryleaktest_head->next;
  _DTMEMLEAK_BLOCK *p_pre = NULL;
   int totalsize=0;

Trace(0,0, "memoryleaktest_result:   /n");
   Trace(0,0, "g_dtmemoryleaktest_head = %d",g_dtmemoryleaktest_head);

if(NULL == g_dtmemoryleaktest_head)
   {
     return;
   }

 
   while(NULL != p_temp )
    {
     Trace(0,0,"filename = %s  ", p_temp->filename );
     Trace(0,0,"codeline = %d  ", p_temp->codeline );
     Trace(0,0,"size = %d  /n", p_temp->size );
     p_pre = p_temp;
     totalsize += p_temp->size;
     p_temp = p_temp->next;
     tp_os_mem_free(p_pre);
    }
    Trace(0,0,"totalsize = %d  /n", totalsize );
 
    tp_os_mem_free(g_dtmemoryleaktest_head);
    g_dtmemoryleaktest_head = NULL;
    g_dtmemoryleak_isbegin = FALSE;
 
     return ;

}

 

 

实际运行情况

 

查找到原有的内存管理是在Sc_share_mem.h文件中进行宏定义转换的,所以更改原有的内存管理接口为新建的内存管理接口。并设置宏开关ZXMEMORYLEAKTEST进行控制管理。

 

其中接着在原工程中添加内存泄漏检测代码文件,并在项目控制文件APP_Config.h中打开宏开关,最后在涉及到的文件中添加上面函数的声明和全局变量的外部引用。在Email模块的进入和退出部分添加相应的BeginEnd函数后,最后工程的输出结果如下:

 

 

 

 

 

 

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 淘宝卖家收钱不发货怎么办 nike拖鞋买小了怎么办 京东换货没货了怎么办 淘宝申请退货退款卖家不处理怎么办 家具连接扣坏了怎么办 锅炉炉水碱度低怎么办 快递里面的东西少了怎么办 很多闲置在家里的东西怎么办 闲鱼买的东西与原物不对怎么办 退款后又发货了怎么办 淘宝卖家不发货也不退款怎么办 已经申请退款卖家仍发货怎么办 淘宝卖家已发货买家申请退款怎么办 爱上街App不发货怎么办 阿里卖家虚假发货怎么办 联系不到淘宝卖家怎么办 没货买家不退款怎么办 买到假货没有发票怎么办 淘宝买的手机发票怎么办 淘宝上的发票丢了怎么办 淘宝买的东西电子发票怎么办 发票联给错顾客怎么办 客人让多给发票怎么办 13岁早晨上学叫不醒怎么办 官换机过保坏了怎么办 官换机在保坏了怎么办 留学生美国东西寄回国怎么办便宜 淘宝东西没到收货了怎么办 在淘宝上买东西被骗怎么办 网上把钱骗走了怎么办 在淘宝上卖号被骗了怎么办 如果淘宝商家保证金不够退款怎么办 天猫账号忘记了怎么办 姨妈来了10天了怎么办 微店商品无资质被下架怎么办 微店商品无资质怎么办 淘宝买东西提前确认收货了怎么办 苹果手机王者荣耀充值充多了怎么办 买家淘宝账号出现虚拟交易怎么办 贵州通登录不上怎么办 华为手机媒体声音小怎么办