Return to BSD News archive
Newsgroups: comp.unix.bsd Path: sserve!manuel.anu.edu.au!munnari.oz.au!spool.mu.edu!news.cs.indiana.edu!umn.edu!csus.edu!netcom.com!jtk From: jtk@netcom.com (Jane Valencia) Subject: [386BSD] crash 1/2 Message-ID: <1992Oct10.211341.4892@netcom.com> Organization: Netcom - Online Communication Services (408 241-9760 guest) Date: Sat, 10 Oct 1992 21:13:41 GMT Lines: 1450 # 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