/************************************************************************ * * Copyright (C) 2003-2004 * Shenzhen SCADA Control Technology Co., Ltd. * All rights reserved. * * 用于调试内存泄漏的对内存分配函数的实现 * * 创建日期: 2005/04/12 * ***********************************************************************/ /*#ifdef OS_LINUX #include #include #endif*/ #include #include #include #include #include #include "time.h" #include "list_entry.h" #include "os_heap.h" #if defined(_WIN32) #include #include #include #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); }