home *** CD-ROM | disk | FTP | other *** search
- /*
- * fxmemmon.c -- mmout, mmpause, mmshow, and internal functions.
- *
- * This file contains memory monitoring code. It is compiled by inclusion
- * in fxtra.c if MemMon is defined. When MemMon is undefined, most of the
- * "MMxxxx" entry points are defined as null macros in rt.h.
- */
-
- #include "../h/config.h"
- #include "../h/rt.h"
- #include "rproto.h"
-
-
-
- #ifdef MemMon
- /*
- * Prototypes.
- */
-
- hidden novalue mmcmd Params((word addr, word len, int c));
- hidden novalue mmdec Params((uword n));
- hidden novalue mmforget Params((noargs));
- hidden novalue mmlen Params((word n, int c));
- hidden novalue mmnewline Params((noargs));
- hidden novalue mmrefresh Params((noargs));
- hidden novalue mmsizes Params((int c));
- hidden novalue mmstatic Params((noargs));
- hidden novalue MMOut Params((char *prefix, char *msg));
-
- static FILE *monfile = NULL; /* output file pointer */
- static char *monname = NULL; /* output file name */
-
- static word llen = 0; /* current output line length */
-
- static char typech[MaxType+1]; /* output character for each type */
-
- /* Define size of curlength table, and bias needed to access it. */
- /* Assumes all type codes are printable characters (or space). */
- /* Smaller table is used if not EBCDIC. */
- #if !EBCDIC
- #define CurSize (127 - ' ')
- #define CurBias ' '
- #else /* !EBCDIC */
- #define CurSize 256
- #define CurBias 0
- #endif /* !EBCDIC */
-
- static word curlength[CurSize]; /* current length for each output character */
-
- /* line limit: start a new line when a command goes beyond this column */
- #define LLIM 70
-
- /* mmchar(c): output character c and update the column counter */
- #define mmchar(c) (llen++,putc((c),monfile))
-
- /* mmspace(): output unneeded whitespace whitespace following a command */
- /* define as "mmchar(' ')" for readable files, or as "0" for compact ones */
- #define mmspace() 0
-
- /*
- * mmout(s) - write the given string to the MemMon file.
- */
-
- FncDcl(mmout,1)
- {
- char sbuf[MaxCvtLen];
- int t;
-
- if ((t = defstr(&Arg1, sbuf, &emptystr)) == Error)
- RunErr(0, NULL);
- /*
- * Make sure Arg1 is a C-style string.
- */
- if (t == NoCvt)
- qtos(&Arg1, sbuf);
- MMOut("", StrLoc(Arg1));
- Arg0 = nulldesc;
- Return;
- }
-
- /*
- * mmpause(s) - pause MemMon displaying string s.
- */
-
- FncDcl(mmpause,1)
- {
- char sbuf[MaxCvtLen];
- int t;
-
- if ((t = defstr(&Arg1, sbuf, &emptystr)) == Error)
- RunErr(0, NULL);
- if (StrLen(Arg1) == 0)
- MMOut("; ", "programmed pause");
- else {
- /*
- * Make sure Arg1 is a C-style string.
- */
- if (t == NoCvt)
- qtos(&Arg1, sbuf);
- MMOut("; ", StrLoc(Arg1));
- }
- Arg0 = nulldesc;
- Return;
- }
-
- /*
- * mmshow(x, s) - alter MemMon display of x depending on s.
- */
-
- FncDcl(mmshow,2)
- {
- char sbuf[MaxCvtLen];
-
- /*
- * Default Arg2 to the empty string and make sure it is a C-style string.
- */
- switch (defstr(&Arg2, sbuf, &emptystr)) {
-
- case Cvt: /* Already converted to a C-style string */
- break;
-
- case Defaulted:
- case NoCvt:
- qtos(&Arg2, sbuf);
- break;
-
- case Error:
- RunErr(0, NULL);
- }
-
- MMShow(&Arg1, StrLoc(Arg2));
- Arg0 = nulldesc;
- Return;
- }
-
- /*
- * MMInit(filename) - initialization.
- *
- * Memory monitoring is activated if the environment variable MEMMON is
- * non-null. Its value names the output file; or, under Unix and on the
- * Archimedes, a value beginning with "|" specifies a command to which the
- * output is piped.
- *
- * If MemMon is defined on a system lacking environment variables,
- * monitoring is always activated and output is to the file "memmon.out".
- */
-
- novalue MMInit(filename)
- char *filename;
- {
- int i;
- FILE *f;
- char time_buf[26];
-
- #ifdef EnvVars
- monname = getenv(MEMMON);
- if (monname == NULL || strlen(monname) == 0)
- return;
- #else /* EnvVars */
- monname = "memmon.out";
- #endif /* EnvVars */
-
- #if ARM || UNIX
- if (monname[0] == '|')
- f = popen(monname+1, WriteText);
- else
- #endif /* ARM || UNIX */
-
- f = fopen(monname, WriteText);
-
- if (f == NULL) {
- fprintf(stderr, "MEMMON: cannot open %s\n", monname);
- fflush(stderr);
- exit(ErrorExit);
- }
-
-
- getctime(time_buf);
- fprintf(f, "## Icon MemMon output\n");
- fprintf(f, "#\n");
- fprintf(f, "# program: %s\n", filename);
- fprintf(f, "# date: %s\n", time_buf);
-
- for (i = 0; i <= MaxType; i++)
- typech[i] = '?'; /* initialize with error character */
-
- #ifdef LargeInts
- typech[T_Bignum] = 'i'; /* long integer */
- #endif /* LargeInts */
-
- typech[T_Real] = 'r'; /* real number */
- typech[T_Cset] = 'c'; /* cset */
- typech[T_File] = 'f'; /* file block */
- typech[T_Record] = 'R'; /* record block */
- typech[T_Tvsubs] = 'u'; /* substring trapped variable */
- typech[T_External]= 'E'; /* external block */
-
- typech[T_List] = 'L'; /* list header block */
- typech[T_Lelem] = 'l'; /* list element block */
-
- typech[T_Table] = 'T'; /* table header block */
- typech[T_Telem] = 't'; /* table element block */
- typech[T_Tvtbl] = 'e'; /* table elem trapped variable*/
-
- typech[T_Set] = 'S'; /* set header block */
- typech[T_Selem] = 's'; /* set element block */
-
- typech[T_Slots] = 'h'; /* set/table hash slots */
-
- typech[T_Coexpr] = 'X'; /* co-expression block (static region) */
- typech[T_Refresh] = 'x'; /* co-expression refresh block */
-
- /*
- * codes used elsewhere but not shown here:
- * in the static region: 'A' = alien (malloc block), 'F' = free
- * in the string region: '"' = string
- */
-
- /*
- * Set monfile to indicate that memmon is active. Don't set it earlier
- * than this, or we'll loop trying to trace the garbage collection that
- * creates the buffer space.
- */
- monfile = f;
- mmrefresh(); /* show current state */
- fflush(monfile); /* force it out */
- }
-
- /*
- * MMTerm(part1, part2) - terminate memory monitoring.
- * part1 and part2 are concatentated to form an explanatory message.
- */
-
- novalue MMTerm(part1, part2)
- char *part1, *part2;
- {
- FILE *f;
-
- if (monfile == NULL)
- return;
- mmnewline();
- mmsizes('='); /* make a final check on region sizes */
-
- if (*part1 || *part2) /* if any reason given, write it as comment */
- fprintf(monfile, "# %s%s\n", part1, part2);
-
- f = monfile;
- monfile = NULL; /* so we don't try to show the freeing of the buffer */
-
- #if ARM || UNIX
- if (monname[0] == '|')
- pclose(f);
- else
- #endif /* ARM || UNIX */
- fclose(f);
- }
-
- /*
- * MMStat(a, n, c) - note static block at a, length n, represented by char 'c'.
- * Output values are in basic units (typically words).
- */
- novalue MMStat(a, n, c)
- char *a;
- word n;
- int c;
- {
- #ifndef FixedRegions
- if (monfile == NULL)
- return;
- mmcmd(DiffPtrs(a, statbase) / MMUnits, n / MMUnits, c);
- #endif /* FixedRegions */
- }
-
- /*
- * MMAlc(len, type) - note an allocation at the end of the block region.
- */
-
- novalue MMAlc(len, type)
- word len;
- int type;
- {
- if (monfile == NULL)
- return;
- mmcmd((word)(-1), len / MMUnits, typech[type]);
- }
-
- /*
- * MMStr(len) - note a string allocation at the end of the string region.
- */
-
- novalue MMStr(slen)
- word slen;
- {
- if (monfile == NULL)
- return;
- mmcmd((word)(-1), slen, '"');
- }
-
- /*
- * MMBGC() - begin garbage collection.
- */
-
- novalue MMBGC(region)
- int region;
- {
- if (monfile == NULL)
- return;
- mmsizes('='); /* write current sizes */
- fprintf(monfile, "%d{\n", region); /* indicate start of g.c. */
- fflush(monfile);
- mmforget(); /* clear memory of block sizes */
- }
-
- /*
- * MMEGC() - end garbage collection.
- */
-
- novalue MMEGC()
- {
- if (monfile == NULL)
- return;
- mmnewline();
- fprintf(monfile, "}\n"); /* indicate end of marking */
- mmrefresh(); /* redraw regions after compaction */
- fprintf(monfile, "!\n"); /* indicate end of g.c. */
- fflush(monfile);
- }
-
- /*
- * MMMark(block, type) - mark indicated block during garbage collection.
- */
-
- novalue MMMark(block, type)
- char *block;
- int type;
- {
- if (monfile == NULL)
- return;
- mmcmd(DiffPtrs(block, blkbase) / MMUnits, (word)BlkSize(block) / MMUnits,
- typech[type]);
- }
-
- /*
- * MMSMark - Mark String.
- */
-
- novalue MMSMark(saddr, slen)
- char *saddr;
- word slen;
- {
- if (monfile == NULL)
- return;
- mmcmd(DiffPtrs(saddr, strbase), slen, '"');
- }
-
- /*
- * MMOut(prefix, msg) - write the prefix and message to the MemMon output file.
- */
-
- static novalue MMOut(prefix, msg)
- char *prefix, *msg;
- {
- if (monfile == NULL)
- return;
- mmnewline();
- fprintf(monfile, "%s%s\n", prefix, msg);
- }
-
- /*
- * MMShow(d, s) - redraw block indicated by descriptor d according to flags
- * in s.
- */
-
- novalue MMShow(d, s)
- dptr d;
- char *s;
- {
- char *block;
- uword addr;
- word len;
- char cmd, tch;
-
- if (monfile == NULL)
- return;
- if (Qual(*d)) {
- /*
- * Show a string.
- */
- /*
- if ((uword)StrLoc(*d)<(uword)strbase || (uword)StrLoc(*d)>=(uword)strend)
- */
- if (!InRange(strbase,StrLoc(*d),strend))
- return; /* ignore if outside string region */
- addr = DiffPtrs(StrLoc(*d), strbase);
- len = StrLen(*d);
- cmd = '$';
- tch = '"';
- }
- else if (Type(*d)==T_Coexpr) {
- /*
- * Show a coexpression block, which will be in the static region.
- */
- block = (char *)BlkLoc(*d);
- addr = DiffPtrs(block, statbase) / MMUnits;
- len = BlkSize(block) / MMUnits;
- cmd = 'Y';
- tch = typech[T_Coexpr];
- }
- else if (Pointer(*d)) {
- /*
- * Show something in the block region.
- */
- block = (char *)BlkLoc(*d);
- /*
- if ((uword)block < (uword)blkbase || (uword)block >= (uword)blkfree)
- */
- if (!InRange(blkbase,block,blkfree))
- return; /* ignore if outside block region */
- addr = DiffPtrs(block, blkbase) / MMUnits;
- len = BlkSize(block) / MMUnits;
- cmd = '%';
- tch = typech[Type(*d)];
- }
-
- mmdec(addr); /* address */
- mmchar('+');
- mmlen(len, cmd); /* length, and $ Y or % command */
- if (s && *s)
- mmchar(*s); /* color flag from mmshow call */
- else
- mmchar('r'); /* default color is 'r' (redraw) */
- mmchar(tch); /* block type character */
- if (llen >= LLIM)
- mmnewline();
- else
- mmspace();
- }
-
- /*
- * mmrefresh() - redraw screen, initially or after garbage collection.
- */
-
- static novalue mmrefresh()
- {
- char *p;
- word n;
-
- mmnewline();
- mmsizes('<'); /* signal start of screen refresh */
- mmnewline();
- mmforget(); /* clear memory of past sizes */
- mmstatic(); /* show the static region */
- mmnewline();
- for (p = blkbase; p < blkfree; p += n)
- MMAlc(n = BlkSize(p), (int)BlkType(p));/* block region */
- mmnewline();
- MMStr(DiffPtrs(strfree, strbase)); /* string region */
- mmnewline();
- fprintf(monfile, ">\n"); /* signal end of refresh */
- mmsizes('='); /* confirm region sizes */
- mmforget(); /* clear memory of past sizes */
- }
-
- /*
- * mmstatic() - recap the static region (stack, coexprs, aliens, free)
- * (this function is empty under FixedRegions)
- */
- static novalue mmstatic()
- {
- #ifndef FixedRegions
- HEADER *p;
- char *a;
- int h;
- word n;
-
- for (p = (HEADER *)statbase; (uword)p < (uword)(HEADER *)statfree;
- p += p->s.bsize) {
- a = (char *)(p + 1);
- n = (p->s.bsize - 1) * sizeof(HEADER);
- h = *(int *)a;
- if (h == T_Coexpr || a == (char *)stack)
- MMStat(a, n, 'X'); /* coexpression block */
- else if (h == FREEMAGIC)
- MMStat(a, n, 'F'); /* free block */
- else
- MMStat(a, n, 'A'); /* alien block */
- }
- a = (char *)p;
- if (a < statend)
- MMStat(a, (word)(statend-a), 'F');/* rest of static region is free */
- #endif /* FixedRegions */
- }
-
- /*
- * mmsizes(c) - output current region sizes, with initial character c.
- * If c is '<', the unit size is written ahead of it.
- */
- static novalue mmsizes(c)
- int c;
- {
- mmnewline();
- if (c == '<')
- fprintf(monfile, "%d", MMUnits);
- fprintf(monfile, "%c %lu:%lu/%lu %lu:%lu/%lu %lu:%lu/%lu\n", c,
- /* static region; show as full, actual amount is unknown */
- (unsigned long)statbase,
- (unsigned long)DiffPtrs(statend, statbase),
- (unsigned long)DiffPtrs(statend, statbase),
- /* string region */
- (unsigned long)strbase,
- (unsigned long)DiffPtrs(strfree, strbase),
- (unsigned long)DiffPtrs(strend, strbase),
- /* block region */
- (unsigned long)blkbase,
- (unsigned long)DiffPtrs(blkfree, blkbase),
- (unsigned long)DiffPtrs(blkend, blkbase));
- }
-
- /*
- * mmcmd(addr, len, c) - output a memmon command.
- * If addr is < 0, it is omitted.
- * If len matches the previous value for command c, it is also omitted.
- * If the output fills the line, a following newline is written.
- */
-
- static novalue mmcmd(addr, len, c)
- word addr, len;
- int c;
- {
- if (addr >= 0) {
- mmdec((uword)addr);
- mmchar('+');
- }
- mmlen(len, c);
- if (llen >= LLIM)
- mmnewline();
- else
- mmspace();
- }
-
- /*
- * mmlen(n, c) - output length n with character c.
- * Omit the length if it matches the previous value for c.
- */
- static novalue mmlen(n, c)
- word n;
- int c;
- {
- if (n != curlength[c-CurBias])
- mmdec((uword)(curlength[c-CurBias] = n));
- mmchar(c);
- }
-
- /*
- * mmdec(n) - output a decimal value, updating the line length.
- */
- static novalue mmdec (n)
- uword n;
- {
- if (n > 9)
- mmdec(n / 10);
- n %= 10;
- mmchar('0'+(int)n);
- }
-
- /*
- * mmnewline() - output a newline and reset the line length.
- */
- static novalue mmnewline()
- {
- if (llen > 0) {
- putc('\n', monfile);
- llen = 0;
- }
- }
-
- /*
- * mmforget() - clear the history of remembered lengths.
- */
- static novalue mmforget()
- {
- int c;
-
- for (c = 0; c < CurSize; c++)
- curlength[c] = -1;
- }
- #else /* MemMon */
- static char x; /* avoid empty module */
- #endif /* MemMon */
-