BREW中对malloc和free检测内存泄露

大约3到4年前,我刚刚开始对BREW进行深入的了解和开发。面对的都是C代码,里面充斥着MALLOC和FREE的调用,难道他们就不会封装一下吗?问题在于,程序可能不是自己写的,嗯,程序的某个地方,在某个时机可能会产生内存泄露;程序在退出时,系统不会自动释放占用的内存吗?答案是NO,直到关机之前,BREW都不会帮你释放,包括最新的BREW 4.0系统。而kddi对此也进行了非常严格的重视,程序进入之前剩余内存和程序退出之后剩余内存误差在5KB之内。系统本身会造成一些内存浪费(碎片),即使在这种情况下,也要求误差5KB。想象一下,查找一个地方的内存泄露是多么的麻烦。因此,换个角度,是否让程序来帮助我们查找,当然,答案是肯定的,不然,我就是在骗人了。

注意到一个事情,BREW中对于函数的调用,基本上都是通过宏来调用的,因此,需要使用一种方法智能的屏蔽掉宏,在合适的时候打开宏。闲话少说,代码上来:
memorylog.h:

       #ifndef _H_MEMORYLOG
#define _H_MEMORYLOG
/*!
 * @struct MEMSTRUCT
 * @brief 内存检测用来记录内存的结构
 *
 * @author --==RIX==--
 * @date 2007年08月14日
 */
typedef struct _MEMSTRUCT{
	void* addr;//!< 分配的内存地址
	int length;//!< 分配的内存征长度
	int count;//!< 分配时的分配索引值
	} MEMSTRUCT;
/*!
 * @brief 内存记录
 *
 * @author --==RIX==--
 * @date 2007年08月14日
 * @warning
 *
 * 内存检测仅仅可以在wins平台使用,ARM平台不可以使用
 */
struct MEMORYLOG
	{
		MEMSTRUCT** imem; //!< 已经记录的内存结构
		int num;//!< 目前已经记录的内存记录数目
		int maxnew;//!< 总共分配的内存次数
		int maxdelete;//!< 总共删除的内存次数
};
extern void MEMORYLOG_INIT();
extern void MEMORYLOG_DESTROY();
extern void* myMalloc(int size);
extern void myFree(void* p);

#if MALLOC!=myMalloc
#define MALLOC(size) myMalloc(size)
#endif
#if FREE!=myAlloc
#define FREE(p) myFree(p)
#endif
#endif

MemoryLog.c:

#include "MemoryLog.h"

struct MEMORYLOG ilog;
void* myMalloc(int size)
{
#if MALLOC==myMalloc
#define MALLOC(size) GET_HELPER()->malloc((size))
#endif
return MALLOC(size);
#if MALLOC!=myMalloc
#define MALLOC(size) myMalloc(size)
#endif
}

void myFree(void* p)
{
#if FREE==myFree
#define FREE(p) GET_HELPER()->free((p))
#endif
return FREE(p);
#if FREE!=myFree
#define FREE(p) myFree(p)
#endif
}
       void	MEMORYLOG_INIT()
			{
 ilog.imem = NULL;
 ilog.num = 0;
 ilog.maxnew = 0;
 ilog.maxdelete = 0;
			}
		/*!
		 * @brief 添加一个记录结构
		 *
		 * @param amem 欲添加的记录结构指针
		 * @return 无
		 */
		void MEMORYLOG_Add( MEMSTRUCT* amem )
			{
                MEMSTRUCT** temp = (MEMSTRUCT**)myMalloc( sizeof( MEMSTRUCT* ) * ( ilog.num + 1 ) );
                for( int i = 0;i < ilog.num; i++ )
                    temp[i] = ilog.imem[i];
                ilog.temp[num] = amem;
                myFree( ilog.imem );
                ilog.imem = temp;
                if( ilog.maxnew == 6 )
                    int k = 10;
                ilog.imem[num]->count = ilog.maxnew;
                ilog.num++;
                ilog.maxnew++;
			}
		/*!
		 * @brief 从内存记录中删除一个记录
		 *
		 * @param aindex 欲删除的记录索引
		 * @return 无
		 */
		void MEMORYLOG_SubNode( int aindex )
			{
                MEMSTRUCT** temp = (MEMSTRUCT**)myMalloc( sizeof( MEMSTRUCT* ) * ( ilog.num - 1 ) );
                for( int i = 0;i < ilog.num; i++ )
                    if( i != aindex )
                        temp[i] = ilog.imem[i];
                    else
                        break;
                myFree( ilog.imem[i] );
                for( i = i+1; i < ilog.num; i++ )
                    temp[i-1] = ilog.imem[i];
                myFree( ilog.imem );
                ilog.imem = temp;
                ilog.num--;
                ilog.maxdelete--;
			}
		/*!
		 * @fn MEMORYLOG_Sub( void* aaddr )
		 * @brief 从内存记录中删除一个记录
		 *
		 * @param aaddr 分配了的内存地址
		 * @return 无
		 */
		void MEMORYLOG_Sub( void* aaddr )
			{
                int i = 0;
                for( i = 0; i < ilog.num; i++ )
				{
                    if( ilog.imem[i]->addr == aaddr )
                        break;
				}
                if( i == ilog.num )
				{
                    return;
				}
                MEMORYLOG_SubNode( i );
                
			}
void 	MEMORYLOG_DESTROY()
			{
                if( ilog.num != 0 )
				{
                    if( ilog.num > 0 )
					{
                        for( int i = 0;i < ilog.num; i++ )
                        {
					
                            char temp[255];
                            SNPRINTF(temp, sizeof(temp), "memory leak addr = %x, size = %d, count = %d", ilog.imem[i]->addr, ilog.imem[i]->length, ilog.imem[i]->count );
                            DBGPRINTF( temp );
                        }
					}
				}
                char temp[255];
                SNPRINTF(temp, sizeof(temp), "memory resulet new count = %d, delete count = %d", ilog.maxnew, ilog.maxdelete );
                DBGPRINTF( temp );
                for( int i = 0; i < ilog.num; i++ )
                    myFree(ilog.imem[i]);
                myFree( ilog.imem );
			}

使用的时候,在初始化的地方调用MEMORYLOG_INIT,一般在InitAppData函数中,在最终退出的时候,调用MEMORYLOG_DESTROY,一般在FreeAppData。如果有内存泄露,在BREW Log中将会打印出memory leak addr=....的字样,重要的是count值,这个表示是第几次分配,修改if( ilog.maxnew == 6 )中的数字为count值,然后下断点,当进行到指定次数的内存分配的时候,就会中断的。
理论上来讲,上面的代码是可以正常工作的。嗯,是的,理论上,因为我从开始做BREW的时候,就决定使用C++的方法,上面的方法,是在帮助别人的时候写的,而代码,则是从C++的代码中修改过来的。顺利的话,过两天会说C++的内存检测,将会全面些。
内存检测只能在模拟器上运行,因为ARM版本不允许使用全局变量,以后可能会提到打破这一限制或者变通的方法的。

发布者

rix

如果连自己都不爱自己,哪还有谁来爱你