From: jwalz Date: Sat, 5 Jan 2002 21:06:00 +0000 (+0000) Subject: *** empty log message *** X-Git-Tag: MOVE2GIT~3624 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=16454d543c594fa49902def8821bd0b25f1ae3cd;p=nethack *** empty log message *** --- diff --git a/util/heaputil.c b/util/heaputil.c new file mode 100644 index 000000000..74a702311 --- /dev/null +++ b/util/heaputil.c @@ -0,0 +1,325 @@ +/* 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 + +#ifdef MICRO +#include +#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*/