Files
nethack/util/heaputil.c
2002-01-05 21:06:00 +00:00

326 lines
7.6 KiB
C

/* SCCS Id: @(#)heaputil.c 3.3 94/07/17 */
/* Copyright (c) Michael Allison, Toronto, 1994 */
/* NetHack may be freely redistributed. See license for details. */
/*
* We want to know the following information:
*
* 1. allocations that are made but never freed, and where they occur.
* 2. attempts to free unallocated blocks.
* 3. the peak amount of heap space allocated during the program.
*
*/
#include "config.h"
#include <ctype.h>
#ifdef MICRO
#include <stdlib.h>
#endif
#ifdef MICRO /* see src/alloc.c */
# define MONITOR_HEAP_FMT
#endif
#ifdef MONITOR_HEAP_FMT
# define PTR_FMT "%p"
# define PTR_TYP genericptr_t /* (void *) */
#else
# define PTR_FMT "%lx"
# define PTR_TYP unsigned long
#endif
extern genericptr_t FDECL(malloc,(size_t));
#ifdef free
#undef free
#endif
extern void FDECL(free,(genericptr_t));
#define quit() exit(EXIT_FAILURE)
static void NDECL(out_of_memory);
static void FDECL(doline,(char *));
static void FDECL(chain,(char *));
static void FDECL(unchain,(char *));
static void NDECL(walkblocks);
static struct memblock *FDECL(findprev,(PTR_TYP));
static void FDECL(btempl,(char *));
#ifdef VMS
static FILE *vms_fopen(name, mode) const char *name, *mode;
{
return fopen(name, mode, "mbc=64", "shr=nil");
}
# define fopen(f,m) vms_fopen(f,m)
#endif
#define DEFAULTNAME "heapuse.log"
#define MAXERR 4
#ifndef _MAX_PATH
#define _MAX_PATH 120
#endif
struct memblock {
struct memblock *next;
long sequence, bsize;
PTR_TYP address;
char fileinfo[5 + _MAX_PATH + 1];
};
/* a cheap way to try to split addresses into two categories */
#define CHAIN_SELECT(X) (((unsigned long)X >> 10) & 1)
#define OPERATION 0 /* first character; + for alloc, - for free */
#define BLKSIZE 1 /* block size; 5 digits starting at 2nd char */
#define ADDRESS 7 /* sizeof "+12345 " - sizeof "" */
#define FILEINFO fileinfo_offset
#define terminate_fields(a) \
a[ADDRESS - 1] = a[FILEINFO - 1] = '\0'
#define zero_fields(a) \
a[OPERATION] = a[BLKSIZE] = a[ADDRESS] = a[FILEINFO] = '\0'
static int fileinfo_offset;
static struct memblock *firstblock[2] = {0,0}, *blkcache = 0;
static long peakmem;
static long curmem;
static long totaldynmem;
static long lineno;
static FILE *infile;
static char line[255];
static const char *infilenm;
static int errcount;
static int have_template;
int main(argc, argv)
int argc;
char *argv[];
{
infilenm = (argc < 2) ? getenv("NH_HEAPLOG") : argv[1];
if (!infilenm || !*infilenm) infilenm = DEFAULTNAME;
infile = fopen(infilenm,"r");
if (!infile) {
printf("%s not found or unavailable\n",infilenm);
quit();
}
while (fgets(line, sizeof line, infile)) {
++lineno;
doline(line);
zero_fields(line);
}
fclose(infile);
walkblocks();
printf("Peak heap usage was %ld bytes.\n",peakmem);
printf("Total heap memory allocations was %ld bytes.\n", totaldynmem);
exit(EXIT_SUCCESS);
/*NOTREACHED*/
return 0;
}
static void out_of_memory()
{
printf("heaputil: out of memory at line %ld of %s\n",
lineno, infilenm);
quit();
}
/*
* Sample:
+ 43 6852:0A0A 568 ..\win\tty\wintty.c
- 6852:0A0A 1291 ..\win\tty\wintty.c
* Description:
^ size address line file ^: + => alloc, - => free
*
* Note: In environments other than MSDOS 16 bit segmented ones,
* the address field will just be a single string of digits.
*
* Parsing:
* The operation flag and allocation size are in fixed columns;
* the address starts at a fixed offset and its width is expected
* to be the same for every entry, but that width may vary from
* platform to platform; the line number is a right-justified
* 4-digit number separated from the address by one space and
* followed after a space by the filename; line+file+newline
* are treated as a single unit throughout.
*
* Only the first line obtained from the file will be scanned
* to determine the offsets of the various fields: operation,
* size, address, and fileinfo. Subsequent lines are processed
* by depositing NUL at the appropriate offsets in the string.
* This method seems to be quite fast and avoids complicated
* parsing.
*/
static void doline(line)
char *line;
{
if (!have_template) btempl(line);
if (line[OPERATION] == '+') chain(line);
else if (line[OPERATION] == '-') unchain(line);
else {
printf("%6ld: invalid operation, badly formatted line.\n",
lineno);
printf(" -> %s",line);
if (++errcount > MAXERR) {
printf("Giving up on this file, its not right.\n");
quit();
}
}
}
static void chain(line)
char *line;
{
struct memblock *block;
int i;
terminate_fields(line);
if (blkcache) {
block = blkcache;
blkcache = block->next;
} else {
block = (struct memblock *)malloc(sizeof(struct memblock));
if (!block) out_of_memory();
}
block->sequence = lineno;
block->bsize = atol(&line[BLKSIZE]);
(void)sscanf(&line[ADDRESS], PTR_FMT, &block->address);
Strcpy(block->fileinfo, &line[FILEINFO]);
if (block->address == 0) {
printf("%6ld: allocation of %ld bytes returned null pointer, %s",
block->sequence, block->bsize, block->fileinfo);
return;
}
i = CHAIN_SELECT(block->address);
block->next = firstblock[i];
firstblock[i] = block;
curmem += block->bsize;
if (curmem > peakmem) peakmem = curmem;
totaldynmem += block->bsize;
}
static void unchain(line)
char *line;
{
struct memblock *block;
struct memblock *rmblock = (struct memblock *)0;
PTR_TYP address;
int i;
terminate_fields(line);
(void)sscanf(&line[ADDRESS], PTR_FMT, &address);
i = CHAIN_SELECT(address);
if (address == 0) {
printf("%6ld: attempt to free null pointer, %s",
lineno, &line[FILEINFO]);
return;
} else if (firstblock[i]) {
/* special case, first one so no prev available */
if (address == firstblock[i]->address) {
rmblock = firstblock[i];
firstblock[i] = rmblock->next;
} else {
block = findprev(address);
if (block) {
rmblock = block->next;
block->next = rmblock->next;
}
}
}
if (!rmblock) {
printf("%6ld: attempt to free unallocated block, %s",
lineno, &line[FILEINFO]);
} else {
curmem -= rmblock->bsize;
if (curmem < 0) {
printf("%6ld: impossible, current memory use negative, %s",
lineno, &line[FILEINFO]);
}
rmblock->next = blkcache;
blkcache = rmblock;
}
}
static struct memblock *
findprev(maddress)
PTR_TYP maddress;
{
struct memblock *tmpblk, *nextblk;
tmpblk = firstblock[CHAIN_SELECT(maddress)];
while (tmpblk) {
nextblk = tmpblk->next;
if (nextblk) {
if (maddress == nextblk->address) return tmpblk;
}
tmpblk = nextblk;
}
return (struct memblock *)0;
}
static void walkblocks()
{
struct memblock *tmpblk, *nextblk;
int i, k;
long unfreedmem = 0L;
/* make a sorted list from all the chains; expected to be short */
tmpblk = 0;
do {
k = 0;
for (i = 1; i < SIZE(firstblock); i++)
if (!firstblock[k] || (firstblock[i] &&
firstblock[i]->sequence > firstblock[k]->sequence))
k = i;
nextblk = firstblock[k];
if (nextblk) {
firstblock[k] = nextblk->next;
nextblk->next = tmpblk;
tmpblk = nextblk;
}
} while (nextblk);
while (tmpblk) {
printf("%6ld: not freed: %5ld bytes, %s",
tmpblk->sequence, tmpblk->bsize, tmpblk->fileinfo);
unfreedmem += tmpblk->bsize;
nextblk = tmpblk->next;
free((genericptr_t)tmpblk);
tmpblk = nextblk;
}
printf("Total of %ld bytes not freed before exit.\n", unfreedmem);
/* free memblock cache */
while (blkcache) {
tmpblk = blkcache;
blkcache = tmpblk->next;
free((genericptr_t)tmpblk);
}
}
static void btempl(line)
char *line;
{
char *p;
p = &line[ADDRESS - 1];
while (isspace(*p)) ++p;
while (*p && *p != ' ' && *p != '\n') ++p;
if (*p == ' ') ++p; /* skip first space */
fileinfo_offset = p - line;
++have_template;
}
/*heaputil.c*/