*** empty log message ***
This commit is contained in:
325
util/heaputil.c
Normal file
325
util/heaputil.c
Normal file
@@ -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 <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*/
|
||||
Reference in New Issue
Block a user