Add new routine 're_alloc()' that functions as MONITOR_HEAP-aware libc realloc(). 'nhrealloc()' is the version that passes source file and line info if built with MONITOR_HEAP enabled. The heaplog data might now contain '<' (freed by realloc), '>' (replacement allocation by realloc), and '*' (resized by realloc) entries in addition to the previous '+' (allocated) and '-' (freed) entries. heaputil has already been updated in the NHinternal repository. Move FITSint_() and FITSuint_() from hacklib.c to alloc.c so that they can be accessed by miscellaneous utility programs. Remove three or four copies of FITSint_() that were duplicated in utility programs like dlb and tile2bmp due to those not having access to src/hacklib.o. They do have access to src/alloc.o (and util/panic.o).
249 lines
6.7 KiB
C
249 lines
6.7 KiB
C
/* NetHack 3.7 alloc.c $NHDT-Date: 1596498147 2020/08/03 23:42:27 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.18 $ */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/*-Copyright (c) Robert Patrick Rankin, 2012. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/* to get the malloc() prototype from system.h */
|
|
#define ALLOC_C /* comment line for pre-compiled headers */
|
|
/* since this file is also used in auxiliary programs, don't include all the
|
|
function declarations for all of nethack */
|
|
#define EXTERN_H /* comment line for pre-compiled headers */
|
|
|
|
#include "config.h"
|
|
#ifndef LUA_INTEGER
|
|
#include "nhlua.h"
|
|
#endif
|
|
|
|
#define FITSint(x) FITSint_(x, __func__, (int) __LINE__)
|
|
extern int FITSint_(LUA_INTEGER, const char *, int);
|
|
#define FITSuint(x) FITSuint_(x, __func__, (int) __LINE__)
|
|
extern unsigned FITSuint_(unsigned long long, const char *, int);
|
|
|
|
char *fmt_ptr(const genericptr);
|
|
|
|
#ifdef MONITOR_HEAP
|
|
#undef alloc
|
|
#undef re_alloc
|
|
#undef free
|
|
extern void free(genericptr_t);
|
|
static void heapmon_init(void);
|
|
|
|
static FILE *heaplog = 0;
|
|
static boolean tried_heaplog = FALSE;
|
|
#endif
|
|
|
|
long *alloc(unsigned int);
|
|
long *re_alloc(long *, unsigned int);
|
|
extern void panic(const char *, ...);
|
|
|
|
long *
|
|
alloc(unsigned int lth)
|
|
{
|
|
#ifdef LINT
|
|
/*
|
|
* a ridiculous definition, suppressing
|
|
* "possible pointer alignment problem" for (long *) malloc()
|
|
* from lint
|
|
*/
|
|
long dummy = ftell(stderr);
|
|
|
|
if (lth)
|
|
dummy = 0; /* make sure arg is used */
|
|
return &dummy;
|
|
#else
|
|
register genericptr_t ptr;
|
|
|
|
ptr = malloc(lth);
|
|
#ifndef MONITOR_HEAP
|
|
if (!ptr)
|
|
panic("Memory allocation failure; cannot get %u bytes", lth);
|
|
#endif
|
|
return (long *) ptr;
|
|
#endif
|
|
}
|
|
|
|
/* realloc() call that might get substituted by nhrealloc(p,l,file,line) */
|
|
long *
|
|
re_alloc(long *oldptr, unsigned int newlth)
|
|
{
|
|
/*
|
|
* if LINT support ever gets resurrected,
|
|
* we probably need some hackery here
|
|
*/
|
|
|
|
long *newptr = (long *) realloc((genericptr_t) oldptr, (size_t) newlth);
|
|
#ifndef MONITOR_HEAP
|
|
/* "extend": assume if won't ever fail if asked to shrink */
|
|
if (newlth && !newptr)
|
|
panic("Memory allocation failure; cannot extend to %u bytes", newlth);
|
|
#endif
|
|
return newptr;
|
|
}
|
|
|
|
#ifdef HAS_PTR_FMT
|
|
#define PTR_FMT "%p"
|
|
#define PTR_TYP genericptr_t
|
|
#else
|
|
#define PTR_FMT "%06lx"
|
|
#define PTR_TYP unsigned long
|
|
#endif
|
|
|
|
/* A small pool of static formatting buffers.
|
|
* PTRBUFSIZ: We assume that pointers will be formatted as integers in
|
|
* hexadecimal, requiring at least 16+1 characters for each buffer to handle
|
|
* 64-bit systems, but the standard doesn't mandate that encoding and an
|
|
* implementation could do something different for %p, so we make some
|
|
* extra room.
|
|
* PTRBUFCNT: Number of formatted values which can be in use at the same
|
|
* time. To have more, callers need to make copies of them as they go.
|
|
*/
|
|
#define PTRBUFCNT 4
|
|
#define PTRBUFSIZ 32
|
|
static char ptrbuf[PTRBUFCNT][PTRBUFSIZ];
|
|
static int ptrbufidx = 0;
|
|
|
|
/* format a pointer for display purposes; returns a static buffer */
|
|
char *
|
|
fmt_ptr(const genericptr ptr)
|
|
{
|
|
char *buf;
|
|
|
|
buf = ptrbuf[ptrbufidx];
|
|
if (++ptrbufidx >= PTRBUFCNT)
|
|
ptrbufidx = 0;
|
|
|
|
Sprintf(buf, PTR_FMT, (PTR_TYP) ptr);
|
|
return buf;
|
|
}
|
|
|
|
#ifdef MONITOR_HEAP
|
|
|
|
/* If ${NH_HEAPLOG} is defined and we can create a file by that name,
|
|
then we'll log the allocation and release information to that file. */
|
|
static void
|
|
heapmon_init(void)
|
|
{
|
|
char *logname = getenv("NH_HEAPLOG");
|
|
|
|
if (logname && *logname)
|
|
heaplog = fopen(logname, "w");
|
|
tried_heaplog = TRUE;
|
|
}
|
|
|
|
long *
|
|
nhalloc(unsigned int lth, const char *file, int line)
|
|
{
|
|
long *ptr = alloc(lth);
|
|
|
|
if (!tried_heaplog)
|
|
heapmon_init();
|
|
if (heaplog)
|
|
(void) fprintf(heaplog, "+%5u %s %4d %s\n", lth,
|
|
fmt_ptr((genericptr_t) ptr), line, file);
|
|
/* potential panic in alloc() was deferred til here */
|
|
if (!ptr)
|
|
panic("Cannot get %u bytes, line %d of %s", lth, line, file);
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/* re_alloc() with heap logging; we lack access to the old alloc size */
|
|
long *
|
|
nhrealloc(
|
|
long *oldptr,
|
|
unsigned int newlth,
|
|
const char *file,
|
|
int line)
|
|
{
|
|
long *newptr = re_alloc(oldptr, newlth);
|
|
|
|
if (!tried_heaplog)
|
|
heapmon_init();
|
|
if (heaplog) {
|
|
char op = '*'; /* assume realloc() will change size of previous
|
|
* allocation rather than make a new one */
|
|
|
|
if (newptr != oldptr) {
|
|
/* realloc() freed oldptr */
|
|
(void) fprintf(heaplog, "%c%5s %s %4d %s\n", '<', "",
|
|
fmt_ptr((genericptr_t) oldptr), line, file);
|
|
op = '>'; /* new allocation rather than size-change of old one */
|
|
}
|
|
(void) fprintf(heaplog, "%c%5u %s %4d %s\n", op, newlth,
|
|
fmt_ptr((genericptr_t) newptr), line, file);
|
|
}
|
|
/* potential panic in re_alloc() was deferred til here */
|
|
/* "extend to": assume if won't ever fail if asked to shrink */
|
|
if (newlth && !newptr)
|
|
panic("Cannot extend to %u bytes, line %d of %s", newlth, line, file);
|
|
|
|
return newptr;
|
|
}
|
|
|
|
void
|
|
nhfree(genericptr_t ptr, const char *file, int line)
|
|
{
|
|
if (!tried_heaplog)
|
|
heapmon_init();
|
|
if (heaplog)
|
|
(void) fprintf(heaplog, "- %s %4d %s\n",
|
|
fmt_ptr((genericptr_t) ptr), line, file);
|
|
|
|
free(ptr);
|
|
}
|
|
|
|
/* strdup() which uses our alloc() rather than libc's malloc(),
|
|
with caller tracking */
|
|
char *
|
|
nhdupstr(const char *string, const char *file, int line)
|
|
{
|
|
return strcpy((char *) nhalloc(strlen(string) + 1, file, line), string);
|
|
}
|
|
#undef dupstr
|
|
|
|
#endif /* MONITOR_HEAP */
|
|
|
|
/* strdup() which uses our alloc() rather than libc's malloc();
|
|
not used when MONITOR_HEAP is enabled, but included unconditionally
|
|
in case utility programs get built using a different setting for that */
|
|
char *
|
|
dupstr(const char *string)
|
|
{
|
|
unsigned len = FITSuint(strlen(string));
|
|
return strcpy((char *) alloc(len + 1), string);
|
|
}
|
|
|
|
/* similar for reasonable size strings, but return the length of the input as well */
|
|
char *
|
|
dupstr_n(const char *string, unsigned int *lenout)
|
|
{
|
|
size_t len = strlen(string);
|
|
if(len >= LARGEST_INT)
|
|
panic("string too long");
|
|
*lenout = (unsigned int) len;
|
|
return strcpy((char *) alloc(len + 1), string);
|
|
}
|
|
|
|
/* cast to int or panic on overflow; use via macro */
|
|
int
|
|
FITSint_(LUA_INTEGER i, const char *file, int line)
|
|
{
|
|
int iret = (int) i;
|
|
|
|
if (iret != i)
|
|
panic("Overflow at %s:%d", file, line);
|
|
return iret;
|
|
}
|
|
|
|
unsigned
|
|
FITSuint_(unsigned long long ull, const char *file, int line)
|
|
{
|
|
unsigned uret = (unsigned) ull;
|
|
|
|
if (uret != ull)
|
|
panic("Overflow at %s:%d", file, line);
|
|
return uret;
|
|
}
|
|
|
|
/*alloc.c*/
|