2159 lines
50 KiB
C
Executable File
2159 lines
50 KiB
C
Executable File
/*
|
|
* 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: //BBN_Linux/Branch/Branch_for_Rel_TP_ASEAN_20161216/tclinux_phoenix/apps/public/tools/dmalloc-5.5.2/malloc.c#1 $
|
|
*/
|
|
|
|
/*
|
|
* 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 $"
|
|
#else
|
|
static 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;
|
|
}
|
|
#else
|
|
/* oh, well. no idea how to get the environmental variables */
|
|
env_str = "";
|
|
#endif /* GETENV_SAFE */
|
|
#endif /* ! __CYGWIN__ */
|
|
/* process the environmental variable(s) */
|
|
process_environ(env_str);
|
|
|
|
/*
|
|
* 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,
|
|
#if DUMP_UNFREED_SUMMARY_ONLY
|
|
0
|
|
#else
|
|
1
|
|
#endif
|
|
);
|
|
}
|
|
|
|
#if LOG_PNT_TIMEVAL
|
|
{
|
|
TIMEVAL_TYPE now;
|
|
char time_buf1[64], time_buf2[64];
|
|
GET_TIMEVAL(now);
|
|
dmalloc_message("ending time = %s, elapsed since start = %s",
|
|
_dmalloc_ptimeval(&now, time_buf1, sizeof(time_buf1), 0),
|
|
_dmalloc_ptimeval(&now, time_buf2, sizeof(time_buf2), 1));
|
|
}
|
|
#else
|
|
#if HAVE_TIME /* NOT LOG_PNT_TIME */
|
|
{
|
|
TIME_TYPE now;
|
|
char time_buf1[64], time_buf2[64];
|
|
now = time(NULL);
|
|
dmalloc_message("ending time = %s, elapsed since start = %s",
|
|
_dmalloc_ptime(&now, time_buf1, sizeof(time_buf1), 0),
|
|
_dmalloc_ptime(&now, time_buf2, sizeof(time_buf2), 1));
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
in_alloc_b = 0;
|
|
|
|
#if LOCK_THREADS
|
|
unlock_thread();
|
|
#endif
|
|
|
|
/* NOTE: do not set enabled_b to false here */
|
|
}
|
|
|
|
#if FINI_DMALLOC
|
|
/*
|
|
* void __fini_dmalloc
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Automatic function to close dmalloc supported by some operating
|
|
* systems. Pretty cool OS/compiler hack. By default it is not
|
|
* necessary because we use atexit() and on_exit() to register the
|
|
* close functions which are more portable.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* None.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* None.
|
|
*/
|
|
void __fini_dmalloc(void)
|
|
{
|
|
dmalloc_shutdown();
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* DMALLOC_PNT dmalloc_malloc
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Allocate and return a memory block of a certain size.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Valid pointer.
|
|
*
|
|
* Failure - 0L
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* file -> File-name or return-address of the caller.
|
|
*
|
|
* line -> Line-number of the caller.
|
|
*
|
|
* size -> Number of bytes requested.
|
|
*
|
|
* func_id -> Function-id to identify the type of call. See
|
|
* dmalloc.h.
|
|
*
|
|
* alignment -> To align the new block to a certain number of bytes,
|
|
* set this to a value greater than 0.
|
|
*
|
|
* xalloc_b -> If set to 1 then print an error and exit if we run out
|
|
* of memory.
|
|
*/
|
|
DMALLOC_PNT dmalloc_malloc(const char *file, const int line,
|
|
const DMALLOC_SIZE size, const int func_id,
|
|
const DMALLOC_SIZE alignment,
|
|
const int xalloc_b)
|
|
{
|
|
void *new_p;
|
|
DMALLOC_SIZE align;
|
|
|
|
#if DMALLOC_SIZE_UNSIGNED == 0
|
|
if (size < 0) {
|
|
dmalloc_errno = ERROR_BAD_SIZE;
|
|
dmalloc_error("malloc");
|
|
if (tracking_func != NULL) {
|
|
tracking_func(file, line, func_id, size, alignment, NULL, NULL);
|
|
}
|
|
return MALLOC_ERROR;
|
|
}
|
|
#endif
|
|
|
|
if (! dmalloc_in(file, line, 1)) {
|
|
if (tracking_func != NULL) {
|
|
tracking_func(file, line, func_id, size, alignment, NULL, NULL);
|
|
}
|
|
return MALLOC_ERROR;
|
|
}
|
|
|
|
if (alignment == 0) {
|
|
if (func_id == DMALLOC_FUNC_VALLOC) {
|
|
align = BLOCK_SIZE;
|
|
}
|
|
else {
|
|
align = 0;
|
|
}
|
|
}
|
|
else if (alignment >= BLOCK_SIZE) {
|
|
align = BLOCK_SIZE;
|
|
}
|
|
else {
|
|
/*
|
|
* NOTE: Currently, there is no support in the library for
|
|
* memalign on less than block boundaries. It will be non-trivial
|
|
* to support valloc with fence-post checking and the lack of the
|
|
* flag width for dblock allocations.
|
|
*/
|
|
if (! memalign_warn_b) {
|
|
dmalloc_message("WARNING: memalign called without library support");
|
|
memalign_warn_b = 1;
|
|
}
|
|
align = 0;
|
|
/* align = alignment */
|
|
}
|
|
|
|
new_p = _dmalloc_chunk_malloc(file, line, size, func_id, align);
|
|
|
|
check_pnt(file, line, new_p, "malloc");
|
|
|
|
dmalloc_out();
|
|
|
|
if (tracking_func != NULL) {
|
|
tracking_func(file, line, func_id, size, alignment, NULL, new_p);
|
|
}
|
|
|
|
if (xalloc_b && new_p == NULL) {
|
|
char mess[1024], desc[128];
|
|
(void)loc_snprintf(mess, sizeof(mess),
|
|
"Out of memory while allocating %d bytes from '%s'\n",
|
|
size,
|
|
_dmalloc_chunk_desc_pnt(desc, sizeof(desc),
|
|
file, line));
|
|
(void)write(STDERR, mess, strlen(mess));
|
|
_exit(1);
|
|
}
|
|
|
|
return new_p;
|
|
}
|
|
|
|
/*
|
|
* DMALLOC_PNT dmalloc_realloc
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Resizes and old pointer to a new number of bytes.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Valid pointer.
|
|
*
|
|
* Failure - 0L
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* file -> File-name or return-address of the caller.
|
|
*
|
|
* line -> Line-number of the caller.
|
|
*
|
|
* old_pnt -> Pointer to an existing memory chunk that we are
|
|
* resizing. If this is NULL then it basically does a malloc.
|
|
*
|
|
* new_size -> New number of bytes requested for the old pointer.
|
|
*
|
|
* func_id -> Function-id to identify the type of call. See
|
|
* dmalloc.h.
|
|
*
|
|
* xalloc_b -> If set to 1 then print an error and exit if we run out
|
|
* of memory.
|
|
*/
|
|
DMALLOC_PNT dmalloc_realloc(const char *file, const int line,
|
|
DMALLOC_PNT old_pnt, DMALLOC_SIZE new_size,
|
|
const int func_id, const int xalloc_b)
|
|
{
|
|
void *new_p;
|
|
|
|
#if DMALLOC_SIZE_UNSIGNED == 0
|
|
if (new_size < 0) {
|
|
dmalloc_errno = ERROR_BAD_SIZE;
|
|
dmalloc_error("realloc");
|
|
if (tracking_func != NULL) {
|
|
tracking_func(file, line, func_id, new_size, 0, old_pnt, NULL);
|
|
}
|
|
return MALLOC_ERROR;
|
|
}
|
|
#endif
|
|
|
|
if (! dmalloc_in(file, line, 1)) {
|
|
if (tracking_func != NULL) {
|
|
tracking_func(file, line, func_id, new_size, 0, old_pnt, NULL);
|
|
}
|
|
return REALLOC_ERROR;
|
|
}
|
|
|
|
check_pnt(file, line, old_pnt, "realloc-in");
|
|
|
|
#if ALLOW_REALLOC_NULL
|
|
if (old_pnt == NULL) {
|
|
int new_func_id;
|
|
/* shift the function id to be calloc or malloc */
|
|
if (func_id == DMALLOC_FUNC_RECALLOC) {
|
|
new_func_id = DMALLOC_FUNC_CALLOC;
|
|
}
|
|
else {
|
|
new_func_id = DMALLOC_FUNC_MALLOC;
|
|
}
|
|
new_p = _dmalloc_chunk_malloc(file, line, new_size, new_func_id, 0);
|
|
}
|
|
else
|
|
#endif
|
|
#if ALLOW_REALLOC_SIZE_ZERO
|
|
if (new_size == 0) {
|
|
/*
|
|
* If realloc(old_pnt, 0) then free(old_pnt). Thanks to Stefan
|
|
* Froehlich for patiently pointing that the realloc in just
|
|
* about every Unix has this functionality.
|
|
*/
|
|
(void)_dmalloc_chunk_free(file, line, old_pnt, func_id);
|
|
new_p = NULL;
|
|
}
|
|
else
|
|
#endif
|
|
new_p = _dmalloc_chunk_realloc(file, line, old_pnt, new_size, func_id);
|
|
|
|
if (new_p != NULL) {
|
|
check_pnt(file, line, new_p, "realloc-out");
|
|
}
|
|
|
|
dmalloc_out();
|
|
|
|
if (tracking_func != NULL) {
|
|
tracking_func(file, line, func_id, new_size, 0, old_pnt, new_p);
|
|
}
|
|
|
|
if (xalloc_b && new_p == NULL) {
|
|
char mess[1024], desc[128];
|
|
(void)loc_snprintf(mess, sizeof(mess),
|
|
"Out of memory while reallocating %d bytes from '%s'\n",
|
|
new_size, _dmalloc_chunk_desc_pnt(desc, sizeof(desc),
|
|
file, line));
|
|
(void)write(STDERR, mess, strlen(mess));
|
|
_exit(1);
|
|
}
|
|
|
|
return new_p;
|
|
}
|
|
|
|
/*
|
|
* int dmalloc_free
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Release a pointer back into the heap.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - FREE_NOERROR
|
|
*
|
|
* Failure - FREE_ERROR
|
|
*
|
|
* Note: many operating systems define free to return (void) so this
|
|
* return value may be filtered. Dumb.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* file -> File-name or return-address of the caller.
|
|
*
|
|
* line -> Line-number of the caller.
|
|
*
|
|
* pnt -> Existing pointer we are freeing.
|
|
*
|
|
* func_id -> Function-id to identify the type of call. See
|
|
* dmalloc.h.
|
|
*/
|
|
int dmalloc_free(const char *file, const int line, DMALLOC_PNT pnt,
|
|
const int func_id)
|
|
{
|
|
int ret;
|
|
|
|
if (! dmalloc_in(file, line, 1)) {
|
|
if (tracking_func != NULL) {
|
|
tracking_func(file, line, func_id, 0, 0, pnt, NULL);
|
|
}
|
|
return FREE_ERROR;
|
|
}
|
|
|
|
check_pnt(file, line, pnt, "free");
|
|
|
|
ret = _dmalloc_chunk_free(file, line, pnt, func_id);
|
|
|
|
dmalloc_out();
|
|
|
|
if (tracking_func != NULL) {
|
|
tracking_func(file, line, DMALLOC_FUNC_FREE, 0, 0, pnt, NULL);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* DMALLOC_PNT dmalloc_strndup
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Allocate and return an allocated block of memory holding a copy of
|
|
* a string of a certain number of characters.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Valid pointer.
|
|
*
|
|
* Failure - 0L
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* file -> File-name or return-address of the caller.
|
|
*
|
|
* line -> Line-number of the caller.
|
|
*
|
|
* string -> String we are duplicating.
|
|
*
|
|
* len -> Length of the string we are duplicating.
|
|
*
|
|
* xalloc_b -> If set to 1 then print an error and exit if we run out
|
|
* of memory.
|
|
*/
|
|
char *dmalloc_strndup(const char *file, const int line,
|
|
const char *string, const int len,
|
|
const int xalloc_b)
|
|
{
|
|
DMALLOC_SIZE size;
|
|
char *new_string;
|
|
const char *string_p;
|
|
|
|
/* check the arguments */
|
|
if (BIT_IS_SET(_dmalloc_flags, DEBUG_CHECK_FUNCS)) {
|
|
/* we check for pointer plus \0 */
|
|
if (! dmalloc_verify_pnt_strsize(file, line, "strdup", string,
|
|
0 /* not exact */, 1 /* strlen */, len)) {
|
|
dmalloc_message("bad pointer argument found in strdup");
|
|
}
|
|
}
|
|
|
|
/* so we have to figure out the max length of the string directly */
|
|
if (len < 0) {
|
|
size = strlen(string);
|
|
}
|
|
else {
|
|
for (string_p = string; string_p < string + len; string_p++) {
|
|
if (*string_p == '\0') {
|
|
break;
|
|
}
|
|
}
|
|
size = string_p - string;
|
|
}
|
|
|
|
/* allocate space for the \0 */
|
|
new_string = dmalloc_malloc(file, line, size + 1, DMALLOC_FUNC_STRDUP,
|
|
0 /* no alignment */, xalloc_b);
|
|
if (new_string != NULL) {
|
|
strncpy(new_string, string, size);
|
|
new_string[size] = '\0';
|
|
}
|
|
|
|
return new_string;
|
|
}
|
|
|
|
/*************************** external memory calls ***************************/
|
|
|
|
/*
|
|
* DMALLOC_PNT malloc
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Overloading the malloc(3) function. Allocate and return a memory
|
|
* block of a certain size.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Valid pointer.
|
|
*
|
|
* Failure - 0L
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* size -> Number of bytes requested.
|
|
*/
|
|
#undef malloc
|
|
DMALLOC_PNT malloc(DMALLOC_SIZE size)
|
|
{
|
|
char *file;
|
|
|
|
GET_RET_ADDR(file);
|
|
return dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, size, DMALLOC_FUNC_MALLOC,
|
|
0 /* no alignment */, 0 /* no xalloc messages */);
|
|
}
|
|
|
|
/*
|
|
* DMALLOC_PNT calloc
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Overloading the calloc(3) function. Returns a block of zeroed memory.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Valid pointer.
|
|
*
|
|
* Failure - 0L
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* num_elements -> Number of elements being allocated.
|
|
*
|
|
* size -> The number of bytes in each element.
|
|
*/
|
|
#undef calloc
|
|
DMALLOC_PNT calloc(DMALLOC_SIZE num_elements, DMALLOC_SIZE size)
|
|
{
|
|
DMALLOC_SIZE len = num_elements * size;
|
|
char *file;
|
|
|
|
GET_RET_ADDR(file);
|
|
return dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, len, DMALLOC_FUNC_CALLOC,
|
|
0 /* no alignment */, 0 /* no xalloc messages */);
|
|
}
|
|
|
|
/*
|
|
* DMALLOC_PNT realloc
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Overload of realloc(3). Resizes and old pointer to a new number of
|
|
* bytes.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Valid pointer.
|
|
*
|
|
* Failure - 0L
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* old_pnt -> Pointer to an existing memory chunk that we are
|
|
* resizing. If this is NULL then it basically does a malloc.
|
|
*
|
|
* new_size -> New number of bytes requested for the old pointer.
|
|
*/
|
|
#undef realloc
|
|
DMALLOC_PNT realloc(DMALLOC_PNT old_pnt, DMALLOC_SIZE new_size)
|
|
{
|
|
char *file;
|
|
|
|
GET_RET_ADDR(file);
|
|
return dmalloc_realloc(file, DMALLOC_DEFAULT_LINE, old_pnt, new_size,
|
|
DMALLOC_FUNC_REALLOC, 0 /* no xalloc messages */);
|
|
}
|
|
|
|
/*
|
|
* DMALLOC_PNT recalloc
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Overload of recalloc(3) which exists on some systems. Resizes and
|
|
* old pointer to a new number of bytes. If we are expanding, then
|
|
* any new bytes will be zeroed.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Valid pointer.
|
|
*
|
|
* Failure - 0L
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* old_pnt -> Pointer to an existing memory chunk that we are
|
|
* resizing.
|
|
*
|
|
* new_size -> New number of bytes requested for the old pointer.
|
|
*/
|
|
#undef recalloc
|
|
DMALLOC_PNT recalloc(DMALLOC_PNT old_pnt, DMALLOC_SIZE new_size)
|
|
{
|
|
char *file;
|
|
|
|
GET_RET_ADDR(file);
|
|
return dmalloc_realloc(file, DMALLOC_DEFAULT_LINE, old_pnt, new_size,
|
|
DMALLOC_FUNC_RECALLOC, 0 /* no xalloc messages */);
|
|
}
|
|
|
|
/*
|
|
* DMALLOC_PNT memalign
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Overloading the memalign(3) function. Allocate and return a memory
|
|
* block of a certain size which have been aligned to a certain
|
|
* alignment.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Valid pointer.
|
|
*
|
|
* Failure - 0L
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* alignment -> Value to which the allocation must be aligned. This
|
|
* should probably be a multiple of 2 with a maximum value equivalent
|
|
* to the block-size which is often 1k or 4k.
|
|
*
|
|
* size -> Number of bytes requested.
|
|
*/
|
|
#undef memalign
|
|
DMALLOC_PNT memalign(DMALLOC_SIZE alignment, DMALLOC_SIZE size)
|
|
{
|
|
char *file;
|
|
|
|
GET_RET_ADDR(file);
|
|
return dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, size,
|
|
DMALLOC_FUNC_MEMALIGN, alignment,
|
|
0 /* no xalloc messages */);
|
|
}
|
|
|
|
/*
|
|
* DMALLOC_PNT valloc
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Overloading the valloc(3) function. Allocate and return a memory
|
|
* block of a certain size which have been aligned to page boundaries
|
|
* which are often 1k or 4k.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Valid pointer.
|
|
*
|
|
* Failure - 0L
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* size -> Number of bytes requested.
|
|
*/
|
|
#undef valloc
|
|
DMALLOC_PNT valloc(DMALLOC_SIZE size)
|
|
{
|
|
char *file;
|
|
|
|
GET_RET_ADDR(file);
|
|
return dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, size, DMALLOC_FUNC_VALLOC,
|
|
BLOCK_SIZE, 0 /* no xalloc messages */);
|
|
}
|
|
|
|
#ifndef DMALLOC_STRDUP_MACRO
|
|
/*
|
|
* DMALLOC_PNT strdup
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Overload of strdup(3). Allocate and return an allocated block of
|
|
* memory holding a copy of a string.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Valid pointer.
|
|
*
|
|
* Failure - 0L
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* string -> String we are duplicating.
|
|
*/
|
|
#undef strdup
|
|
char *strdup(const char *string)
|
|
{
|
|
int len;
|
|
char *buf, *file;
|
|
|
|
GET_RET_ADDR(file);
|
|
|
|
/* check the arguments */
|
|
if (BIT_IS_SET(_dmalloc_flags, DEBUG_CHECK_FUNCS)) {
|
|
if (! dmalloc_verify_pnt(file, 0 /* no line */, "strdup", string,
|
|
0 /* not exact */, -1)) {
|
|
dmalloc_message("bad pointer argument found in strdup");
|
|
}
|
|
}
|
|
|
|
/* len + \0 */
|
|
len = strlen(string) + 1;
|
|
|
|
buf = dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, len, DMALLOC_FUNC_STRDUP,
|
|
0 /* no alignment */, 0 /* no xalloc messages */);
|
|
if (buf != NULL) {
|
|
(void)memcpy(buf, string, len);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* DMALLOC_PNT strndup
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Overload of strndup(3). Allocate and return an allocated block of
|
|
* memory holding a copy of a string with a maximum length.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - Valid pointer.
|
|
*
|
|
* Failure - 0L
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* string -> String we are duplicating.
|
|
*
|
|
* len -> Length of the string to duplicate.
|
|
*/
|
|
#undef strndup
|
|
char *strndup(const char *string, const DMALLOC_SIZE len)
|
|
{
|
|
int size;
|
|
char *buf, *file;
|
|
const char *string_p;
|
|
|
|
GET_RET_ADDR(file);
|
|
|
|
/* check the arguments */
|
|
if (BIT_IS_SET(_dmalloc_flags, DEBUG_CHECK_FUNCS)) {
|
|
if (! dmalloc_verify_pnt_strsize(file, 0 /* no line */, "strdup", string,
|
|
0 /* not exact */, 1 /* strlen */,
|
|
size)) {
|
|
dmalloc_message("bad pointer argument found in strdup");
|
|
}
|
|
}
|
|
|
|
/* so we have to figure out the max length of the string directly */
|
|
for (string_p = string; string_p < string + len; string_p++) {
|
|
if (*string_p == '\0') {
|
|
break;
|
|
}
|
|
}
|
|
size = string_p - string;
|
|
|
|
/* at 1 for null */
|
|
buf = dmalloc_malloc(file, DMALLOC_DEFAULT_LINE, size + 1,
|
|
DMALLOC_FUNC_STRDUP, 0 /* no alignment */,
|
|
0 /* no xalloc messages */);
|
|
if (buf != NULL) {
|
|
(void)memcpy(buf, string, size);
|
|
buf[size] = '\0';
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* DMALLOC_FREE_RET free
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Release a pointer back into the heap.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Returns FREE_ERROR, FREE_NOERROR or void depending on whether STDC
|
|
* is defined by your compiler.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pnt -> Existing pointer we are freeing.
|
|
*/
|
|
#undef free
|
|
DMALLOC_FREE_RET free(DMALLOC_PNT pnt)
|
|
{
|
|
char *file;
|
|
int ret;
|
|
|
|
GET_RET_ADDR(file);
|
|
ret = dmalloc_free(file, DMALLOC_DEFAULT_LINE, pnt, DMALLOC_FUNC_FREE);
|
|
|
|
#if (defined(__STDC__) && __STDC__ == 1) || defined(__cplusplus) || defined(STDC_HEADERS)
|
|
#else
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* DMALLOC_FREE_RET cfree
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Same as free.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Returns FREE_ERROR, FREE_NOERROR or void depending on whether STDC
|
|
* is defined by your compiler.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pnt -> Existing pointer we are freeing.
|
|
*/
|
|
#undef cfree
|
|
DMALLOC_FREE_RET cfree(DMALLOC_PNT pnt)
|
|
{
|
|
char *file;
|
|
int ret;
|
|
|
|
GET_RET_ADDR(file);
|
|
ret = dmalloc_free(file, DMALLOC_DEFAULT_LINE, pnt, DMALLOC_FUNC_CFREE);
|
|
|
|
#if (defined(__STDC__) && __STDC__ == 1) || defined(__cplusplus) || defined(STDC_HEADERS)
|
|
#else
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
/******************************* utility calls *******************************/
|
|
|
|
/*
|
|
* int dmalloc_verify
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Verify a pointer which has previously been allocated by the
|
|
* library or check the entire heap.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - MALLOC_VERIFY_NOERROR
|
|
*
|
|
* Failure - MALLOC_VERIFY_ERROR
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pnt -> Pointer we are verifying. If 0L then check the entire heap.
|
|
*/
|
|
int dmalloc_verify(const DMALLOC_PNT pnt)
|
|
{
|
|
int ret;
|
|
|
|
if (! dmalloc_in(DMALLOC_DEFAULT_FILE, DMALLOC_DEFAULT_LINE, 0)) {
|
|
return MALLOC_VERIFY_NOERROR;
|
|
}
|
|
|
|
/* should not check heap here because we will be doing it below */
|
|
|
|
if (pnt == NULL) {
|
|
ret = _dmalloc_chunk_heap_check();
|
|
}
|
|
else {
|
|
ret = _dmalloc_chunk_pnt_check("dmalloc_verify", pnt,
|
|
1 /* exact pointer */, 0 /* no strlen */,
|
|
0 /* no min size */);
|
|
}
|
|
|
|
dmalloc_out();
|
|
|
|
if (ret) {
|
|
return MALLOC_VERIFY_NOERROR;
|
|
}
|
|
else {
|
|
return MALLOC_VERIFY_ERROR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* int malloc_verify
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Verify a pointer which has previously been allocated by the
|
|
* library. Same as dmalloc_verify.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - MALLOC_VERIFY_NOERROR
|
|
*
|
|
* Failure - MALLOC_VERIFY_ERROR
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pnt -> Pointer we are verifying. If 0L then check the entire heap.
|
|
*/
|
|
int malloc_verify(const DMALLOC_PNT pnt)
|
|
{
|
|
return dmalloc_verify(pnt);
|
|
}
|
|
|
|
/*
|
|
* int dmalloc_verify_pnt
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function is mainly used by the arg_check.c functions to verify
|
|
* specific pointers. This can be used by users to provide more fine
|
|
* grained tests on pointers.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - MALLOC_VERIFY_NOERROR
|
|
*
|
|
* Failure - MALLOC_VERIFY_ERROR
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* file -> File-name or return-address of the caller. You can use
|
|
* __FILE__ for this argument or 0L for none.
|
|
*
|
|
* line -> Line-number of the caller. You can use __LINE__ for this
|
|
* argument or 0 for none.
|
|
*
|
|
* func -> Function string which is checking the pointer. 0L if none.
|
|
*
|
|
* pnt -> Pointer we are checking.
|
|
*
|
|
* exact_b -> Set to 1 if this pointer was definitely handed back from
|
|
* a memory allocation. If set to 0 then this pointer can be inside
|
|
* another allocation or outside the heap altogether.
|
|
*
|
|
* strlen_b -> Set to 1 to make sure that this pointer can handle
|
|
* strlen(pnt) + 1 bytes up to the maximum specified by min_size. If
|
|
* this is 1 and min_size > 0 then it is in effect a strnlen.
|
|
*
|
|
* min_size -> Make sure that pointer can hold at least that many
|
|
* bytes if inside of the heap. If 0 then don't check the size.
|
|
*/
|
|
int dmalloc_verify_pnt_strsize(const char *file, const int line,
|
|
const char *func, const void *pnt,
|
|
const int exact_b, const int strlen_b,
|
|
const int min_size)
|
|
{
|
|
int ret;
|
|
|
|
if (! dmalloc_in(file, line, 0)) {
|
|
return MALLOC_VERIFY_NOERROR;
|
|
}
|
|
|
|
/* call the pnt checking chunk code */
|
|
ret = _dmalloc_chunk_pnt_check(func, pnt, exact_b, strlen_b, min_size);
|
|
dmalloc_out();
|
|
|
|
if (ret) {
|
|
return MALLOC_VERIFY_NOERROR;
|
|
}
|
|
else {
|
|
return MALLOC_VERIFY_ERROR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* int dmalloc_verify_pnt
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* This function is mainly used by the arg_check.c functions to verify
|
|
* specific pointers. This can be used by users to provide more fine
|
|
* grained tests on pointers.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - MALLOC_VERIFY_NOERROR
|
|
*
|
|
* Failure - MALLOC_VERIFY_ERROR
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* file -> File-name or return-address of the caller. You can use
|
|
* __FILE__ for this argument or 0L for none.
|
|
*
|
|
* line -> Line-number of the caller. You can use __LINE__ for this
|
|
* argument or 0 for none.
|
|
*
|
|
* func -> Function string which is checking the pointer. 0L if none.
|
|
*
|
|
* pnt -> Pointer we are checking.
|
|
*
|
|
* exact_b -> Set to 1 if this pointer was definitely handed back from
|
|
* a memory allocation. If set to 0 then this pointer can be inside
|
|
* another allocation or outside the heap altogether.
|
|
*
|
|
* min_size -> Make sure that pointer can hold at least that many
|
|
* bytes if inside of the heap. If -1 then make sure it can handle
|
|
* strlen(pnt) + 1 bytes (+1 for the \0). If 0 then don't check the
|
|
* size. If you need strnlen functionality with a maximum on the
|
|
* strlen, see dmalloc_verify_pnt_strsize.
|
|
*/
|
|
int dmalloc_verify_pnt(const char *file, const int line, const char *func,
|
|
const void *pnt, const int exact_b,
|
|
const int min_size)
|
|
{
|
|
if (min_size < 0) {
|
|
return dmalloc_verify_pnt_strsize(file, line, func, pnt, exact_b,
|
|
1 /* strlen */, 0 /* no min-size */);
|
|
} else {
|
|
return dmalloc_verify_pnt_strsize(file, line, func, pnt, exact_b,
|
|
0 /* no strlen */, min_size);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* unsigned int dmalloc_debug
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Set the global debug functionality flags. You can also use
|
|
* dmalloc_debug_setup.
|
|
*
|
|
* Note: you cannot add or remove certain flags such as signal
|
|
* handlers since they are setup at initialization time only.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* The old debug flag value.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* flags -> Flag value to set. Pass in 0 to disable all debugging.
|
|
*/
|
|
unsigned int dmalloc_debug(const unsigned int flags)
|
|
{
|
|
unsigned int old_flags;
|
|
|
|
if (! enabled_b) {
|
|
(void)dmalloc_startup(NULL /* no options string */);
|
|
}
|
|
|
|
old_flags = _dmalloc_flags;
|
|
|
|
/* add the new flags */
|
|
_dmalloc_flags = flags;
|
|
|
|
return old_flags;
|
|
}
|
|
|
|
/*
|
|
* unsigned int dmalloc_debug_current
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Returns the current debug functionality flags. This allows you to
|
|
* save a dmalloc library state to be restored later.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Current debug flags.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* None.
|
|
*/
|
|
unsigned int dmalloc_debug_current(void)
|
|
{
|
|
if (! enabled_b) {
|
|
(void)dmalloc_startup(NULL /* no options string */);
|
|
}
|
|
|
|
/* should not check the heap here since we are dumping the debug variable */
|
|
return _dmalloc_flags;
|
|
}
|
|
|
|
/*
|
|
* void dmalloc_debug_setup
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Set the global debugging functionality as an option string.
|
|
* Normally this would be pased in in the DMALLOC_OPTIONS
|
|
* environmental variable. This is here to override the env or for
|
|
* circumstances where modifying the environment is not possible or
|
|
* does not apply such as servers or cgi-bin programs.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* None.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* options_str -> Options string to set the library flags.
|
|
*/
|
|
void dmalloc_debug_setup(const char *options_str)
|
|
{
|
|
if (! enabled_b) {
|
|
(void)dmalloc_startup(options_str);
|
|
/* if we just started up then we don't have to do anything else */
|
|
return;
|
|
}
|
|
|
|
/* we need to lock */
|
|
if (! dmalloc_in(NULL /* no file-name */, 0 /* no line-number */,
|
|
0 /* don't-check-heap */)) {
|
|
return;
|
|
}
|
|
|
|
process_environ(options_str);
|
|
dmalloc_out();
|
|
}
|
|
|
|
/*
|
|
* int dmalloc_examine
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Examine a pointer and pass back information on its allocation size
|
|
* as well as the file and line-number where it was allocated. If the
|
|
* file and line number is not available, then it will pass back the
|
|
* allocation location's return-address if available.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - DMALLOC_NOERROR
|
|
*
|
|
* Failure - DMALLOC_ERROR
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* pnt -> Pointer we are checking.
|
|
*
|
|
* user_size_p <- Pointer to a DMALLOC_SIZE type variable which, if
|
|
* not NULL, will be set to the size of bytes from the pointer.
|
|
*
|
|
* total_size_p <- Poiner to a DMALLOC_SIZE type variable which, if
|
|
* not NULL, will be set to the total size given for this allocation
|
|
* including administrative overhead.
|
|
*
|
|
* file_p <- Pointer to a character pointer which, if not NULL, will
|
|
* be set to the file where the pointer was allocated.
|
|
*
|
|
* line_p <- Pointer to an unsigned integer which, if not NULL, will
|
|
* be set to the line-number where the pointer was allocated.
|
|
*
|
|
* ret_attr_p <- Pointer to a void pointer, if not NULL, will be set
|
|
* to the return-address where the pointer was allocated.
|
|
*
|
|
* used_mark_p <- Poiner to an unsigned integer which, if not NULL,
|
|
* will be set to the mark of when the pointer was last "used". This
|
|
* could be when it was allocated, reallocated, or freed.
|
|
*
|
|
* seen_p <- Poiner to an unsigned long which, if not NULL, will be
|
|
* set to the number of times that this pointer has been allocated,
|
|
* realloced, or freed. NOTE: LOG_PNT_SEEN_COUNT must be set to 1
|
|
* otherwise no seen information is available and it will be set to 0.
|
|
*/
|
|
int dmalloc_examine(const DMALLOC_PNT pnt, DMALLOC_SIZE *user_size_p,
|
|
DMALLOC_SIZE *total_size_p, char **file_p,
|
|
unsigned int *line_p, DMALLOC_PNT *ret_attr_p,
|
|
unsigned long *used_mark_p, unsigned long *seen_p)
|
|
{
|
|
int ret;
|
|
unsigned int user_size_map, tot_size_map;
|
|
unsigned long *loc_seen_p;
|
|
|
|
/*
|
|
* NOTE: we use the size maps because we use a unsigned int size
|
|
* type internally but may use some size_t externally.
|
|
*/
|
|
|
|
/* need to check the heap here since we are geting info from it below */
|
|
if (! dmalloc_in(DMALLOC_DEFAULT_FILE, DMALLOC_DEFAULT_LINE, 1)) {
|
|
return DMALLOC_ERROR;
|
|
}
|
|
|
|
/* NOTE: we do not need the alloc-size info */
|
|
ret = _dmalloc_chunk_read_info(pnt, "dmalloc_examine", &user_size_map,
|
|
&tot_size_map, file_p, line_p, ret_attr_p,
|
|
&loc_seen_p, used_mark_p, NULL, NULL);
|
|
|
|
dmalloc_out();
|
|
|
|
if (ret) {
|
|
SET_POINTER(user_size_p, user_size_map);
|
|
SET_POINTER(total_size_p, tot_size_map);
|
|
if (loc_seen_p == NULL) {
|
|
SET_POINTER(seen_p, 0);
|
|
} else {
|
|
SET_POINTER(seen_p, *loc_seen_p);
|
|
}
|
|
return DMALLOC_NOERROR;
|
|
}
|
|
else {
|
|
return DMALLOC_ERROR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* void dmalloc_track
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Register an allocation tracking function which will be called each
|
|
* time an allocation occurs.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* None.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* track_func -> Function to register as the tracking function. Set
|
|
* to NULL to disable.
|
|
*/
|
|
void dmalloc_track(const dmalloc_track_t track_func)
|
|
{
|
|
tracking_func = track_func;
|
|
}
|
|
|
|
/*
|
|
* unsigned long dmalloc_mark
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Return to the caller the current "mark" which can be used later by
|
|
* dmalloc_log_changed to log the changed pointers since this point.
|
|
* Multiple marks can be saved and used.
|
|
*
|
|
* This is also the iteration number and can be logged at the front of
|
|
* each memory transaction in the logfile with the LOG_ITERATION
|
|
* define in settings.h and can be logged with each pointer with the
|
|
* LOG_PNT_ITERATION define in settings.h.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Current mark value
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* None.
|
|
*/
|
|
unsigned long dmalloc_mark(void)
|
|
{
|
|
if (! enabled_b) {
|
|
(void)dmalloc_startup(NULL /* no options string */);
|
|
}
|
|
|
|
return _dmalloc_iter_c;
|
|
}
|
|
|
|
/*
|
|
* unsigned long dmalloc_memory_allocated
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Return the total number of bytes allocated by the program so far.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Total number of bytes allocated by the program so far.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* None.
|
|
*/
|
|
unsigned long dmalloc_memory_allocated(void)
|
|
{
|
|
if (! enabled_b) {
|
|
(void)dmalloc_startup(NULL /* no options string */);
|
|
}
|
|
|
|
return _dmalloc_alloc_total;
|
|
}
|
|
|
|
/*
|
|
* unsigned int dmalloc_page_size
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Get the page-size being used by dmalloc.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Page size.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* None.
|
|
*/
|
|
unsigned int dmalloc_page_size(void)
|
|
{
|
|
if (! enabled_b) {
|
|
(void)dmalloc_startup(NULL /* no options string */);
|
|
}
|
|
|
|
return BLOCK_SIZE;
|
|
}
|
|
|
|
/*
|
|
* unsigned long dmalloc_count_changed
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Count the changed memory bytes since a particular mark.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Number of bytes since mark.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* mark -> Sets the point from which to count the changed memory. You
|
|
* can use dmalloc_mark to get the current mark value which can later
|
|
* be passed in here. Pass in 0 to report on the unfreed memory since
|
|
* the program started.
|
|
*
|
|
* not_freed_b -> Set to 1 to count the new pointers that are non-freed.
|
|
*
|
|
* free_b -> Set to 1 to count the new pointers that are freed.
|
|
*/
|
|
unsigned long dmalloc_count_changed(const unsigned long mark,
|
|
const int not_freed_b, const int free_b)
|
|
{
|
|
unsigned long mem_count;
|
|
|
|
if (! dmalloc_in(DMALLOC_DEFAULT_FILE, DMALLOC_DEFAULT_LINE, 1)) {
|
|
return 0;
|
|
}
|
|
|
|
if (! BIT_IS_SET(_dmalloc_flags, DEBUG_LOG_TRANS)) {
|
|
dmalloc_message("counting the unfreed memory since mark %lu", mark);
|
|
}
|
|
|
|
mem_count = _dmalloc_chunk_count_changed(mark, not_freed_b, free_b);
|
|
|
|
dmalloc_out();
|
|
|
|
return mem_count;
|
|
}
|
|
|
|
/*
|
|
* void dmalloc_log_status
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Dump dmalloc statistics to logfile.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* None.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* None.
|
|
*/
|
|
void dmalloc_log_stats(void)
|
|
{
|
|
if (! dmalloc_in(DMALLOC_DEFAULT_FILE, DMALLOC_DEFAULT_LINE, 1)) {
|
|
return;
|
|
}
|
|
|
|
_dmalloc_chunk_log_stats();
|
|
|
|
dmalloc_out();
|
|
}
|
|
|
|
/*
|
|
* void dmalloc_log_unfreed
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Dump unfreed-memory info to logfile.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* None.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* None.
|
|
*/
|
|
void dmalloc_log_unfreed(void)
|
|
{
|
|
if (! dmalloc_in(DMALLOC_DEFAULT_FILE, DMALLOC_DEFAULT_LINE, 1)) {
|
|
return;
|
|
}
|
|
|
|
if (! BIT_IS_SET(_dmalloc_flags, DEBUG_LOG_TRANS)) {
|
|
dmalloc_message("dumping the unfreed pointers");
|
|
}
|
|
|
|
/*
|
|
* to log the non-free we are interested in the pointers currently
|
|
* being used
|
|
*/
|
|
_dmalloc_chunk_log_changed(0, 1, 0,
|
|
#if DUMP_UNFREED_SUMMARY_ONLY
|
|
0
|
|
#else
|
|
1
|
|
#endif
|
|
);
|
|
|
|
dmalloc_out();
|
|
}
|
|
|
|
/*
|
|
* void dmalloc_log_changed
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Dump the pointers that have changed since a point in time.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* mark -> Sets the point to compare against. You can use
|
|
* dmalloc_mark to get the current mark value which can later be
|
|
* passed in here. Pass in 0 to log what has changed since the
|
|
* program started.
|
|
*
|
|
* not_freed_b -> Set to 1 to log the new pointers that are non-freed.
|
|
*
|
|
* free_b -> Set to 1 to log the new pointers that are freed.
|
|
*
|
|
* details_b -> Set to 1 to dump the individual pointers that have
|
|
* changed otherwise the summaries will be logged.
|
|
*/
|
|
void dmalloc_log_changed(const unsigned long mark, const int not_freed_b,
|
|
const int free_b, const int details_b)
|
|
{
|
|
if (! dmalloc_in(DMALLOC_DEFAULT_FILE, DMALLOC_DEFAULT_LINE, 1)) {
|
|
return;
|
|
}
|
|
_dmalloc_chunk_log_changed(mark, not_freed_b, free_b, details_b);
|
|
|
|
dmalloc_out();
|
|
}
|
|
|
|
/*
|
|
* void dmalloc_vmessage
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Message writer with vprintf like arguments which adds a line to the
|
|
* dmalloc logfile.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* None.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* format -> Printf-style format statement.
|
|
*
|
|
* args -> Already converted pointer to a stdarg list.
|
|
*/
|
|
void dmalloc_vmessage(const char *format, va_list args)
|
|
{
|
|
_dmalloc_vmessage(format, args);
|
|
}
|
|
|
|
/*
|
|
* void dmalloc_message
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Message writer with printf like arguments which adds a line to the
|
|
* dmalloc logfile.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* None.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* format -> Printf-style format statement.
|
|
*
|
|
* ... -> Variable argument list.
|
|
*/
|
|
void dmalloc_message(const char *format, ...)
|
|
/* __attribute__ ((format (printf, 1, 2))) */
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
_dmalloc_vmessage(format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
/*
|
|
* void dmalloc_get_stats
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Return a number of statistics about the current heap.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* None.
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* heap_low_p <- Pointer to pointer which, if not 0L, will be set to
|
|
* the low address in the heap.
|
|
*
|
|
* heap_high_p <- Pointer to pointer which, if not 0L, will be set to
|
|
* the high address in the heap.
|
|
*
|
|
* total_space_p <- Pointer to an unsigned long which, if not 0L, will
|
|
* be set to the total space managed by the library including user
|
|
* space, administrative space, and overhead.
|
|
*
|
|
* user_space_p <- Pointer to an unsigned long which, if not 0L, will
|
|
* be set to the space given to the user process (allocated and free).
|
|
*
|
|
* current_allocated_p <- Pointer to an unsigned long which, if not
|
|
* 0L, will be set to the current allocated space given to the user
|
|
* process.
|
|
*
|
|
* current_pnt_np <- Pointer to an unsigned long which, if not 0L,
|
|
* will be set to the current number of pointers allocated by the user
|
|
* process.
|
|
*
|
|
* max_allocated_p <- Pointer to an unsigned long which, if not 0L,
|
|
* will be set to the maximum allocated space given to the user
|
|
* process.
|
|
*
|
|
* max_pnt_np <- Pointer to an unsigned long which, if not 0L, will be
|
|
* set to the maximum number of pointers allocated by the user
|
|
* process.
|
|
*
|
|
* max_one_p <- Pointer to an unsigned long which, if not 0L, will be
|
|
* set to the maximum allocated with 1 call by the user process.
|
|
*/
|
|
void dmalloc_get_stats(DMALLOC_PNT *heap_low_p,
|
|
DMALLOC_PNT *heap_high_p,
|
|
unsigned long *total_space_p,
|
|
unsigned long *user_space_p,
|
|
unsigned long *current_allocated_p,
|
|
unsigned long *current_pnt_np,
|
|
unsigned long *max_allocated_p,
|
|
unsigned long *max_pnt_np,
|
|
unsigned long *max_one_p)
|
|
{
|
|
_dmalloc_chunk_get_stats(heap_low_p, heap_high_p, total_space_p,
|
|
user_space_p, current_allocated_p, current_pnt_np,
|
|
max_allocated_p, max_pnt_np, max_one_p);
|
|
}
|
|
|
|
/*
|
|
* const char *dmalloc_strerror
|
|
*
|
|
* DESCRIPTION:
|
|
*
|
|
* Convert a dmalloc error code into its string equivalent.
|
|
*
|
|
* RETURNS:
|
|
*
|
|
* Success - String version of the error
|
|
*
|
|
* Failure - The string "unknown error"
|
|
*
|
|
* ARGUMENTS:
|
|
*
|
|
* error_num -> Error number we are converting.
|
|
*/
|
|
const char *dmalloc_strerror(const int error_num)
|
|
{
|
|
error_str_t *err_p;
|
|
|
|
/* should not dmalloc_in here because _dmalloc_error calls this */
|
|
|
|
for (err_p = error_list; err_p->es_error != 0; err_p++) {
|
|
if (err_p->es_error == error_num) {
|
|
return err_p->es_string;
|
|
}
|
|
}
|
|
|
|
return INVALID_ERROR;
|
|
}
|