📄 malloc.c
字号:
/* * 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 + -