⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 malloc.c

📁 测试内存泄露工具
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * user-level memory-allocation routines * * Copyright 2000 by Gray Watson * * This file is part of the dmalloc package. * * Permission to use, copy, modify, and distribute this software for * any purpose and without fee is hereby granted, provided that the * above copyright notice and this permission notice appear in all * copies, and that the name of Gray Watson not be used in advertising * or publicity pertaining to distribution of the document or software * without specific, written prior permission. * * Gray Watson makes no representations about the suitability of the * software described herein for any purpose.  It is provided "as is" * without express or implied warranty. * * The author may be contacted via http://dmalloc.com/ * * $Id: malloc.c,v 1.183 2004/09/13 22:37:36 gray Exp $ *//* * This file contains the user-level calls to the memory allocation * routines.  It handles a lot of the miscellaneous support garbage for * chunk.c which is the real heap manager. */#include <stdio.h>				/* for sprintf sometimes */#if HAVE_STDLIB_H# include <stdlib.h>				/* for atexit */#endif#if HAVE_STRING_H# include <string.h>				/* for strlen */#endif#if HAVE_UNISTD_H# include <unistd.h>				/* for write */#endif/* * cygwin includes */#if HAVE_SYS_CYGWIN_H# include <sys/cygwin.h>#endif#if HAVE_STDARG_H# include <stdarg.h>#endif#if HAVE_W32API_WINDEF_H# include <w32api/windef.h>#endif#if HAVE_W32API_WINBASE_H# include <w32api/winbase.h>#endif#include "conf.h"				/* up here for _INCLUDE */#if LOG_PNT_TIMEVAL# ifdef TIMEVAL_INCLUDE#  include TIMEVAL_INCLUDE# endif#else# if HAVE_TIME /* NOT LOG_PNT_TIME */#  ifdef TIME_INCLUDE#   include TIME_INCLUDE#  endif# endif#endif#if LOCK_THREADS#if HAVE_PTHREAD_H#include <pthread.h>#endif#if HAVE_PTHREADS_H#include <pthreads.h>#endif#endif#if SIGNAL_OKAY && HAVE_SIGNAL_H#include <signal.h>#endif#define DMALLOC_DISABLE#include "dmalloc.h"#include "chunk.h"#include "compat.h"#include "debug_tok.h"#include "env.h"#include "error.h"#include "error_val.h"#include "heap.h"#include "dmalloc_loc.h"#include "malloc_funcs.h"#include "return.h"#if LOCK_THREADS#if IDENT_WORKS#ident "@(#) $Information: lock-threads is enabled $"#elsestatic char *information = "@(#) $Information: lock-threads is enabled $";#endif#endif/* exported variables *//* internal dmalloc error number for reference purposes only */int		dmalloc_errno = ERROR_NONE;/* logfile for dumping dmalloc info, DMALLOC_LOGFILE env var overrides this */char		*dmalloc_logpath = NULL;/* local variables */static	int		enabled_b = 0;		/* have we started yet? */static	int		in_alloc_b = 0;		/* can't be here twice */static	int		do_shutdown_b = 0;	/* execute shutdown soon */static	int		memalign_warn_b = 0;	/* memalign warning printed?*/static	dmalloc_track_t	tracking_func = NULL;	/* memory trxn tracking func *//* debug variables */static	char		*start_file = NULL;	/* file to start at */static	int		start_line = 0;		/* line to start */static	unsigned long	start_iter = 0;		/* start after X iterations */static	unsigned long	start_size = 0;		/* start after X bytes */static	int		thread_lock_c = 0;	/* lock counter *//****************************** thread locking *******************************/#if LOCK_THREADS#ifdef THREAD_MUTEX_T/* * Define a global variable we use as a lock counter. * * NOTE: we do not use the PTHREAD_MUTEX_INITIALIZER since this * basically delays the pthread_mutex_init call to when * pthread_mutex_lock is called for the first time (at least on * freebsd).  Since we don't want to go recursive into the pthread * library when we go to lock our mutex variable, we want to force the * initialization to happen beforehand with a call to * pthread_mute_init. */static THREAD_MUTEX_T dmalloc_mutex;#else#error We need to have THREAD_MUTEX_T defined by the configure script#endif#endif/* * THREADS LOCKING: * * Because we need to protect for multiple threads making calls into * the dmalloc library at the same time, we need to initialize and use * a thread mutex variable.  The problem is that most thread libraries * uses malloc itself and do not like to go recursive. * * There are two places where we may have this problem.  One is when * we try to use our mutex-lock variable when pthreads is starting up * in a shaky state.  The thread library allocates some space, the * dmalloc library needs to lock its mutex variable so calls back into * the pthread library before it is ready for a call, and a core dump * is probably the result. * * We hopefully solve this by having the dmalloc library not lock * during the first couple of memory transactions.  The number is * controlled by lock-on dmalloc program environmental setting (set * with ``dmalloc -o X'').  You will have to play with the value.  Too * many will cause two threads to march into the dmalloc code at the * same time generating a ERROR_IN_TWICE error.  Too few and you will * get a core dump in the pthreads initialization code. * * The second place where we might go recursive is when we go to * actually initialize our mutex-lock before we can use it.  The * THREAD_INIT_LOCK variable (in settings.h) defines that the * initialization happens 2 memory transactions before the library * begins to use the mutex (lock-on - 2).  It we waited to initialize * the variable right before we used it, the pthread library might * want to allocate some memory for the variable causing a recursive * call and probably a seg-fault -- at least in OSF.  If people need * to have this variable also be runtime configurable or would like to * present an alternative default, please let me know. */#if LOCK_THREADS/* * mutex lock the malloc library */static	void	lock_thread(void){  /* we only lock if the lock-on counter has reached 0 */  if (thread_lock_c == 0) {#if HAVE_PTHREAD_MUTEX_LOCK    pthread_mutex_lock(&dmalloc_mutex);#endif  }}/* * mutex unlock the malloc library */static	void	unlock_thread(void){  /* if the lock-on counter has not reached 0 then count it down */  if (thread_lock_c > 0) {    thread_lock_c--;    /*     * As we approach the time when we start mutex locking the     * library, we need to init the mutex variable.  This sets how     * many times before we start locking should we init the variable     * taking in account that the init itself might generate a call     * into the library.  Ugh.     */    if (thread_lock_c == THREAD_INIT_LOCK) {#if HAVE_PTHREAD_MUTEX_INIT      /*       * NOTE: we do not use the PTHREAD_MUTEX_INITIALIZER since this       * basically delays the pthread_mutex_init call to when       * pthread_mutex_lock is called for the first time (at least on       * freebsd).  Since we don't want to go recursive into the       * pthread library when we go to lock our mutex variable, we       * want to force the initialization to happen beforehand with a       * call to pthread_mute_init.       */      pthread_mutex_init(&dmalloc_mutex, THREAD_LOCK_INIT_VAL);#endif    }  }  else if (thread_lock_c == 0) {#if HAVE_PTHREAD_MUTEX_UNLOCK    pthread_mutex_unlock(&dmalloc_mutex);#endif  }}#endif/****************************** local utilities ******************************//* * check out a pointer to see if we were looking for it.  this should * be re-entrant and it may not return. */static	void	check_pnt(const char *file, const int line, const void *pnt,			  const char *label){  static unsigned long	addr_c = 0;  char			where_buf[64];    if (_dmalloc_address == NULL || pnt != _dmalloc_address) {    return;  }    addr_c++;  dmalloc_message("address '%#lx' found in '%s' at pass %ld from '%s'",		  (unsigned long)pnt, label, addr_c,		  _dmalloc_chunk_desc_pnt(where_buf, sizeof(where_buf), file,					  line));    /* NOTE: if address_seen_n == 0 then never quit */  if (_dmalloc_address_seen_n > 0 && addr_c >= _dmalloc_address_seen_n) {    dmalloc_errno = ERROR_IS_FOUND;    dmalloc_error("check_pnt");  }}/* * process the values of dmalloc environ variables */static	void	process_environ(const char *option_str){  /*   * we have a static here so we can store the string without getting   * into problems   */  static char	options[1024];    /* process the options flag */  if (option_str == NULL) {    options[0] = '\0';  }  else {    strncpy(options, option_str, sizeof(options));    options[sizeof(options) - 1] = '\0';  }    _dmalloc_environ_process(options, &_dmalloc_address,			   (long *)&_dmalloc_address_seen_n, &_dmalloc_flags,			   &_dmalloc_check_interval, &_dmalloc_lock_on,			   &dmalloc_logpath, &start_file, &start_line,			   &start_iter, &start_size, &_dmalloc_memory_limit);  thread_lock_c = _dmalloc_lock_on;    /* if we set the start stuff, then check-heap comes on later */  if (start_iter > 0 || start_size > 0) {    BIT_CLEAR(_dmalloc_flags, DEBUG_CHECK_HEAP);  }    /* indicate that we should reopen the logfile if we need to */  _dmalloc_reopen_log();  #if LOCK_THREADS == 0  /* was thread-lock-on specified but not configured? */  if (_dmalloc_lock_on > 0) {    dmalloc_errno = ERROR_LOCK_NOT_CONFIG;    _dmalloc_die(0);  }#endif}/************************** startup/shutdown calls ***************************/#if SIGNAL_OKAY/* * signal catcher */static	RETSIGTYPE	signal_handler(const int sig){  dmalloc_message("caught signal %d", sig);  /* if we are already inside malloc then do the shutdown later */  if (in_alloc_b) {    do_shutdown_b = 1;  }  else {    dmalloc_shutdown();  }}#endif/* * startup the memory-allocation module */static	int	dmalloc_startup(const char *debug_str){  static int	some_up_b = 0;  const char	*env_str;#ifdef __CYGWIN__  char		env_buf[256];#endif    /* have we started already? */  if (enabled_b) {    return 0;  }    if (! some_up_b) {    /* set this up here so if an error occurs below, it will not try again */    some_up_b = 1;    #if LOG_PNT_TIMEVAL    GET_TIMEVAL(_dmalloc_start);#else#if HAVE_TIME /* NOT LOG_PNT_TIME */    _dmalloc_start = time(NULL);#endif#endif        /*     * If we are running under Cygwin then getenv may not be safe.  We     * try to use the GetEnvironmentVariableA function instead.     */#if defined(__CYGWIN__) && HAVE_GETENVIRONMENTVARIABLEA    /* use this function instead of getenv */    GetEnvironmentVariableA(OPTIONS_ENVIRON, env_buf, sizeof(env_buf));    env_str = env_buf;#else /* ! __CYGWIN__ */#if GETENV_SAFE    /* get the options flag */    if (debug_str == NULL) {      env_str = getenv(OPTIONS_ENVIRON);    }    else {      env_str = debug_str;    }        /* process the environmental variable(s) */    process_environ(env_str);#endif /* GETENV_SAFE */#endif /* ! __CYGWIN__ */        /*     * Tune the environment here.  If we have a start-file,     * start-count, or interval enabled then make sure the check-heap     * flag is cleared.     */     if (start_file != NULL	|| start_iter > 0	|| start_size > 0	|| _dmalloc_check_interval > 0) {      BIT_CLEAR(_dmalloc_flags, DEBUG_CHECK_HEAP);    }        /* startup heap code */    if (! _dmalloc_heap_startup()) {      return 0;    }        /* startup the chunk lower-level code */    if (! _dmalloc_chunk_startup()) {      return 0;    }  }  #if LOCK_THREADS  if (thread_lock_c > 0) {    return 1;  }#endif    /*   * We have initialized all of our code.   *   * NOTE: set this up here so if an error occurs below, it will not   * try again   */  enabled_b = 1;    /*   * NOTE: we may go recursive below here becasue atexit or on_exit   * may ask for memory to be allocated.  We won't worry about it and   * will just give it to them.  We hope that atexit didn't start the   * allocating.  Ugh.   */#if AUTO_SHUTDOWN  /* NOTE: I use the else here in case some dumb systems has both */#if HAVE_ATEXIT  (void)atexit(dmalloc_shutdown);#else#if HAVE_ON_EXIT  (void)on_exit(dmalloc_shutdown, NULL);#endif /* HAVE_ON_EXIT */#endif /* ! HAVE_ATEXIT */#endif /* AUTO_SHUTDOWN */  #if SIGNAL_OKAY  if (BIT_IS_SET(_dmalloc_flags, DEBUG_CATCH_SIGNALS)) {#ifdef SIGNAL1    (void)signal(SIGNAL1, signal_handler);#endif#ifdef SIGNAL2    (void)signal(SIGNAL2, signal_handler);#endif#ifdef SIGNAL3    (void)signal(SIGNAL3, signal_handler);#endif#ifdef SIGNAL4    (void)signal(SIGNAL4, signal_handler);#endif#ifdef SIGNAL5    (void)signal(SIGNAL5, signal_handler);#endif#ifdef SIGNAL6    (void)signal(SIGNAL6, signal_handler);#endif  }#endif /* SIGNAL_OKAY */    return 1;}/* * static int dmalloc_in * * DESCRIPTION: * * Call to the alloc routines has been made.  Do some initialization, * locking, and check some debug variables. * * RETURNS: * * Success - 1 * * Failure - 0 * * ARGUMENTS: * * file -> File-name or return-address of the caller. * * line -> Line-number of the caller. * * check_heap_b -> Set to 1 if it is okay to check the heap.  If set * to 0 then the caller will check it itself or it is a non-invasive * call. */static	int	dmalloc_in(const char *file, const int line,			   const int check_heap_b){  if (_dmalloc_aborting_b) {    return 0;  }    /*   * NOTE: we need to do this outside of lock to get env vars   * otherwise our _dmalloc_lock_on variable won't be initialized and   * the THREAD_LOCK will flip.   */  if (! enabled_b) {    if (! dmalloc_startup(NULL /* no options string */)) {      return 0;    }  }  #if LOCK_THREADS  lock_thread();#endif    if (in_alloc_b) {    dmalloc_errno = ERROR_IN_TWICE;    dmalloc_error("dmalloc_in");    /* NOTE: dmalloc_error may die already */    _dmalloc_die(0);    /*NOTREACHED*/  }    in_alloc_b = 1;    /* increment our interval */  _dmalloc_iter_c++;    /* check start file/line specifications */  if ((! BIT_IS_SET(_dmalloc_flags, DEBUG_CHECK_HEAP))      && start_file != NULL      && file != DMALLOC_DEFAULT_FILE      && line != DMALLOC_DEFAULT_LINE      && strcmp(start_file, file) == 0      && (start_line == 0 || start_line == line)) {    BIT_SET(_dmalloc_flags, DEBUG_CHECK_HEAP);    /*     * we disable the start file so we won't check this again and the     * interval can go on/off     */    start_file = NULL;  }    /* start checking heap after X times */  else if (start_iter > 0) {    if (--start_iter == 0) {      BIT_SET(_dmalloc_flags, DEBUG_CHECK_HEAP);      /*       * this is automatically disabled since it goes to 0 so the       * interval can go on/off       */    }  }    else if (start_size > 0 && start_size >= _dmalloc_alloc_total) {    BIT_SET(_dmalloc_flags, DEBUG_CHECK_HEAP);    start_size = 0;    /* disable this check so the interval can go on/off */  }    /* checking heap every X times */  else if (_dmalloc_check_interval > 0) {    if (_dmalloc_iter_c % _dmalloc_check_interval == 0) {      BIT_SET(_dmalloc_flags, DEBUG_CHECK_HEAP);    }    else {       BIT_CLEAR(_dmalloc_flags, DEBUG_CHECK_HEAP);    }  }    /* after all that, do we need to check the heap? */  if (check_heap_b && BIT_IS_SET(_dmalloc_flags, DEBUG_CHECK_HEAP)) {    (void)_dmalloc_chunk_heap_check();  }    return 1;}/* * Going out of the alloc routines back to user space. */static	void	dmalloc_out(void){  in_alloc_b = 0;  #if LOCK_THREADS  unlock_thread();#endif    if (do_shutdown_b) {    dmalloc_shutdown();  }}/***************************** exported routines *****************************//* * void dmalloc_shutdown * * DESCRIPTION: * * Shutdown the dmalloc library and provide statistics if necessary. * * RETURNS: * * None. * * ARGUMENTS: * * None. */void	dmalloc_shutdown(void){  /* NOTE: do not generate errors for IN_TWICE here */    /* if we're already in die mode leave fast and quietly */  if (_dmalloc_aborting_b) {    return;  }    /*   * Make sure that the log file is open.  We do this here because we   * might cause an allocation in the open() and don't want to go   * recursive.   */  _dmalloc_open_log();    /* if we've died in dmalloc somewhere then leave fast and quietly */  if (in_alloc_b) {    return;  }  #if LOCK_THREADS  lock_thread();#endif    /* we do it again in case the lock synced the flag to true now */  if (in_alloc_b) {#if LOCK_THREADS    unlock_thread();#endif    return;  }    in_alloc_b = 1;    /*   * Check the heap since we are dumping info from it.  We check it   * when check-blank is enabled do make sure all of the areas have   * not been overwritten.  Thanks Randell.   */  if (BIT_IS_SET(_dmalloc_flags, DEBUG_CHECK_HEAP)      || BIT_IS_SET(_dmalloc_flags, DEBUG_CHECK_BLANK)      || BIT_IS_SET(_dmalloc_flags, DEBUG_CHECK_SHUTDOWN)) {    (void)_dmalloc_chunk_heap_check();  }    /* dump some statistics to the logfile */  if (BIT_IS_SET(_dmalloc_flags, DEBUG_LOG_STATS)) {    _dmalloc_chunk_log_stats();  }    /* report on non-freed pointers */  if (BIT_IS_SET(_dmalloc_flags, DEBUG_LOG_NONFREE)) {    _dmalloc_chunk_log_changed(0, 1, 0,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -