|
|
/************************************************************************
|
|
|
*
|
|
|
* Copyright (C) 2003-2004
|
|
|
* Shenzhen SCADA Control Technology Co., Ltd.
|
|
|
* All rights reserved.
|
|
|
*
|
|
|
* 用于调试内存泄漏的对内存分配函数的实现
|
|
|
*
|
|
|
* 创建日期: 2005/04/12
|
|
|
*
|
|
|
***********************************************************************/
|
|
|
/*#ifdef OS_LINUX
|
|
|
#include <sys/types.h>
|
|
|
#include <sys/time.h>
|
|
|
#endif*/
|
|
|
|
|
|
#include <stdio.h>
|
|
|
#include <stdlib.h>
|
|
|
#include <memory.h>
|
|
|
#include <string.h>
|
|
|
#include <errno.h>
|
|
|
|
|
|
#include "time.h"
|
|
|
#include "list_entry.h"
|
|
|
#include "os_heap.h"
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
#include <winsock2.h>
|
|
|
#include <direct.h>
|
|
|
#include <process.h>
|
|
|
#endif
|
|
|
|
|
|
#ifdef _fclose
|
|
|
#undef _fclose
|
|
|
#endif
|
|
|
#define _fclose(_fp) do {if (_fp) {while (EOF == fclose((_fp)) && EINTR == errno);}} while(0)
|
|
|
|
|
|
/*!
|
|
|
* 堆内存块的类型
|
|
|
*/
|
|
|
typedef enum {
|
|
|
eMalloc = 0, /* 使用malloc分配的内存块 */
|
|
|
eCalloc = 1 /* 使用calloc分配的内存块 */
|
|
|
} heap_type_t;
|
|
|
|
|
|
/*!
|
|
|
* 堆内存块附加头
|
|
|
*
|
|
|
* \note
|
|
|
* 必须保证本结构的尺寸被16整除。
|
|
|
*/
|
|
|
typedef struct tag_heap_head_t {
|
|
|
|
|
|
/* 堆内存块附加头所在的链表的占位 */
|
|
|
list_entry_t anchor;
|
|
|
|
|
|
/*! 堆内存块分配发生的时间 */
|
|
|
struct timeval tv;
|
|
|
|
|
|
/*! 堆内存块类型 */
|
|
|
heap_type_t type;
|
|
|
|
|
|
/*! 堆内存块分配发生的源文件名,NULL表示匿名源文件。 */
|
|
|
char * fl;
|
|
|
|
|
|
/*! 堆内存块分配发生的源代码行 */
|
|
|
int line;
|
|
|
|
|
|
/*! 堆内存块的尺寸(字节) */
|
|
|
size_t size;
|
|
|
|
|
|
} heap_list_t;
|
|
|
|
|
|
|
|
|
/* 边界对齐量 */
|
|
|
#define os_heap_alignment 16
|
|
|
#define os_heap_pad ((sizeof(heap_list_t) % os_heap_alignment) ? (os_heap_alignment - (sizeof(heap_list_t) % os_heap_alignment)) : 0)
|
|
|
|
|
|
/* 根据数据地址求os_heap块的起始地址 */
|
|
|
#define os_heap_start_addr(_data) ((void *)(((unsigned char *)(_data)) - sizeof(heap_list_t) - os_heap_pad))
|
|
|
|
|
|
/* 根据os_heap块的起始地址求数据地址 */
|
|
|
#define os_heap_data_addr(_start) ((void *)(((unsigned char *)(_start)) + sizeof(heap_list_t) + os_heap_pad))
|
|
|
|
|
|
/* 根据数据块的尺寸求os_heap块的尺寸 */
|
|
|
#define os_heap_mb_size(_size) (sizeof(heap_list_t) + os_heap_pad + (_size))
|
|
|
|
|
|
/*
|
|
|
* 内存管理模块:
|
|
|
*
|
|
|
* 1. 初始化过程
|
|
|
* 2. 具有一个私有线程,专门侦听外部的信号,并打印报告。
|
|
|
* 3. 堆内存块链表头、尾指针。
|
|
|
* 4. 互斥访问保护
|
|
|
*/
|
|
|
|
|
|
/* 堆内存块设施是否已经初始化 */
|
|
|
static int g_heap_initialized = 0;
|
|
|
|
|
|
/* 堆内存块双向链表头 */
|
|
|
static list_entry_t g_heap_head;
|
|
|
|
|
|
/*
|
|
|
* 进程中使用heap_malloc/heap_calloc分配的内存
|
|
|
* 且现在还在使用的内存块的总数。
|
|
|
*/
|
|
|
static size_t g_heap_item_counter = 0;
|
|
|
|
|
|
/*
|
|
|
* 进程中使用heap_malloc/heap_calloc分配的内存
|
|
|
* 且现在还在使用的总量(字节)
|
|
|
*/
|
|
|
static size_t g_heap_byte_counter = 0;
|
|
|
|
|
|
|
|
|
/* 初始化堆内存块设施 */
|
|
|
static void heap_intialize();
|
|
|
|
|
|
/*!
|
|
|
* \brief 堆内存分配
|
|
|
*
|
|
|
* \param size 希望分配的内存的尺寸
|
|
|
* \param file 指向发生堆内存分配的源代码文件名称,以null字符结尾。
|
|
|
* 如果等于NULL, 表示匿名源代码文件名(anony)
|
|
|
* \param line 发生堆内存分配的源代码行号
|
|
|
*
|
|
|
* \retutrn
|
|
|
* 对内存分配成功的情况下,返回指向分配的内存的指针。如果内存
|
|
|
* 分配失败,返回NULL。
|
|
|
*
|
|
|
* \note
|
|
|
* 当分配的内存不在使用时,必须调用heap_free是否分配的内存。
|
|
|
*
|
|
|
* 参见 ANSI C 函数 malloc。
|
|
|
*/
|
|
|
void * heap_malloc (
|
|
|
size_t size,
|
|
|
const char * file,
|
|
|
int line )
|
|
|
{
|
|
|
// #ifdef DISABLE_OS_HEAP
|
|
|
return malloc(size);
|
|
|
/*#else /* #ifdef DISABLE_OS_HEAP */
|
|
|
/*
|
|
|
struct timeval tv;
|
|
|
char * fl = NULL;
|
|
|
void * mb = NULL;
|
|
|
heap_list_t * hl = NULL;
|
|
|
|
|
|
if (0 == size)
|
|
|
return NULL;
|
|
|
|
|
|
if (file) {
|
|
|
fl = (char *)malloc(strlen(file) + 4);
|
|
|
if (fl) strcpy (fl, file);
|
|
|
}
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
|
mb = malloc(os_heap_mb_size(size));
|
|
|
if (NULL == mb) {
|
|
|
if (fl) free (fl);
|
|
|
return mb;
|
|
|
} else {
|
|
|
memset (mb, 0, os_heap_mb_size(size));
|
|
|
}
|
|
|
|
|
|
hl = (heap_list_t *)mb;
|
|
|
|
|
|
hl->type = eMalloc;
|
|
|
hl->tv = tv;
|
|
|
hl->fl = fl;
|
|
|
hl->line = line;
|
|
|
hl->size = size;
|
|
|
|
|
|
if (!g_heap_initialized) {
|
|
|
heap_intialize();
|
|
|
g_heap_initialized = 1;
|
|
|
}
|
|
|
|
|
|
insert_tail_list (&g_heap_head, &(hl->anchor));
|
|
|
g_heap_item_counter++;
|
|
|
g_heap_byte_counter += hl->size;
|
|
|
|
|
|
return os_heap_data_addr(hl);
|
|
|
#endif *//* #ifdef DISABLE_OS_HEAP */
|
|
|
}
|
|
|
|
|
|
/*!
|
|
|
* \brief 堆内存分配, 且内存块被初始化0.
|
|
|
*
|
|
|
* \param num 内存块个数
|
|
|
* \param elm_size 每一个内存块的尺寸(字节)
|
|
|
* \param file 指向发生堆内存分配的源代码文件名称,以null字符结尾。
|
|
|
* 如果等于NULL, 表示匿名源代码文件名(anony)
|
|
|
* \param line 发生堆内存分配的源代码行号
|
|
|
*
|
|
|
* \retutrn
|
|
|
* 对内存分配成功的情况下,返回指向分配的内存的指针。如果内存
|
|
|
* 分配失败,返回NULL。
|
|
|
*
|
|
|
* \note
|
|
|
* 当分配的内存不在使用时,必须调用heap_free释放分配的内存。
|
|
|
*
|
|
|
* 参见 ANSI C 函数 calloc。本函数与 calloc 不同之处在于不能
|
|
|
* 象calloc那样保证返回的堆内存块的其实地址对齐在elm_size边界
|
|
|
* 上。
|
|
|
*/
|
|
|
void * heap_calloc (
|
|
|
size_t num,
|
|
|
size_t elm_size,
|
|
|
const char * file,
|
|
|
int line )
|
|
|
{
|
|
|
//#ifdef DISABLE_OS_HEAP
|
|
|
return calloc(num, elm_size);
|
|
|
//#else /* #ifdef DISABLE_OS_HEAP */
|
|
|
|
|
|
/* struct timeval tv;
|
|
|
char * fl = NULL;
|
|
|
void * mb = NULL;
|
|
|
heap_list_t * hl = NULL;
|
|
|
|
|
|
if (0 == num * elm_size)
|
|
|
return NULL;
|
|
|
|
|
|
if (file) {
|
|
|
fl = (char *)malloc(strlen(file) + 4);
|
|
|
if (fl) strcpy (fl, file);
|
|
|
}
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
|
mb = malloc(os_heap_mb_size(elm_size * num));
|
|
|
if (NULL == mb) {
|
|
|
if (fl) free (fl);
|
|
|
return mb;
|
|
|
} else {
|
|
|
memset (mb, 0, os_heap_mb_size(elm_size * num));
|
|
|
}
|
|
|
|
|
|
hl = (heap_list_t *)mb;
|
|
|
|
|
|
hl->type = eCalloc;
|
|
|
hl->tv = tv;
|
|
|
hl->fl = fl;
|
|
|
hl->line = line;
|
|
|
hl->size = elm_size * num;
|
|
|
|
|
|
if (!g_heap_initialized) {
|
|
|
heap_intialize();
|
|
|
g_heap_initialized = 1;
|
|
|
}
|
|
|
|
|
|
insert_tail_list (&g_heap_head, &(hl->anchor));
|
|
|
g_heap_item_counter++;
|
|
|
g_heap_byte_counter += hl->size;
|
|
|
|
|
|
return os_heap_data_addr(hl);
|
|
|
#endif *//* #ifdef DISABLE_OS_HEAP */
|
|
|
}
|
|
|
|
|
|
/*!
|
|
|
* \brief 释放堆内存
|
|
|
*
|
|
|
* \param memblock 指向先前调用heap_alloc/heap_calloc分配的内存。
|
|
|
*
|
|
|
* \return
|
|
|
* 无。
|
|
|
*/
|
|
|
void heap_free (void * mb)
|
|
|
{
|
|
|
#ifdef DISABLE_OS_HEAP
|
|
|
free (mb);
|
|
|
#else /* #ifdef DISABLE_OS_HEAP */
|
|
|
size_t sz = 0;
|
|
|
heap_list_t * hl = NULL;
|
|
|
|
|
|
if (NULL == mb)
|
|
|
return;
|
|
|
|
|
|
if (!g_heap_initialized) {
|
|
|
heap_intialize();
|
|
|
g_heap_initialized = 1;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
hl = (heap_list_t *)os_heap_start_addr(mb);
|
|
|
|
|
|
#if defined(WIN32) && (defined(DEBUG) || defined(_DEBUG))
|
|
|
if (IsBadReadPtr((void *)hl, sizeof(heap_list_t))) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (IsBadReadPtr((void *)hl, os_heap_mb_size(hl->size))) {
|
|
|
return;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
remove_entry_list (&(hl->anchor));
|
|
|
sz = hl->size;
|
|
|
if (hl->fl)
|
|
|
free ((void *)(hl->fl));
|
|
|
free ((void *)hl);
|
|
|
|
|
|
g_heap_item_counter--;
|
|
|
g_heap_byte_counter -= sz;
|
|
|
|
|
|
#endif /* #ifdef DISABLE_OS_HEAP */
|
|
|
}
|
|
|
|
|
|
/*!
|
|
|
* \brief 报告堆内存分配
|
|
|
*
|
|
|
* 调用本函数将堆内存分配情况以追加的方式输出到指定文件中。
|
|
|
*
|
|
|
* \param fname 对内存分配报告将输出到该文件中。如果fname等于NULL,
|
|
|
* 则报告将输出到标准输出上。
|
|
|
*
|
|
|
* \retval -1 函数调用失败。
|
|
|
* \retval 0 函数调用成功。
|
|
|
*
|
|
|
* \note
|
|
|
* 报告的格式如下
|
|
|
* 时间:
|
|
|
* 内存使用总量:
|
|
|
* ......
|
|
|
* 内存块列表i:
|
|
|
* 时间: tv
|
|
|
* 类型: malloc/calloc,
|
|
|
* 源码位置: fl(line)
|
|
|
* 尺寸: size
|
|
|
* ......
|
|
|
*/
|
|
|
int heap_report(const char * fname)
|
|
|
{
|
|
|
/* #ifndef DISABLE_OS_HEAP
|
|
|
int i = 0;
|
|
|
FILE * fp = NULL;
|
|
|
|
|
|
heap_list_t * hl = NULL;
|
|
|
|
|
|
list_entry_t * item = NULL;
|
|
|
list_entry_t * first = NULL;
|
|
|
list_entry_t * next = NULL;
|
|
|
|
|
|
char szdt[32];
|
|
|
|
|
|
if (fname) {
|
|
|
fp = fopen (fname, "a+t");
|
|
|
if (NULL == fp)
|
|
|
return -1;
|
|
|
} else
|
|
|
fp = stdout;
|
|
|
|
|
|
if (!g_heap_initialized) {
|
|
|
heap_intialize();
|
|
|
g_heap_initialized = 1;
|
|
|
if (fname) _fclose(fp);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
fprintf (fp, "\n"
|
|
|
"heap allocation summary...\n"
|
|
|
"time: %s\n"
|
|
|
"heap_item_counter: %u\n"
|
|
|
"heap_byte_counter: %u(bytes)\n",
|
|
|
cur_tm_string (szdt),
|
|
|
g_heap_item_counter,
|
|
|
g_heap_byte_counter);
|
|
|
|
|
|
first = &g_heap_head;
|
|
|
for ( next = first->flink; next != first; next = next->flink )
|
|
|
{
|
|
|
item = next;
|
|
|
hl = CONTAINING_RECORD ( item, heap_list_t, anchor );
|
|
|
|
|
|
fprintf (fp, " heap block %d:"
|
|
|
" times: %s"
|
|
|
" type: %s"
|
|
|
" size: %08u (bytes)"
|
|
|
" source: %s [%d]\n",
|
|
|
i++, timeval_string(&(hl->tv), szdt),
|
|
|
(eMalloc == hl->type ? "malloc" : "calloc"),
|
|
|
hl->size, (hl->fl ? hl->fl : "nony"), hl->line);
|
|
|
}
|
|
|
|
|
|
if (fname) _fclose(fp);
|
|
|
#endif*/
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static void heap_intialize()
|
|
|
{
|
|
|
Initialize_list_head (&g_heap_head);
|
|
|
}
|