From: jtk@netcom.com (Jane Valencia)
Subject: [386BSD] crash 1/2
Organization: Netcom - Online Communication Services  (408 241-9760 guest) 
Date: Sat, 10 Oct 1992 21:13:41 GMT

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	Makefile
#	TODO
#	crash.1
#	crash.c
#	expr.y
#	lex.c
#	malloc.c
#	mem.c
#	nlist.c
#	node.c
#	proc.c
#	trace.c
#	vers.h
#
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
XOBJS= crash.o malloc.o nlist.o mem.o dump.o proc.o disasm.o expr.o lex.o \
X	trace.o node.o
XCFLAGS= -g
X
Xcrash: $(OBJS)
X	$(CC) $(CFLAGS) -o crash $(OBJS)
X
Xlex.c: expr.c
X
Xexpr.c: expr.y
X	yacc -d expr.y
X	mv y.tab.c expr.c
X	mv y.tab.h expr.h
X
Xclean:
X	rm -f *.o expr.c expr.h
X
Xclobber: clean
X	rm -f crash
END-of-Makefile
echo x - TODO
sed 's/^X//' >TODO << 'END-of-TODO'
Xx Understand at least simple expressions
Xx Stack backtrace
Xx Local symbols
XStructure offsets?  From -g?  Or at least include the ones
X	for the common structures?
END-of-TODO
echo x - crash.1
sed 's/^X//' >crash.1 << 'END-of-crash.1'
X.Dd October 10, 1992
X.Dt CRASH 1
X.Os BSD 4
X.Sh NAME
X.Nm crash
X.Nd interactive crash dump analyzer
X.Sh SYNOPSIS
X.Nm crash
X.Op Ar aout
X.Op Ar core
X.Sh DESCRIPTION
X.Nm
Xis an interactive program for looking at crash dumps
Xor live systems.  By default, it opens
X.Pa /386bsd
Xand
X.Pa /dev/mem
Xand examines the currently running system.  The rest of
Xthis man page describes the various commands available
Xin
X.Nm crash.
XOnly enough characters to uniquely identify a command
Xneed be typed.
X.Pp
X.Nm =
X.Ar expr
X.Pp
XEvaluates
X.Ar expr
Xand prints the value in the following formats:
Xsymbolic, hex, unsigned decimal, signed decimal, and octal.
X.Pp
X.Nm di
X.Op Ar addr
X.Op Ar count
X.Pp
XDumps memory at the given virtual address, in instruction
Xformat.  If
X.Ar addr
Xor
X.Ar count
Xare not provided, the last value used will be used again for
X.Ar count,
Xand
X.Ar addr
Xwill be the next address after the last address examined.
X.Pp
X.Nm dp
X.Op Ar format
X.Op Ar addr
X.Op Ar count
X.Pp
XDumps memory given physical addresses.  Arguments are the
Xsame as for
X.Nm di,
Xexcept that
X.Ar format
Xis a single character specifying a format.
XCurrently,
X.Nm crash
Xunderstands 'X' for hex and 's' for "string".
XThis command is useful for looking at memory locations
Xgleaned from the physical address field of a PTE, and
Xother occasions where you wish to side-step the virtual
Xto physical translation step.
X.Pp
X.Nm dv
X.Op Ar format
X.Op Ar addr
X.Op Ar count
X.Pp
XSame as
X.Nm dp,
Xexcept that all addresses are treated as kernel-virtual.
X.Pp
X.Nm inode
X.Ar addr
X.Pp
XDumps the fields of an in-core inode, given the virtual address of
Xan inode.
X.Pp
X.Nm kmem
X.Pp
XDumps out the statistics kept by the
X.I kern_malloc
Xkernel memory allocator.
X.Pp
X.Nm proc
X.Op Ar pid
X.Pp
XWithout arguments, prints out a simple process table.
XGiven a
X.Ar pid
Xas an argument, dumps out the fields associated with
Xthat process structure.
X.Pp
X.Nm quit
X.Pp
XQuits the program.
X.Pp
X.Nm set
X.Ar name
X.Ar value
X.Pp
XSets the named symbol to the given value.  Useful when you
Xkeep looking at some big, hairy address and you're tired
Xof typing it each time.  After being set, the new symbol
Xcan be used like any other in expressions used by other
Xcommands.
X.Pp
X.Nm trace
X.Ar pid
X.Pp
XProvides a jkernel stack backtrace of the named process.
X.Pp
X.Nm vnode
X.Ar addr
X.Pp
XMuch like the
X.Nm inode
Xcommand, except that an in-core
X.B vnode
Xis dumped out.
X.Pp
X.Sh SEE ALSO
X.Xr a.out 5 ,
X.Xr stab 5
X.Sh HISTORY
XA
X.Nm crash
Xcommand appeared in 4.2 BSD.
END-of-crash.1
echo x - crash.c
sed 's/^X//' >crash.c << 'END-of-crash.c'
X/*
X * crash.c
X *	Main routine for crash program
X */
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include <setjmp.h>
X#include <string.h>
X#include "vers.h"
X
Xextern void dump_malloc(), dump_phys(), dump_virt(), dump_proc(),
X	dump_instr(), trace(), dump_inode(), dump_vnode();
Xstatic void quit(), calc(), set();
X
Xchar *aout = "/386bsd",
X	*core = "/dev/mem";
Xint aoutf, coref;
Xint live = 0;		/* Looking at running system? */
Xjmp_buf errjmp;		/* For aborting on error */
X
X/*
X * Table of commands
X */
Xtypedef void (*voidfun)();
Xstruct {
X	char *c_name;	/* Name of command */
X	voidfun c_fn;	/* Function to process the command */
X} cmdtab[] = {
X	"=", calc,
X	"btrace", trace,
X	"calc", calc,
X	"di", dump_instr,
X	"dp", dump_phys,
X	"dv", dump_virt,
X	"inode", dump_inode,
X	"kmem", dump_malloc,
X	"proc", dump_proc,
X	"quit", quit,
X	"set", set,
X	"trace", trace,
X	"vnode", dump_vnode,
X	0, 0
X};
X
X/*
X * quit()
X *	Bye bye
X */
Xstatic void
Xquit()
X{
X	exit(0);
X}
X
X/*
X * calc()
X *	Print out value in multiple formats
X */
Xstatic void
Xcalc(str)
X	char *str;
X{
X	int x;
X	extern int get_num();
X
X	x = get_num(str);
X	printf("%-10s 0x%x %u %d 0%o\n",
X		symloc(x), x, x, x, x);
X}
X
X/*
X * do_cmd()
X *	Given command string, look up and invoke handler
X */
Xstatic void
Xdo_cmd(str)
X	char *str;
X{
X	int x, len, matches = 0, match;
X	char *p;
X
X	p = strchr(str, ' ');
X	if (p)
X		len = p-str;
X	else
X		len = strlen(str);
X
X	for (x = 0; cmdtab[x].c_name; ++x) {
X		if (!strncmp(cmdtab[x].c_name, str, len)) {
X			++matches;
X			match = x;
X		}
X	}
X	if (matches == 0) {
X		printf("No such command\n");
X		return;
X	}
X	if (matches > 1) {
X		printf("Ambiguous\n");
X		return;
X	}
X	(*cmdtab[match].c_fn)(p ? p+1 : p);
X}
X
Xmain(argc, argv)
X	int argc;
X	char **argv;
X{
X	struct stat st;
X	char cmd[128], lastcmd[16];
X
X	printf("crash version %s\n", vers);
X	/*
X	 * Open the various files
X	 */
X	lastcmd[0] = '\0';
X	if (argc > 1)
X		aout = argv[1];
X	if (argc > 2)
X		core = argv[2];
X	if ((aoutf = open(aout, 0)) == NULL) {
X		perror(aout);
X		exit(1);
X	}
X	if ((coref = open(core, 0)) == NULL) {
X		perror(core);
X		exit(1);
X	}
X
X	/*
X	 * Is it live, or is it...
X	 */
X 	if (fstat(coref, &st) >= 0) {
X 		if ((st.st_mode & S_IFMT) == S_IFCHR) {
X 			printf("Examining live system...\n");
X 			live = 1;
X 		}
X 	}
X
X 	/*
X 	 * Top-level error catcher
X 	 */
X 	if (setjmp(errjmp)) {
X 		extern void kernel_pde();
X
X 		printf("Reset to command mode\n");
X 		kernel_pde();
X 	}
X
X 	/*
X 	 * Command loop
X 	 */
X 	for (;;) {
X#ifndef READLINE
X		printf(">");
X		if (gets(cmd) == 0)
X			break;
X#else
X		XXX
X#endif
X		/*
X		 * Keep last command, insert it if they just hit return
X		 */
X		if (!cmd[0] && lastcmd[0]) {
X			strcpy(cmd, lastcmd);
X		} else {
X			char *p, *q, c;
X
X			p = cmd;	/* Copy up to first space */
X			q = lastcmd;
X			while (c = *p++) {
X				if (c == ' ')
X					break;
X				*q++ = c;
X			}
X			*q = '\0';
X		}
X		do_cmd(cmd);
X	}
X	return(0);
X}
X
X/*
X * yyerror()
X *	Report syntax error and reset
X */
Xyyerror()
X{
X	fprintf(stderr, "Syntax error in expression\n");
X	longjmp(errjmp, 1);
X}
X
X/*
X * set()
X *	Set a symbol to a value
X */
Xstatic void
Xset(s)
X	char *s;
X{
X	off_t o;
X	char *n = s;
X	extern void setsym();
X
X	s = strchr(s, ' ');
X	if (!s) {
X		printf("Usage: set <name> <value>\n");
X		longjmp(errjmp, 1);
X	}
X	*s = '\0'; ++s;
X	setsym(n, get_num(s));
X}
END-of-crash.c
echo x - expr.y
sed 's/^X//' >expr.y << 'END-of-expr.y'
X%left	'*' '/'
X%left	'+' '-'
X%right	MINUS
X%right LE GE EQ NE
X%left '&'
X%token ID INT
X%{
X#ifdef YYDEBUG
Xint yydebug = 1;
X#endif
X%}
X
X%%
X
Xexpr	:	expr '+' expr
X			{$$ = $1 + $3;}
X	|	expr '-' expr
X			{$$ = $1 - $3;}
X	|	expr '/' expr
X			{$$ = $1 / $3;}
X	|	expr '*' expr
X			{$$ = $1 * $3;}
X	|	expr LE expr
X			{$$ = $1 <= $3;}
X	|	expr GE expr
X			{$$ = $1 >= $3;}
X	|	expr EQ expr
X			{$$ = $1 == $3;}
X	|	expr NE expr
X			{$$ = $1 != $3;}
X	|	expr '&' expr
X			{$$ = $1 & $3;}
X	|	'~' expr %prec MINUS
X			{$$ = ~$2;}
X	|	'-' expr %prec MINUS
X			{$$ = 0-$2;}
X	|	'(' expr ')'
X			{$$ = $2;}
X	|	ID
X	|	INT
X	;
X%%
END-of-expr.y
echo x - lex.c
sed 's/^X//' >lex.c << 'END-of-lex.c'
X/*
X * A standard lexical analyzer
X */
X#include <stdio.h>
X#include <ctype.h>
X#include "expr.h"
X
Xchar *expr_line, *expr_pos;
X
Xstatic char buf[80];
Xstatic int donum();
Xextern int yylval;
X
X /*
X  * getchar() function for lexical analyzer.
X  */
Xstatic inline
Xnextc()
X{
X	register int c;
X
X	/*
X	 * Pop up a level of indirection on EOF
X	 */
X	c = *expr_pos;
X	if (c == '\0') {
X		return(EOF);
X	}
X	expr_pos += 1;
X	return (c);
X}
X
X/*
X * Push back a character
X */
Xstatic void inline
Xunget_c(c)
X	int c;
X{
X	if ((expr_pos <= expr_line) || (c == EOF) || !c)
X		return;
X	expr_pos -= 1;
X	*expr_pos = c;
X}
X
X/*
X * Skip leading white space in current input stream
X */
Xstatic void
Xskipwhite()
X{
X	register c;
X
X	/*
X	 * Skip leading blank space
X	 */
X	while ((c = nextc()) != EOF)
X		if (!isspace(c))
X			break;
X	unget_c(c);
X}
X
X/*
X * Lexical analyzer for YACC
X */
Xyylex()
X{
X	register char *p = buf;
X	register c, c1;
X
X	/*
X	 * Skip over white space
X	 */
Xagain:
X	skipwhite();
X	c = nextc();
X
X	/*
X	 * Return EOF
X	 */
X	if (c == EOF)
X		return (c);
X
X	/*
X	 * An "identifier"?
X	 */
X	if (isalpha(c)) {
X		/*
X		 * Assemble a "word" out of the input stream, symbol table it
X		 */
X		*p++ = c;
X		for (;;) {
X			c = nextc();
X			if (!isalnum(c) && (c != '_'))
X				break;
X			*p++ = c;
X		}
X		unget_c(c);
X		*p = '\0';
X		yylval = symval(buf);
X		return (ID);
X	}
X
X	/*
X	 * For numbers, call our number routine.
X	 */
X	if (isdigit(c))
X		return (donum(c));
X
X	/*
X	 * For certain C operators, need to look at following char to
X	 *	assemble relationals.  Otherwise, just return the char.
X	 */
X	yylval = c;
X	switch (c) {
X	case '<':
X		if ((c1 = nextc()) == '=')
X			return (LE);
X		unget_c(c1);
X		return (c);
X	case '>':
X		if ((c1 = nextc()) == '=')
X			return (GE);
X		unget_c(c1);
X		return (c);
X	case '~':
X		if ((c1 = nextc()) == '=')
X			return (NE);
X		unget_c(c1);
X		return (c);
X	default:
X		return (c);
X	}
X}
X
X/*
X * donum()
X *	Handle parsing of a number
X */
Xstatic int
Xdonum(startc)
X	char startc;
X{
X	register char c, *p = buf;
X
X	/*
X	 * Hex numbers
X	 */
X	if (startc == '0') {
X		c = nextc();
X		if (c == 'x') {
X			c = nextc();
X			while (isxdigit(c)) {
X				*p++ = c;
X				c = nextc();
X			}
X			unget_c(c);
X			*p = '\0';
X			sscanf(buf, "%x", &yylval);
X			return (INT);
X		}
X		unget_c(c);
X	}
X
X	/*
X	 * Otherwise assume decimal
X	 */
X	*p++ = startc;
X	for (;;) {
X		c = nextc();
X		if (isdigit(c)) {
X			*p++ = c;
X			continue;
X		}
X		unget_c(c);
X		break;
X	}
X	*p = '\0';
X	sscanf(buf, "%d", &yylval);
X	return (INT);
X}
X
END-of-lex.c
echo x - malloc.c
sed 's/^X//' >malloc.c << 'END-of-malloc.c'
X/*
X * malloc.c
X *	Dump the kernel kern_malloc kmem stats
X */
X#include <sys/types.h>
X#include <sys/malloc.h>
X
X#define K (1024)
X
Xextern void readmem();
X
Xstruct kmemstats kmemstats[M_LAST];
Xchar *kmemnames[] = INITKMEMNAMES;
X
X/*
X * entry()
X *	Dump a kmemstats entry
X */
Xstatic void
Xentry(idx, k)
X	int idx;
X	struct kmemstats *k;
X{
X	if (k->ks_calls == 0)
X		return;
X	printf("%-10s %6d %6d %3dk %6d %6d %3dk %3dk\n",
X		kmemnames[idx],
X		k->ks_inuse, k->ks_calls, k->ks_memuse / K,
X		k->ks_limblocks, k->ks_mapblocks,
X		k->ks_maxused / K, k->ks_limit / K);
X}
X
Xvoid
Xdump_malloc(cmd)
X	char *cmd;
X{
X	int x;
X
X	if (!cmd || cmd[0] == 'k') {
X		printf("%-10s %6s %6s %4s %6s %6s %4s %4sk\n",
X			"Type", "Use", "Call", "Mem", "Lim", "Map", "Max", "kLim");
X		readmem("kmemstats", kmemstats, sizeof(kmemstats), 0);
X		for (x = 0; x < M_LAST; ++x)
X			entry(x, &kmemstats[x]);
X	} else {
X	}
X}
END-of-malloc.c
echo x - mem.c
sed 's/^X//' >mem.c << 'END-of-mem.c'
X/*
X * mem.c
X *	Routines for examining memory (or an image thereof)
X */
X#include <sys/types.h>
X#include <vm/vm_param.h>
X#include <vm/lock.h>		/* XXX for simple_lock_t dec'l */
X#include <vm/vm_statistics.h>	/* XXX for pm_statistics */
X#include <machine/pmap.h>
X#include <machine/param.h>
X#include <stdio.h>
X#include <setjmp.h>
X
X#define NPTPG (NBPG/sizeof(struct pte))
X#define VOFF (NBPG-1)
X
Xextern FILE *coref;
Xextern int live;	/* XXX we should cache when not live */
Xextern jmp_buf errjmp;
X
Xstatic off_t pde_off,	/* Root of page table */
X	kern_pde;	/*  ...master snapshot */
X
X/*
X * vtop()
X *	Convert kernel virtual into physical
X */
Xstatic off_t
Xvtop(addr)
X	off_t addr;
X{
X	struct pde pde;
X	struct pte pte;
X	off_t o;
X	static int init = 0;
X
X	/*
X	 * One-time calculation of location of kernel PDEs
X	 */
X	if (!init) {
X		o = symval("kernel_pmap");
X		lseek(coref, o-KERNBASE, 0);
X		read(coref, &o, sizeof(o));	/* VA of kern pmap */
X		lseek(coref, o-KERNBASE, 0);
X		read(coref, &o, sizeof(o));	/* VA PDEs */
X		kern_pde = pde_off = o - KERNBASE;
X		init = 1;
X	}
X
X	/*
X	 * Pick PDE for given vaddr
X	 */
X 	pde.pd_v = 0;
X 	lseek(coref, pde_off + pdei(addr)*sizeof(struct pde), 0);
X 	read(coref, &pde, sizeof(pde));
X 	if (!pde.pd_v) {
X 		fprintf(stderr, "Error: PDE invalid on 0x%x\n", addr);
X 		longjmp(errjmp, 1);
X	}
X
X 	/*
X 	 * Get just page number part, add in offset from vaddr,
X 	 * get appopriate PTE
X 	 */
X 	pte.pg_v = 0;
X 	lseek(coref, pde.pd_pfnum*NBPG + ptei(addr)*sizeof(struct pte), 0);
X 	read(coref, &pte, sizeof(pte));
X 	if (!pte.pg_v) {
X 		fprintf(stderr, "Error: PTE invalid on 0x%x\n", addr);
X 		longjmp(errjmp, 1);
X	}
X 	return(pte.pg_pfnum*NBPG + (addr & VOFF));
X}
X
X/*
X * user_pde()
X *	Override kernel PDE with given root
X *
X * Used to look at addresses within a particular process context
X * Kernel PDE is restored by kernel_pde(), either explicityly or
X * via an error reset.
X */
Xvoid
Xuser_pde(val)
X	off_t val;
X{
X	pde_off = val;
X}
X
X/*
X * kern_pde()
X *	Restore to main kernel PDE
X */
Xvoid
Xkernel_pde()
X{
X	pde_off = kern_pde;
X}
X
X/*
X * readloc()
X *	Like readmem(), but location given explicitly
X */
Xvoid
Xreadloc(off, mem, size, phys)
X	off_t off;
X	void *mem;
X	int size, phys;
X{
X	off_t a;
X	int len;
X
X	/*
X	 * Physical needs no mapping, and the read is contiguous
X	 */
X 	if (phys) {
X 		lseek(coref, off, 0);
X 		read(coref, mem, size);
X 		return;
X 	}
X
X 	/*
X 	 * For virtual, we must loop, using vtop() as we enter
X 	 * each page.
X 	 */
X	for (a = off; a < off+size; a += len) {
X 		int o;
X
X 		len = NBPG - (a & VOFF);
X 		if (len > ((off+size)-a))
X 			len = (off+size)-a;
X 		o = vtop(a);
X 		lseek(coref, o, 0);
X 		read(coref, mem, len);
X 		mem += len;
X	}
X}
X
X/*
X * readmem()
X *	Read a block of memory at the given symbol's location
X *
X * On error we complain and longjmp() out to our error handler.
X */
Xvoid
Xreadmem(name, mem, size, phys)
X	char *name;
X	void *mem;
X	int size, phys;
X{
X	off_t off;
X
X	/*
X	 * Look up symbol
X	 */
X	off = symval(name);
X	readloc(off, mem, size, phys);
X}
X
END-of-mem.c
echo x - nlist.c
sed 's/^X//' >nlist.c << 'END-of-nlist.c'
X#include <a.out.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <setjmp.h>
X
Xstatic void init_nlist();
Xextern jmp_buf errjmp;
X
X#define MAXNAME 33	/* Max chars in name+1 */
X#define HASHSZ (253)	/* Size of hash table */
X
X/*
X * Actual hash table
X */
Xstruct hashent {
X	char h_name[MAXNAME];
X	off_t h_val;
X	struct hashent *h_next;
X} *hashtab[HASHSZ];
X
X/*
X * hashval()
X *	Hash a string into a value from 0..HASHSZ-1
X */
Xstatic
Xhashval(str)
X	char *str;
X{
X	unsigned int x = 0;
X
X	while (*str)
X		x ^= (*str++) & 0xFF;
X	return(x % HASHSZ);
X}
X
X/*
X * addhash()
X *	Add a symbol/value pair to the hash table
X */
Xaddhash(name, val)
X	char *name;
X	off_t val;
X{
X	int hval = hashval(name);
X	struct hashent *h, **hp;
X
X	h = malloc(sizeof(struct hashent));
X	if (!h) {
X		perror("addhash");
X		exit(1);
X	}
X	strcpy(h->h_name, name);
X	h->h_val = val;
X	h->h_next = hashtab[hval];
X	hashtab[hval] = h;
X}
X
X/*
X * find_hashent()
X *	Internal routine to look up hash entries
X *
X * "reterr" tells if we want to return an error indication of NULL
X * when we can't find the entry.  Otherwise we longjmp() to the global
X * error handler.
X */
Xstatic struct hashent *
Xfind_hashent(name, reterr)
X	char *name;
X	int reterr;
X{
X	static int inited = 0;
X	struct hashent *h;
X
X	/*
X	 * Read in all the symbols first time only
X	 */
X	if (!inited) {
X		extern char *aout;
X
X		init_nlist(aout);
X		inited = 1;
X	}
X
X	/*
X	 * Scan all entries associated with the hash value for
X	 * the symbol.
X	 */
X 	h = hashtab[hashval(name)];
X 	while (h) {
X 		if (!strcmp(h->h_name, name))
X 			return(h);
X		h = h->h_next;
X 	}
X	if (!reterr) {
X		fprintf(stderr, "Unknown symbol: %s\n", name);
X		longjmp(errjmp, 1);
X	}
X	return(0);
X}
X
X/*
X * symval()
X *	Given name, do hashed lookup to value for symbol
X *
X * longjmp()'s on failure
X */
Xoff_t
Xsymval(name)
X	char *name;
X{
X	return (find_hashent(name, 0)->h_val);
X}
X
X/*
X * setsym()
X *	Set named symbol to given value
X *
X * Will warn if overwriting an existing symbol
X */
Xvoid
Xsetsym(name, val)
X	char *name;
X	off_t val;
X{
X	struct hashent *h;
X
X	h = find_hashent(name, 1);
X	if (h) {
X		printf("Warning: previous value for %s was %x\n",
X			name, h->h_val);
X		h->h_val = val;
X	} else {
X		addhash(name, val);
X	}
X}
X
X/*
X * nameval()
X *	Give a symbol for the named value
X */
Xchar *
Xnameval(loc)
X	off_t loc;
X{
X	int x;
X	struct hashent *h;
X
X	for (x = 0; x < HASHSZ; ++x) {
X		for (h = hashtab[x]; h; h = h->h_next) {
X			if (h->h_val == loc)
X				return(h->h_name);
X		}
X	}
X	return(0);
X}
X
X/*
X * symloc()
X *	Return pointer to string describing the given location
X */
Xchar *
Xsymloc(loc)
X	off_t loc;
X{
X	int x, closest = 99999999;
X	struct hashent *h, *hclosest;
X	static char buf[48];
X
X	for (x = 0; x < HASHSZ; ++x) {
X		for (h = hashtab[x]; h; h = h->h_next) {
X			if (h->h_val == loc)
X				return(h->h_name);
X			if (h->h_val > loc)
X				continue;
X			/* Record nearest miss */
X			if ((loc - h->h_val) < closest) {
X				closest = loc - h->h_val;
X				hclosest = h;
X			}
X		}
X	}
X	sprintf(buf, "%s+%x", hclosest->h_name, closest);
X	return(buf);
X}
X
Xtypedef struct nlist NLIST;
X#define	_strx	n_un.n_strx
X#define	_name	n_un.n_name
X#define	ISVALID(p)	(p->_name && p->_name[0])
X
X/*
X * init_nlist()
X *	Read all symbols from a.out, store in hash table
X */
Xstatic void
Xinit_nlist(name)
X	const char *name;
X{
X	register NLIST *p, *s;
X	struct exec ebuf;
X	FILE *fstr = 0, *fsym = 0;
X	NLIST nbuf;
X	off_t strings_offset, symbol_offset, symbol_size, lseek();
X	int len;
X	char sbuf[256];
X
X	if (!(fsym = fopen(name, "r")))
X		return;
X	if (fread((char *)&ebuf, sizeof(struct exec), 1, fsym) != 1 ||
X	    N_BADMAG(ebuf))
X		goto done;
X
X	symbol_offset = N_SYMOFF(ebuf);
X	symbol_size = ebuf.a_syms;
X	strings_offset = symbol_offset + symbol_size;
X	if (fseek(fsym, symbol_offset, SEEK_SET))
X		goto done;
X
X	if (!(fstr = fopen(name, "r")))
X		goto done;
X
X	for (s = &nbuf; symbol_size; symbol_size -= sizeof(NLIST)) {
X		if (fread((char *)s, sizeof(NLIST), 1, fsym) != 1)
X			break;;
X		if (!s->_strx || s->n_type&N_STAB)
X			continue;
X		if (fseek(fstr, strings_offset + s->_strx, SEEK_SET))
X			break;;
X		(void)fread(sbuf, sizeof(sbuf[0]), 32, fstr);
X		addhash((sbuf[0] == '_') ? sbuf+1 : sbuf, s->n_value);
X	}
Xdone:
X	if (fstr)
X		fclose(fstr);
X	if (fsym)
X		fclose(fsym);
X}
END-of-nlist.c
echo x - node.c
sed 's/^X//' >node.c << 'END-of-node.c'
X/*
X * node.c
X *	Routines to dump vnodes and inodes
X */
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/time.h>
X#include <sys/proc.h>
X#include <sys/uio.h>
X#include <sys/vnode.h>
X#include <ufs/quota.h>
X#include <ufs/inode.h>
X
X/*
X * dump_inode()
X *	Dump out fields of a UFS inode
X */
Xvoid
Xdump_inode(arg)
X	char *arg;
X{
X	struct inode inod, *i = &inod;
X	struct dinode *d = &i->i_din;
X	int x;
X
X	readloc(get_num(arg), i, sizeof(inod), 0);
X	printf("mode %o nlink %d uid %d gid %d\n", d->di_mode,
X		d->di_nlink, d->di_uid, d->di_gid);
X	printf("size %d blocks %d\n", d->di_qsize, d->di_blocks);
X	printf("i_chain %x/%x vnode %x devvp %x\n",
X		i->i_chain[0], i->i_chain[1], i->i_vnode,
X		i->i_devvp);
X	printf("Flags:");
X#define D(n) if (i->i_flags & n) printf(" %s", #n);
X	D(ILOCKED); D(IWANT); D(IRENAME); D(IUPD); D(IACC);
X	D(ICHG); D(IMOD); D(ISHLOCK); D(IEXLOCK); D(ILWAIT);
X#undef D
X	printf("\ndev %x inum %d fs %x\n", i->i_dev, i->i_number, i->i_fs);
X	printf(" atime %s mtime %s ctime %s",
X		ctime(&d->di_atime), ctime(&d->di_mtime),
X		ctime(&d->di_ctime));
X	printf("db:");
X	for (x = 0; x < NDADDR; ++x)
X		printf(" %x", d->di_db[x]);
X	printf("\nidb:");
X	for (x = 0; x < NIADDR; ++x)
X		printf(" %x", d->di_ib[x]);
X	printf("\n");
X}
X
X/*
X * dump_vnode()
X *	Dump fields of a "virtual" file node
X */
X/*
X * vnode types. VNON means no type.
X */
Xvoid
Xdump_vnode(s)
X	char *s;
X{
X	caddr_t vaddr;
X	struct vnode vn, *v = &vn;
X	static char *vtypes[] = { "VNON", "VREG", "VDIR", "VBLK", "VCHR",
X		"VLNK", "VSOCK", "VFIFO", "VBAD" };
X	static char *vtags[] = { "NON", "UFS", "NFS", "MFS" };
X
X	vaddr = (caddr_t)get_num(s);
X	readloc(vaddr, v, sizeof(vn), 0);
X	printf("Type %s tag %s data %x\n", vtypes[v->v_type],
X		vtags[v->v_tag],
X		vaddr+((char *)(v->v_data) - (char *)v));
X	printf("Flags:");
X#define D(n) if (v->v_flag & n) printf(" %s", #n);
X	D(VROOT); D(VTEXT); D(VSYSTEM); D(VXLOCK);
X	D(VXWANT); D(VBWAIT); D(VALIASED);
X#undef D
X	printf("\nuse %d write %d hold %ld lastr %x\n",
X		v->v_usecount, v->v_writecount, v->v_holdcnt, v->v_lastr);
X	printf("id %lx mount %x vops %x free %x/%x\n",
X		v->v_id, v->v_mount, v->v_op, v->v_freef, v->v_freeb);
X	printf("mountf %x mountb %x clean %x dirty %x\n",
X		v->v_mountf, v->v_mountb, v->v_cleanblkhd,
X		v->v_dirtyblkhd);
X	printf("numout %ld v_un %x\n", v->v_numoutput, v->v_un.vu_socket);
X}
END-of-node.c
echo x - proc.c
sed 's/^X//' >proc.c << 'END-of-proc.c'
X/*
X * proc.c
X *	Routines to dump out processes
X */
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/time.h>
X#include <sys/proc.h>
X#include <unistd.h>
X#include <setjmp.h>
X
Xextern char *nameval();
Xextern jmp_buf errjmp;
X
X/*
X * statename()
X *	Give printable string for p_stat field
X */
Xstatic char *
Xstatename(s)
X	int s;
X{
X	switch (s) {
X	case SSLEEP: return("SSLEEP");
X	case SWAIT: return("SWAIT");
X	case SRUN: return("SRUN");
X	case SIDL: return("SIDL");
X	case SZOMB: return("SZOMB");
X	case SSTOP: return("SSTOP");
X	default: return("???");
X	}
X}
X
X/*
X * pfind()
X *	Given pid, fill in a proc structure
X *
X * Returns 0 on success, 1 on failure
X */
Xpfind(pid, procp)
X	int pid;
X	struct proc *procp;
X{
X	off_t o;
X
X	readmem("allproc", &o, sizeof(o), 0);
X	while (o) {
X		readloc(o, procp, sizeof(struct proc), 0);
X		if (procp->p_pid == pid)
X			return(0);
X		o = (off_t)procp->p_nxt;
X	}
X	return(1);
X}
X
X/*
X * paddr()
X *	Return address of U area given PID
X */
Xvoid *
Xpaddr(pid)
X	int pid;
X{
X	struct proc p;
X
X	if (pfind(pid, &p)) {
X		printf("No such pid: %d\n", pid);
X		longjmp(errjmp, 1);
X	}
X	if (!(p.p_flag & SLOAD)) {
X		printf("Process %d is swapped\n", pid);
X		longjmp(errjmp, 1);
X	}
X	return((void *)p.p_addr);
X}
X
X/*
X * dump_proc()
X *	Dump a particular process, or all of them
X */
Xvoid
Xdump_proc(arg)
X	char *arg;
X{
X	off_t o;
X	struct proc p;
X	struct pcred pc;
X	char locname[9];
X	int pid, f;
X
X	/*
X	 * No arguments--give abbreviated list of processes
X	 */
X	if (!arg || !arg[0]) {
X		printf("%4s %3s %3s %6s %8s %s\n",
X			"PID", "UID", "GID", "STAT", "WCHAN", "CMD");
X		readmem("allproc", &o, sizeof(o), 0);
X		while (o) {
X			readloc(o, &p, sizeof(p), 0);
X			o = (off_t)p.p_nxt;
X			readloc(p.p_cred, &pc, sizeof(pc), 0);
X			if (p.p_wchan) {
X				char *sym;
X
X				sym = nameval((off_t)p.p_wchan);
X				if (sym) {
X					strncpy(locname, sym, 8);
X					locname[8] = '\0';
X				} else {
X					sprintf(locname, "%8x", p.p_wchan);
X				}
X			} else {
X				locname[0] = '\0';
X			}
X			printf("%3d: %3d %3d %6s %-8s %s\n",
X				p.p_pid,
X				pc.p_ruid, pc.p_rgid,
X				statename(p.p_stat),
X				locname, p.p_comm);
X		}
X		return;
X	}
X
X	/*
X	 * Otherwise, get PID, hunt down proc slot, and dump out
X	 * a much fuller display.
X	 */
X	if (sscanf(arg, "%d", &pid) != 1) {
X		printf("Invalid PID: %s\n", arg);
X		return;
X	}
X	if (pfind(pid, &p)) {
X		printf("No such PID: %d\n", pid);
X		return;
X	}
X	readloc(p.p_cred, &pc, sizeof(pc), 0);
X	printf("Process pid %d state %s '%s'\n",
X		p.p_pid, statename(p.p_stat), p.p_comm);
X	printf(" flags:"); f = p.p_flag;
X#define FLAG(v, s) if (f & v) { printf(" %s", s); }
X	FLAG(SLOAD, "LOAD"); FLAG(SSYS, "SYS"); FLAG(SSINTR, "SINTR");
X	FLAG(SCTTY, "CTTY"); FLAG(SPPWAIT, "PPWAIT"); FLAG(SEXEC, "EXEC");
X	FLAG(STIMO, "TIMO"); FLAG(SSEL, "SEL"); FLAG(SWEXIT, "WEXIT");
X	FLAG(SNOCLDSTOP, "NOCLDSTOP"); FLAG(SLOCK, "LOCK");
X	FLAG(SKEEP, "KEEP"); FLAG(SPHYSIO, "PHYSIO"); FLAG(STRC, "TRC");
X	FLAG(SWTED, "WTED"); FLAG(SADVLCK, "ADVLCK");
X	FLAG(SOWEUPC, "OWEUPC"); FLAG(SPAGE, "PAGE");
X	printf("\n");
X	printf(" cpu %d cpticks %d pctcpu %d wchan 0x%x\n",
X		p.p_cpu, p.p_cpticks, p.p_pctcpu, p.p_wchan);
X	printf(" time 0x%x slptime 0x%x pri %d usrpri %d nice %d\n",
X		p.p_time, p.p_slptime, p.p_pri, p.p_usrpri, p.p_nice);
X	printf(" addr 0x%x link 0x%x rlink 0x%x nxt 0x%x prev 0x%x\n",
X		p.p_addr, p.p_link, p.p_rlink, p.p_nxt, p.p_prev);
X	printf(" cred 0x%x fd 0x%x stats 0x%x limit 0x%x\n",
X		p.p_cred, p.p_fd, p.p_stats, p.p_limit);
X	printf(" vmspace 0x%x sigacts 0x%x\n",
X		p.p_vmspace, p.p_sigacts);
X}
END-of-proc.c
echo x - trace.c
sed 's/^X//' >trace.c << 'END-of-trace.c'
X/*
X * trace.c
X *	Routines to do stack backtraces
X */
X#include <machine/tss.h>
X
Xextern void *paddr();
X
X/*
X * trace()
X *	Given proc slot #, provide stack backtrace
X */
Xvoid
Xtrace(str)
X	char *str;
X{
X	int pid, ebp, eip;
X	void *pa;
X	struct i386tss tss;
X
X	/*
X	 * Parse PID
X	 */
X	if (!str || !*str)
X		return;
X	if (sscanf(str, "%d", &pid) != 1) {
X		printf("Invalid pid: %s\n", str);
X		return;
X	}
X
X	/*
X	 * Get opaque pointer to proc structure
X	 */
X 	pa = paddr(pid);
X
X 	/*
X 	 * Read in TSS for process, set root page table to process
X 	 */
X 	readloc(pa, &tss, sizeof(tss), 0);
X 	user_pde(tss.tss_cr3);
X
X 	/*
X 	 * Loop, reading pairs of frame pointers and return addresses
X 	 */
X#define INSTACK(v) ((((unsigned long)v) & 0xFFFFF000) == 0xFDBFF000)
X 	for (ebp = tss.tss_ebp, eip = tss.tss_eip; INSTACK(ebp);) {
X 		struct {
X 			int s_ebp;
X 			int s_eip;
X 			int s_args[9];
X		} stkframe;
X		int narg, x;
X		char *p, *loc;
X 		extern char *symloc(), *strchr();
X
X 		/*
X 		 * Read next stack frame, output called procedure name
X 		 */
X 		readloc(ebp, &stkframe, sizeof(stkframe), 0);
X 		loc = symloc(eip);
X 		if (p = strchr(loc, '+'))
X 			*p = '\0';
X 		printf("%s(", loc);
X
X 		/*
X 		 * Calculate number of arguments, default to 4.  We
X 		 * figure it out by looking at the stack cleanup at
X 		 * the return address.  If GNU C has optimized this
X 		 * out (for instance, bunched several cleanups together),
X 		 * we're out of luck.
X 		 */
X 		readloc(stkframe.s_eip, &x, sizeof(x), 0);
X 		if ((x & 0xFF) == 0x59)
X 			narg = 1;
X		else if ((x & 0xFFFF) == 0xC483)
X			narg = ((x >> 18) & 0xF);
X		else
X			narg = 4;
X
X 		/*
X 		 * Print arguments
X 		 */
X 		for (x = 0; x < narg; ++x) {
X 			if (x > 0)
X 				printf(", ");
X 			printf("0x%x", stkframe.s_args[x]);
X		}
X
X		/*
X		 * Print where called from.  We just assume that there's
X		 * a 5-byte long call.
X		 */
X		printf(") called from %s\n", symloc(stkframe.s_eip-5));
X 		ebp = stkframe.s_ebp;
X 		eip = stkframe.s_eip;
X 	}
X
X 	/*
X 	 * Restore kernel PDE now that we're done with this process
X 	 */
X 	kernel_pde();
X}
END-of-trace.c
echo x - vers.h
sed 's/^X//' >vers.h << 'END-of-vers.h'
X/*
X * vers.h
X *	Just a dump little way for me to tabulate releases
X */
Xchar vers[] = "0.0";
END-of-vers.h
exit