/* * U N I X 2 . 9 B S D C R A S H A N A L Y Z E R * * * Name - * crash -- analyze post-mortem or active system image * * Author - * Mike Muuss, JHU EE * * Synopsis of options - * * -b Brief mode; skip general display of processes * -d Crash dump contains swap image. (default?) * -v Verbose; dump much information about each proc [future] * -t TTY structs to be dumpped * -i Incore inode table to be printed * -c FILE Provide non-standard file name for system image input * -s FILE Provide non-standard symboltable input * -u ADDR Trace a process other than currently selected one * -z Interrupt Trace displayed * APS Print PS & PC at time of interupt * * * * synopsis: crash [aps] [-s sfile] [-c cfile] * * It examines a dump of unix which it looks for in the file * sysdump. It prints out the contents of the general * registers, the kernel stack and a traceback through the * kernel stack. If an aps is specified, the ps and pc at * time of interrupt are also printed out. The dump of the * stack commences from a "reasonable" address and all addresses * are relocated to virtual addresses by using the value of * kdsa6 found in the dump. * If the -s argument is found the following argument is taken * to be the name of a file, containing a symbol table which * should be used in interpreting text addresses. The default * is "/unix". If the -c argument is found, the following argument * is taken to be the name of a file which should be used instead * of the default. * * R E V I S I O N H I S T O R Y * * ??/??/?? MJM More than I can tell * * 01/09/80 MJM Added C-list printing for JHU/UNIX clists. * * 12/11/80 MJM+RSM Modified to use new MOUNT structure. * * 12/16/80 RSM Ability to ask for text or data symbol on printout. * * 12/21/80 RSM Added ability to name-list an overlay file. * * 10/07/81 RSM Can now print longs in decimal format. * I don't think this works--JCS * * 12/08/81 JCP+MJM The SYS Group * Modified to work for Version 7 Vanilla UNIX * * 03/03/83 JCS Modified for 2.81 kernel * Another new mount structure * Made stack traces work with SRI overlaid kernel * New p_flag values, uses USIZE * Dump of some network stats (broke tty dump--Ritchie * compiler is a crock) * Print longs in octal format. * * 2/10/84 GLS Modified for 2.9 kernel * Made it work with MENLO_KOV * Output 2.9 clists. * */ #include #include #include #include #include #include #include #include #include #define USIZEB (64*USIZE) /* *-----The following three lines *must* remain together */ #include struct user u; char u_wasteXX[USIZEB-sizeof(struct user)]; /* STORAGE FOR KERNEL STACKS * TO LOAD--must follow user.h */ #include #include #include #include #include #include #include #include "crash.h" struct buf bfreelist; struct proc *proc; /* NPROC */ struct callout *callout; /* NCALL */ /* * Interrupt Tracing Strutures - The SYS Group */ #ifdef INTRPT /* * to preserve the integrity of the trace information, the pointer and data * area are fetched in the same read. This means the following two lines * *MUST* appear in this order */ char *itrptr; /* pointer to next structure to use */ struct itrace itrace[100]; /* max size of trace area */ /* related kernel variables */ char *itrstr; /* start of tracing area */ char *itrend; /* end of tracing area */ #endif INTRPT /* Actual core allocation for tty structures */ int ndh11 = 0; struct tty *dh11; /* NDH11 */ int ndz11 = 0; struct tty *dz11; /* NDZ11 */ int nkl11 = 0; struct tty *kl11; /* NKL11 */ /* Global Variables */ struct msgbuf msgbuf; char *panicstr; unsigned saveps; /* ps after trap */ long dumplo; /* Offset of swapspace on dump */ unsigned nbuf; /* size of buffer cache */ int ncallout; /* max simultaneous callouts */ int nfile; /* Stored value of NFILE */ int ninode; /* Stored value of NINODE */ int nproc; /* Stored value of NPROC */ int ntext; /* Stored value of NTEXT */ int nswap; /* Number of swap blocks */ int nmount; /* number of mountable file systems */ int nclist; /* number of clists */ long stime; /* crash time */ long bootime; /* when system was booted */ unsigned bpaddr; /* Location of actual buffers (clicks) */ dev_t rootdev; /* device of the root */ dev_t swapdev; /* swapping device */ dev_t pipedev; /* pipe device */ unsigned bsize; /* size of buffers */ int cputype; /* type of cpu = 40, 44, 45, 60, or 70 */ struct inode *rootdir; /* pointer to inode of root directory */ struct proc *runq; /* head of linked list of running processes */ int lbolt; /* clock ticks since time was last updated */ int mpid; /* generic for unique process id's */ bool_t runin; /* set when sched wants to swap someone in */ bool_t runout; /* set when sched is out of work */ bool_t runrun; /* reschedule at next opportunity */ char curpri; /* p_pri of current process */ size_t maxmem; /* actual max memory per process */ size_t physmem; /* physical memory */ u_short *lks; /* pointer to clock device */ int updlock; /* lock for sync */ daddr_t rablock; /* block to be read ahead */ memaddr clststrt; /* Location of actual clists (clicks) */ memaddr cfreebase; /* base of clists in kernel */ unsigned clsize; /* Size of clists */ short ucbclist; /* mapped out clist flag */ /* * V7 (Stone knives & bear skins memorial) Clist structures. "cfree" conflicts * with calloc's cfree ... */ struct cblock *cfree_; /* NCLIST */ struct cblock *cfreelist; int cbad; char *subhead; /* pntr to sub-heading */ int line = 0; /* current line number */ int kmem; /* Global FD of dump file */ int overlay; /* overlays found flag */ int ovno, ovend; /* current overlay number */ u_int aova, aovd; /* addresses of ova and ovd in core */ unsigned ova[8], ovd[8]; /* overlay addresses and sizes */ /* Display table for Mounted Files */ struct display mnt_tab[] = { "\n\nDevice", (char *) &((struct mount *)0)->m_dev, DEV, 0, "\tOn Inode", (char *) &((struct mount *)0)->m_inodp, OCT, 0, END }; #ifdef INTRPT /* Display for Interrupt Tracing Feature (the SYS Group) */ struct display itr_buf[] { "", &itrace[0].intpc, OCT, 0, "", &itrace[0].intpc, TADDR, 0, "", &itrace[0].intps, OCT, 0, "", &itrace[0].r0, OCT, 0, "", &itrace[0].r0, TADDR, 0, "", &itrace[0].savps, OCT, 0, END }; #endif INTRPT /* Display table for Callouts */ struct display cal_buf[] = { "\nfunc", (char *) &((struct callout *)0)->c_func, OCT, 0, " ", (char *) &((struct callout *)0)->c_func, TADDR, 0, "\targ", (char *) &((struct callout *)0)->c_arg, DEC, 0, "\ttime", (char *) &((struct callout *)0)->c_time,DEC, 0, END }; /* Display for C-list display */ struct display cl_buf[] = { ": link", (char *) &((struct cblock *)0)->c_next, OCT, 0, "\tCh", (char *) ((struct cblock *)0)->c_info, CHARS, 0, END }; int ttyflg(), ttystat(); /* Display for tty structures */ struct display tty_buf[] = { "\n\nMaj/Min", (char *) &((struct tty *)0)->t_dev, DEV, 0, "\nFlags", (char *) &((struct tty *)0)->t_flags, OCT, ttyflg, "\nAddr", (char *) &((struct tty *)0)->t_addr, OCT, 0, "", (char *) &((struct tty *)0)->t_addr, DADDR, 0, "\nDelct", (char *) &((struct tty *)0)->t_delct, HALFDEC, 0, "\nLine", (char *) &((struct tty *)0)->t_line, HALFDEC, 0, "\nCol", (char *) &((struct tty *)0)->t_col, HALFDEC, 0, "\nState", (char *) &((struct tty *)0)->t_state, OCT, ttystat, "\nIspeed", (char *) &((struct tty *)0)->t_ispeed, HALFDEC, 0, "\tOspeed", (char *) &((struct tty *)0)->t_ospeed, HALFDEC, 0, "\nRAW", (char *) &((struct tty *)0)->t_rawq.c_cc,DEC, 0, "\tc_cf", (char *) &((struct tty *)0)->t_rawq.c_cf,OCT, 0, "\tc_cl", (char *) &((struct tty *)0)->t_rawq.c_cl,OCT, 0, "\nOUT", (char *) &((struct tty *)0)->t_outq.c_cc,DEC, 0, "\tc_cf", (char *) &((struct tty *)0)->t_outq.c_cf,OCT, 0, "\tc_cl", (char *) &((struct tty *)0)->t_outq.c_cl,OCT, 0, "\nCAN", (char *) &((struct tty *)0)->t_canq.c_cc,DEC, 0, "\tc_cf", (char *) &((struct tty *)0)->t_canq.c_cf,OCT, 0, "\tc_cl", (char *) &((struct tty *)0)->t_canq.c_cl,OCT, 0, END }; /* Display table for Random Variables */ struct display rv_tab[] = { "\nRoot directory", (char *) &rootdir, OCT, 0, "\nLightningbolt", (char *) &lbolt, DEC, 0, "\nNext PID", (char *) &mpid, DEC, 0, "\n runrun", &runrun, OCT, 0, "\n runout", &runout, OCT, 0, "\n runin", &runin, OCT, 0, "\n curpri", &curpri, OCT, 0, "\n maxmem", (char *) &maxmem, OCT, 0, "\n physmem", (char *) &physmem, OCT, 0, "\nClock addr", (char *) &lks, OCT, 0, "\nRoot dev", (char *) &rootdev, DEV, 0, "\nSwap Dev", (char *) &swapdev, DEV, 0, "\nPipe Dev", (char *) &pipedev, DEV, 0, "\nSwap Size", (char *) &nswap, DEC, 0, "\nupdate() lock", (char *) &updlock, DEC, 0, "\nReadahead blk", (char *) &rablock, OCT, 0, "\n Max procs", (char *) &nproc, DEC, 0, "\n Max inodes", (char *) &ninode, DEC, 0, "\n Max files", (char *) &nfile, DEC, 0, "\n Max texts", (char *) &ntext, DEC, 0, "\n*cfreelist", (char *) &cfreelist, OCT, 0, "\nSystem Buffer start click", (char *) &bpaddr, OCT, 0, "\tNo. Buffers", (char *) &nbuf, DEC, 0, "\tSize", (char *) &bsize, OCT, 0, "\nClist start click", (char *) &clststrt, OCT, 0, "\nClist start address",(char *) &cfreebase, OCT, 0, "\tSize of area", (char *) &clsize, DEC, 0, END }; int pdrprint(), printuid(), printgid(); struct display ov_tab[] = { "\n\nThe Kernel Overlay Information and Variables:\n\t__ovno", (char *) &ovno, DEC, 0, "\n\tOverlay #0 PAR", (char *) &ova[0], OCT, 0, "\tPDR", (char *) &ovd[0], OCT, pdrprint, "\n\tOverlay #1 PAR", (char *) &ova[1], OCT, 0, "\tPDR", (char *) &ovd[1], OCT, pdrprint, "\n\tOverlay #2 PAR", (char *) &ova[2], OCT, 0, "\tPDR", (char *) &ovd[2], OCT, pdrprint, "\n\tOverlay #3 PAR", (char *) &ova[3], OCT, 0, "\tPDR", (char *) &ovd[3], OCT, pdrprint, "\n\tOverlay #4 PAR", (char *) &ova[4], OCT, 0, "\tPDR", (char *) &ovd[4], OCT, pdrprint, "\n\tOverlay #5 PAR", (char *) &ova[5], OCT, 0, "\tPDR", (char *) &ovd[5], OCT, pdrprint, "\n\tOverlay #6 PAR", (char *) &ova[6], OCT, 0, "\tPDR", (char *) &ovd[6], OCT, pdrprint, "\n\tOverlay #7 PAR", (char *) &ova[7], OCT, 0, "\tPDR", (char *) &ovd[7], OCT, pdrprint, "\n\tOverlay #8 PAR", (char *) &ova[8], OCT, 0, "\tPDR", (char *) &ovd[8], OCT, pdrprint, "\n\tOverlay #9 PAR", (char *) &ova[9], OCT, 0, "\tPDR", (char *) &ovd[9], OCT, pdrprint, "\n\tOverlay #a PAR", (char *) &ova[10], OCT, 0, "\tPDR", (char *) &ovd[10], OCT, pdrprint, "\n\tOverlay #b PAR", (char *) &ova[11], OCT, 0, "\tPDR", (char *) &ovd[11], OCT, pdrprint, "\n\tOverlay #c PAR", (char *) &ova[12], OCT, 0, "\tPDR", (char *) &ovd[12], OCT, pdrprint, "\n\tOverlay #d PAR", (char *) &ova[13], OCT, 0, "\tPDR", (char *) &ovd[13], OCT, pdrprint, "\n\tOverlay #e PAR", (char *) &ova[14], OCT, 0, "\tPDR", (char *) &ovd[14], OCT, pdrprint, "\n\tOverlay #f PAR", (char *) &ova[15], OCT, 0, "\tPDR", (char *) &ovd[15], OCT, pdrprint, END }; int bufflg(); struct display buf_tab[] = { "\nFlags", (char *) &((struct buf *)0)->b_flags, OCT, bufflg, "\tdev", (char *) &((struct buf *)0)->b_dev, DEV, 0, "\tbcount", (char *) &((struct buf *)0)->b_bcount, DEC, 0, "\nxmem", &((struct buf *)0)->b_xmem, ONEBYTE, 0, "\taddr", (char *) &((struct buf *)0)->b_un.b_addr, OCT, 0, "\tblkno", (char *) &((struct buf *)0)->b_blkno, LONGDEC, 0, "\nerror", &((struct buf *)0)->b_error, ONEBYTE, 0, "\tresid", (char *) &((struct buf *)0)->b_resid, OCT, 0, END }; int inoflg(), inomod(); struct display inode_tab[] = { "\ndev", (char *) &((struct inode *)0)->i_dev,DEV, 0, "\tnumber", (char *) &((struct inode *)0)->i_number,DEC,0, "\tcount", (char *) &((struct inode *)0)->i_count, DEC,0, "\tflag", (char *) &((struct inode *)0)->i_flag, OCT,inoflg, "\nnlink", (char *) &((struct inode *)0)->i_nlink, DEC,0, "\tuid", (char *) &((struct inode *)0)->i_uid, UDEC,printuid, "\tgid", (char *) &((struct inode *)0)->i_gid, UDEC,printgid, "\nmode", (char *) NULL, IGNORE, inomod, END }; /* Display the USER Structure */ /* * note that on V7, the u.u_ar0 and u.u_qsav areas contain more than * they do on V6 UNIX * * On V6 u.u_ar0[0] = saved R5 * u.u_ar0[1] = saved R6 * * On V7 u.u_ar0[0] = saved R2 * u.u_ar0[1] = saved R3 * u.u_ar0[2] = saved R4 * u.u_ar0[3] = saved R5 * u.u_ar0[4] = saved R6 (stack pointer) * * for this reason, we declare the following labels, which must be used * anywhere the saved registers are referenced */ #define R2 0 /* index of saved R2 */ #define R3 1 #define R4 2 #define R5 3 #define R6 4 #define R1 5 struct display u_tab[] = { "\nsaved R5", (char *) &(u.u_rsave.val[R5]), OCT, 0, "\tsaved R6", (char *) &(u.u_rsave.val[R6]), OCT, 0, "\tsegflg", &(u.u_segflg), ONEBYTE, 0, "\terror", &(u.u_error), HALFDEC, 0, "\nuid", (char *) &(u.u_uid), UDEC, printuid, "\nsvuid", (char *) &(u.u_svuid), UDEC, printuid, "\nruid", (char *) &(u.u_ruid), UDEC, printuid, "\tsvgid", (char *) &(u.u_svgid), UDEC, printgid, "\trgid", (char *) &(u.u_rgid), UDEC, printgid, "\tgroups", (char *) u.u_groups, UDEC, printgid, "\nprocp", (char *) &(u.u_procp), OCT, 0, "\tbase", (char *) &(u.u_base), OCT, 0, "\tcount", (char *) &(u.u_count), DEC, 0, "\toff", (char *) &(u.u_offset), HEXL, 0, "\ncdir", (char *) &(u.u_cdir), OCT, 0, "\tdirp", (char *) &(u.u_dirp), OCT, 0, "\tpdir", (char *) &(u.u_pdir), OCT, 0, "\targ0", (char *) &(u.u_arg[0]), OCT, 0, "\narg1", (char *) &(u.u_arg[1]), OCT, 0, "\targ2", (char *) &(u.u_arg[2]), OCT, 0, "\targ3", (char *) &(u.u_arg[3]), OCT, 0, "\targ4", (char *) &(u.u_arg[4]), OCT, 0, "\narg5", (char *) &(u.u_arg[5]), OCT, 0, "\ttsize", (char *) &(u.u_tsize), DEC, 0, "\tdsize", (char *) &(u.u_dsize), DEC, 0, "\tssize", (char *) &(u.u_ssize), DEC, 0, "\nctty", (char *) &(u.u_ttyp), OCT, 0, "\tttydev", (char *) &(u.u_ttyd), DEV, 0, "\nsep", &(u.u_sep), ONEBYTE, 0, "\tar0", (char *) &(u.u_ar0), OCT, 0, END }; int procflg(), prtsig(); /* Display table for Proc Structure */ struct display proc_tab[] = { "\nStat", (char *) &((struct proc *)0)->p_stat, ONEBYTE, 0, "\tFlags", (char *) &((struct proc *)0)->p_flag, HEX, procflg, "\nPri", (char *) &((struct proc *)0)->p_pri, HALFDEC, 0, "\tSig", (char *) &((struct proc *)0)->p_sig, HEXL, 0, "\tCursig", (char *) &((struct proc *)0)->p_cursig, HALFDEC, 0, "\tUid", (char *) &((struct proc *)0)->p_uid, DEC, printuid, "\nsigmask", (char *) &((struct proc *)0)->p_sigmask,HEXL, prtsig, "\nsigignore", (char *) &((struct proc *)0)->p_sigignore,HEXL, prtsig, "\nsigcatch", (char *) &((struct proc *)0)->p_sigcatch,HEXL, prtsig, "\nTime", (char *) &((struct proc *)0)->p_time, HALFDEC, 0, "\tcpu", (char *) &((struct proc *)0)->p_cpu, HALFDEC, 0, "\tnice", (char *) &((struct proc *)0)->p_nice, HALFDEC, 0, "\tpgrp", (char *) &((struct proc *)0)->p_pgrp, OCT, 0, "\nPid", (char *) &((struct proc *)0)->p_pid, DEC, 0, "\tPpid", (char *) &((struct proc *)0)->p_ppid, DEC, 0, "\taddr", (char *) &((struct proc *)0)->p_addr, OCT, 0, "\tdaddr", (char *) &((struct proc *)0)->p_daddr, OCT, 0, "\tsaddr", (char *) &((struct proc *)0)->p_saddr, OCT, 0, "\tdsize", (char *) &((struct proc *)0)->p_dsize, OCT, 0, "\tssize", (char *) &((struct proc *)0)->p_ssize, OCT, 0, "\ntextp", (char *) &((struct proc *)0)->p_textp, OCT, 0, "\tWchan", (char *) &((struct proc *)0)->p_wchan, OCT, 0, "", (char *) &((struct proc *)0)->p_wchan, DADDR, 0, "\nRlink", (char *) &((struct proc *)0)->p_link, OCT, 0, "\tClktim", (char *) &((struct proc *)0)->p_clktim, DEC, 0, "\tSlptim", (char *) &((struct proc *)0)->p_slptime, HALFDEC, 0, END }; /* The Process Status word interpretation */ char *pmsg1[] = { "", "Sleeping (all cases)", "Abandoned Wait State", "*** Running ***", "Being created", "Being Terminated (ZOMB)", "Ptrace Stopped", "" }; /* The Process Flag Word bit table */ char *p_bits[] = { "in core, ", "scheduler proc, ", "locked in core, ", "being swapped, ", "being traced, ", "WTED, ", "ULOCK, ", "OMASK,", "VFORK, ", "VFPRINT, ", "VFDONE, ", "timing out, ", "detached, ", "OUSIG, ", "selecting, ", }; #define FLAG_MASK 037777 /* * This table lists all of the dump variables to be fetched. */ struct fetch fetchtab[] = { #ifdef INTRPT /* interrupt tracing feature - The SYS Group */ "_savptr", &itrptr, sizeof itrptr + sizeof itrace, #endif "_bfreelist", (char *) &bfreelist, sizeof bfreelist, "_nfile", (char *) &nfile, sizeof nfile, "_nmount", (char *) &nmount, sizeof nmount, "_nproc", (char *) &nproc, sizeof nproc, "_ntext", (char *) &ntext, sizeof ntext, "_nclist", (char *) &nclist, sizeof nclist, "_nbuf", (char *) &nbuf, sizeof nbuf, "_ncallout", (char *) &ncallout, sizeof ncallout, "_u", (char *) &u, sizeof u, "_bpaddr", (char *) &bpaddr, sizeof bpaddr, "_bsize", (char *) &bsize, sizeof bsize, "_ucb_cli", (char *) &ucbclist, sizeof ucbclist, "_clststrt", (char *) &clststrt, sizeof clststrt, "_cfree", (char *) &cfreebase, sizeof cfreebase, "_ninode", (char *) &ninode, sizeof ninode, /* Random Variables */ #ifdef notdef "_dumplo", (char *) &dumplo, sizeof dumplo, #endif notdef "_rootdir", (char *) &rootdir, sizeof rootdir, "_cputype", (char *) &cputype, sizeof cputype, "_lbolt", (char *) &lbolt, sizeof lbolt, "_time", (char *) &stime, sizeof stime, "_boottim", (char *) &bootime, sizeof bootime, "_mpid", (char *) &mpid, sizeof mpid, "_runrun", (char *) &runrun, sizeof runrun, "_runout", (char *) &runout, sizeof runout, "_runin", (char *) &runin, sizeof runin, "_curpri", (char *) &curpri, sizeof curpri, "_maxmem", (char *) &maxmem, sizeof maxmem, "_physmem", (char *) &physmem, sizeof physmem, "_rootdev", (char *) &rootdev, sizeof rootdev, "_swapdev", (char *) &swapdev, sizeof swapdev, "_pipedev", (char *) &pipedev, sizeof pipedev, "_nswap", (char *) &nswap, sizeof nswap, "_updlock", (char *) &updlock, sizeof updlock, "_rablock", (char *) &rablock, sizeof rablock, "_msgbuf", (char *) &msgbuf, sizeof msgbuf, "_panicst", (char *) &panicstr, sizeof panicstr, END }; struct fetch fetch1tab[] = { "__ovno", (char *) &ovno, sizeof ovno, END }; /* * This table lists all addresses to be fetched. */ struct fetch fetchatab[] = { "ova", (char *) &aova, sizeof *ova, "ovd", (char *) &aovd, sizeof *ovd, END }; char *corref = "/core"; /* Default dump file */ char *symref = "/unix"; char *MIS_ARG = "Missing arg"; unsigned find(); int stroct(); char *calloc(); /* Flag definitions */ int bflg = 0; /* brief flag -- u only */ int dflg = 0; /* Swapdev flag */ int vflg = 0; /* verbose flag -- dump each proc */ int tflg = 0; /* dump tty structs flag */ int iflg = 0; /* dump inode table flag */ #ifdef INTRPT int trflg = 0; /* interrupt trace display flag */ #endif INTRPT /* * M A I N * * Consume options and supervise the flow of control * */ main(argc,argv) int argc; char **argv; { static unsigned *up; /*!!! never initialized */ register int i; int trapstack; int u_addr; /* u block number */ int u_death; /* death u */ unsigned sp; unsigned r5; unsigned xbuf[8]; trapstack = 0; u_addr = 0; /* parse off the arguments and do what we can with them */ for(i=1; i= argc) barf( MIS_ARG ); corref = argv[++i]; continue; case 's': /* Symboltable input file name */ if ((i+1) >= argc) barf( MIS_ARG ); symref = argv[++i]; continue; case 'u': /* trace a process other than current (takes U addr) */ if ((i+1) >= argc) barf( MIS_ARG ); u_addr = stroct( argv[++i] ); continue; case 'b': /* Brief */ bflg++; continue; case 'd': /* Dump includes swap area */ dflg++; continue; case 'v': /* do everything */ /* Verbose */ vflg++; continue; case 't': /* TTY structures */ tflg++; continue; case 'i': /* Inode table */ iflg++; continue; #ifdef INTRPT case 'z': /* Interrupt Trace */ trflg++; continue; #endif INTRPT default: printf("Bad argument: %s\n", argv[i]); case 'h': /* Help message */ printf("Usage: crash [-i] [-d] -[t] [-c dumpfile] [-s symtab file] [-u u-addr]\n\n"); exit(0); } } else { trapstack = stroct( argv[i] ); if( trapstack == 0 ) printf("Bad argument: %s\n", argv[i]); } kmem = open(corref,0); if (kmem < 0) { printf("Unable to open %s\n",corref); exit(-1); } /* * Load the symbol table */ getsym(symref); /* * Load all of the shadow variables from the dump file, * and print the header page. */ fetch(fetchtab); if (tflg) { clsize = nclist * sizeof(struct cblock); cfree_ = (struct cblock *)calloc(nclist,sizeof(struct cblock)); if (cfree_ == (struct cblock *) NULL) { printf("crash: out of memory getting cfree\n"); exit(1); } /* CLIST loaded first, improve accuracy on dump running sys */ if (!vf("_cfreeli", (char *) &cfreelist, sizeof cfreelist)) { printf("could not find _cfreeli\n"); exit(1); } if (ucbclist) lseek(kmem, (off_t)ctob((long)clststrt), 0); /* clists click */ else lseek(kmem, (off_t) cfreebase, 0); /* clists in kernel */ read(kmem, (caddr_t) cfree_, clsize); vf("_nkl11", (char *) &nkl11, sizeof nkl11); kl11 = (struct tty *) calloc(nkl11, sizeof(struct tty)); if (kl11 == (struct tty *) NULL) { printf("crash: out of memory in kl11\n"); exit(1); } vf("_kl11", (char *) kl11, sizeof(struct tty)*nkl11); if (vf("_ndh11", (char *) &ndh11, sizeof ndh11)) { dh11 = (struct tty *) calloc(ndh11, sizeof(struct tty)); if (dh11 == (struct tty *) NULL) { printf("crash: out of memory in dh11\n"); exit(1); } vf("_dh11", (char *) dh11, sizeof(struct tty)*ndh11); } if (vf("_ndz11", (char *) &ndz11, sizeof dz11)) { dz11 = (struct tty *) calloc(ndz11, sizeof(struct tty)); if (dz11 == (struct tty *) NULL) { printf("crash: out of memory in dz11\n"); exit(1); } vf("_dz11", (char *) dz11, sizeof(struct tty)*ndz11); } } /* * Setup for MENLO_KOV */ if (overlay) { fetch(fetch1tab); fetcha(fetchatab); vf("ova", (char *) ova, sizeof ova); vf("ovd", (char *) ovd, sizeof ovd); getpars(); } /* Display general information */ general(); /* dump the registers */ subhead = "Registers & Stack Dump"; saveps = find("saveps", 0); lseek(kmem, (off_t) saveps, 0); read(kmem, (caddr_t)&saveps, sizeof saveps); newpage(); printf("\tThe registers\n"); lseek(kmem, (off_t)0300,0); read(kmem,xbuf,sizeof xbuf); printf("\nr0: "); octout(xbuf[0]); /* 2 */ putchar(' '); if(!symbol(xbuf[0], DSYM, 0)) putchar('\t'); printf("\tr1: "); octout(xbuf[1]); /* 3 */ putchar(' '); if(!symbol(xbuf[1], DSYM, 0)) putchar('\t'); printf("\tr2: "); octout(xbuf[2]); /* 4 */ putchar(' '); if(!symbol(xbuf[2], DSYM, 0)) putchar('\t'); printf("\nr3: "); octout(xbuf[3]); /* 5 */ putchar(' '); if(!symbol(xbuf[3], DSYM, 0)) putchar('\t'); printf("\tr4: "); octout(xbuf[4]); /* 6 */ putchar(' '); if(!symbol(xbuf[4], DSYM, 0)) putchar('\t'); printf("\tr5: "); octout(xbuf[5]); /* 7 */ putchar(' '); if(!symbol(xbuf[5], DSYM, 0)) putchar('\t'); printf("\nsp: "); octout(xbuf[6]); /* 8 */ putchar(' '); if(!symbol(xbuf[6], DSYM, 0)) putchar('\t'); printf("\tksda: "); octout(xbuf[7]); /* 9 */ printf("\t\tps: "); octout(saveps); printps(saveps); /* was: sp = xbuf[6] & 0177760; /*!!! why this mask?? */ sp = xbuf[6] & 0177776; r5 = xbuf[5]; /* * Take a stack dump */ u_death = xbuf[7]; if( u_addr == 0 ) u_addr = u_death; /* lseek in u_addr core clicks (64 bytes) */ lseek(kmem, (off_t)(unsigned)u_addr<<6,0); read (kmem, &u, USIZEB); if (u_addr != u_death) { sp = u.u_rsave.val[R6]; r5 = u.u_rsave.val[R5]; } printf ("\n\n u at 0%o, r5 = 0%o, sp = 0%o\n\n", u_addr, r5, sp); /* If an aps was specified, give ps, pc, sp at trap time */ if (trapstack == 0) { trapstack = sp; /* temporary */ } else { printf("\n\n\timmediately prior to the trap:\n"); printf("\nsp: "); octout((unsigned)sp+4); printf("\tps: "); octout(up[1]); printf("\tpc: "); octout(up[0]); putchar(' '); symbol(up[0], ISYM, 0); } stackdump( sp-2, sp-4 ); /* current r5 and some number less */ /* Display Procsss information */ if (!bflg) dprocs(); putchar('\n'); /* Flush buffer: The End */ exit(0); } /* * S T A C K D U M P * * This routine interprets the kernel stack in the * user structure (_u). */ stackdump( r5, sp ) unsigned r5, sp; { register unsigned *up; register unsigned i; if ((sp < 0140000) || (sp > 0140000+USIZEB-18)) { printf("\n\tsp(%0o) is unreasonable, 0140000 assumed\n", sp); sp = 0140000; /* since sp is bad, give him whole user area */ } /* now give a dump of the stack, relocating as we go */ printf("\n\n"); printf("STACK: loc+00 loc+02 loc+04 loc+06 loc+10 loc+12 loc+14 loc+16"); for( i = sp & ~017; i < 0140000+USIZEB; i += 16) { up = (unsigned *) &u; up += ((int)i - 0140000)>>1; /* damn pointer conversions */ printf("\n"); octout(i); printf(": "); octout(up[0]); printf(" "); octout(up[1]); printf(" "); octout(up[2]); printf(" "); octout(up[3]); printf(" "); octout(up[4]); printf(" "); octout(up[5]); printf(" "); octout(up[6]); printf(" "); octout(up[7]); } /* go back to beginning of stack space and give trace */ printf("\n\n"); printf("TRACE: old r2 old r3 old r4 "); if (overlay) printf("ovno "); printf("old r5 old pc"); i = sp; while ((r5 > i) && (r5 < 0140000+USIZEB)) { if (r5 == 0140000+USIZEB-18) break; up = (unsigned *) &u; up += ((int)r5 - 0140000)>>1; printf("\n"); octout(r5); printf(": "); if (overlay) { octout(up[-4]); printf(" "); } octout(up[-3]); printf(" "); octout(up[-2]); printf(" "); octout(up[-1]); printf(" "); octout(up[0]); printf(" "); octout(up[1]); putchar(' '); symbol(up[1], ISYM, (overlay ? up[-1] : 0)); i = r5 + 4; r5 = up[0]; } putchar('\n'); if (r5 != 0140000+USIZEB-18) printf("\tThe dynamic chain is broken\n"); } /* * G E N E R A L * * * Display information of a general nature */ general() { register int i; /* multi-function counter */ register int j; /* Set up page heading */ subhead = "General Information"; newpage(); /* Tell general stuff */ printf("UNIX Version 2.10bsd was running on a PDP-11/%d CPU\n", cputype); printf("when a crash occured at %s", ctime(&stime)); printf("system booted at %s", ctime(&bootime)); /* print panic message */ panicpr(); printf("\n\nThe `Random Variables' at time of crash:\n\n"); display(rv_tab, 0); if (overlay) display(ov_tab, 0); /* print contents of msgbf */ msgbufpr(); /* Display Mount Table */ { struct mount *mount; /* NMOUNT */ register struct mount *mp; mount = (struct mount *) calloc(nmount, sizeof(struct mount)); if (mount != (struct mount *) NULL) { subhead = "The Mount Structure"; newpage(); if (vf("_mount", (char *)mount, sizeof(struct mount)*nmount)) for(mp = mount; mp < &mount[nmount]; mp++) if (mp->m_dev) { display(mnt_tab, mp); printdev(mp); } free(mount); } } /* Display mbuf status info */ dispnet(); /* Display the Callouts */ if( tflg ) { struct callout *cp; subhead = "The Callout Structure"; newpage(); callout = (struct callout *) calloc(ncallout, sizeof(struct callout)); if (callout == (struct callout *) NULL) { printf("crash: out of memory getting callout\n"); exit(1); } vf("_callout", (char *) callout, sizeof(struct callout)*ncallout); for(cp=callout; cp<&callout[ncallout]; cp++) if (cp->c_func) display( cal_buf, cp ); free(callout); } #ifdef INTRPT /* Display Interrupt Trace */ if( trflg ) { itrstr = find( "_savstr", 1 ); itrend = find( "_savend", 1 ); subhead = "The Interrupt Trace (The SYS Group)"; newpage(); printf("\nEntry %d was the last trap processed\n", (itrptr - itrstr)/(sizeof (struct itrace)) - 1 ); printf("\nEntry\tInt PC\t
\tInt PS\tTrap r0\t
\tTrap PS\n"); for(i = 0; i < (itrend - itrstr)/(sizeof(struct itrace)); i++) { printf("\n%3d",i); display( itr_buf, i*(sizeof (struct itrace)) ); } } #endif /* If requested, dump tty structures and C-list */ if( tflg ) { struct tty *tp; struct cblock *bp; subhead = "The KL-11 TTY Structures"; newpage(); line = 8; for(tp=kl11; tp<&kl11[nkl11]; tp++) { if( line > LINESPERPAGE ) { newpage(); line = 8; } display(tty_buf, tp ); line += 13; if( tp->t_rawq.c_cc > 0 ) { bp = (struct cblock *)(tp->t_rawq.c_cf-1); bp = (struct cblock *)((int)bp & ~CROUND); chase( "RAW", bp); } if( tp->t_outq.c_cc > 0 ) { bp = (struct cblock *)(tp->t_outq.c_cf-1); bp = (struct cblock *)((int)bp & ~CROUND); chase( "OUT", bp ); } if( tp->t_canq.c_cc > 0 ) { bp = (struct cblock *)(tp->t_canq.c_cf-1); bp = (struct cblock *)((int)bp & ~CROUND); chase( "CAN", bp); } } if (ndh11 != 0) { subhead = "The DH-11 TTY structures"; newpage(); line = 8; for(tp=dh11; tp<&dh11[ndh11]; tp++) { if( line > LINESPERPAGE ) { newpage(); line = 8; } display(tty_buf, tp); line += 13; if( tp->t_rawq.c_cc > 0 ) { bp = (struct cblock *)(tp->t_rawq.c_cf-1); bp = (struct cblock *)((int)bp & ~CROUND); chase("RAW", bp); } if( tp->t_outq.c_cc > 0 ) { bp = (struct cblock *)(tp->t_outq.c_cf-1); bp = (struct cblock *)((int)bp & ~CROUND); chase( "OUT", bp); } if( tp->t_canq.c_cc > 0 ) { bp = (struct cblock *)(tp->t_canq.c_cf-1); bp = (struct cblock *)((int)bp & ~CROUND); chase( "CAN", bp); } } } if (ndz11 != 0) { subhead = "The DZ-11 TTY Structures"; newpage(); line = 8; for(tp=dz11; tp<&dz11[ndz11]; tp++) { if( line > LINESPERPAGE ) { newpage(); line = 8; } display(tty_buf, tp ); line += 13; if( tp->t_rawq.c_cc > 0 ) { bp = (struct cblock *)(tp->t_rawq.c_cf-1); bp = (struct cblock *)((int)bp & ~CROUND); chase( "RAW", bp ); } if( tp->t_outq.c_cc > 0 ) { bp = (struct cblock *)(tp->t_outq.c_cf-1); bp = (struct cblock *)((int)bp & ~CROUND); chase( "OUT", bp ); } if( tp->t_canq.c_cc > 0 ) { bp = (struct cblock *)(tp->t_canq.c_cf-1); bp = (struct cblock *)((int)bp & ~CROUND); chase( "CAN", &tp->t_canq ); } } } subhead = "The CBUF Freelist"; newpage(); line = 8; if( cfreelist != (struct cblock *) NULL ) chase( "Freelist", cfreelist); else printf("The freelist was empty\n"); } /* display the C-list */ if( tflg ) { struct cblock *cp; subhead = "The C-list"; newpage(); line = 8; if (ucbclist) j = (unsigned)0120000; else j = (unsigned)cfreebase; for(cp=cfree_; cp<&cfree_[nclist]; cp++) { putchar('\n'); octout( j ); display( cl_buf, cp); j += sizeof(struct cblock); line++; if (line > LINESPERPAGE) { newpage(); line = 8; } } } /* Display the in-core inode table */ if( iflg ) { struct buf *buf, *bp; /* NBUF */ struct inode *inode, *ip; /* NINODE */ buf = (struct buf *) calloc(nbuf, sizeof(struct buf)); if (buf == (struct buf *) NULL) { printf("crash: out of memory in buf\n"); exit(1); } subhead = "The BUF struct"; newpage(); line = 8; j = find("_buf", 1 ); if (vf("_buf", (char *) buf, sizeof(struct buf)*nbuf)) { for(bp = buf; bp < &buf[nbuf]; bp++) { if( line > LINESPERPAGE ) { newpage(); line = 8; } printf("\n\nBuf stored at 0%o\n", j); display(buf_tab, bp); line += 6; j += sizeof(struct buf); } } free(buf); inode = (struct inode *) calloc(ninode, sizeof(struct inode)); if (inode == (struct inode *) NULL) { printf("crash: out of memory in inode\n"); exit(1); } subhead = "The In-Core Inode Table"; newpage(); j = find( "_inode", 1 ); if (vf("_inode", (char *) inode, sizeof(struct inode)*ninode)) { for(ip = inode; ip<&inode[ninode]; ip++ ) { if (ip->i_count == 0) continue; if( line > LINESPERPAGE ) newpage(); printf("\n\nInode stored at %o\n", j); display(inode_tab, ip); line += 6; j += sizeof inode[0]; } } free(inode); } } /* * C H A S E * * This routine is used to chase down a c-list chain, * and display the data found there. * Note the external variable, clststrt or cfreebase. */ chase( msg, headp ) register struct cblock *headp; { register unsigned int cp; memaddr clstp, clstbase, clstend; if (ucbclist) clstbase = (memaddr)0120000; else clstbase = (memaddr)cfreebase; clstend = clstbase + clsize; printf("\n----The %s Queue-----\n", msg); line += 2; clstp = (unsigned) headp; cp = (unsigned) cfree_ + clstp - clstbase; while( clstp != 0 ) { putchar('\n'); line++; if (line > LINESPERPAGE) { newpage(); printf("\n----The %s Queue-----\n", msg); line = 8; } octout( clstp ); if( clstp > clstend || clstp < clstbase) barf(" cp exceeds limit"); display( cl_buf, cp ); clstp = (memaddr) ((struct cblock *)cp)->c_next; cp = (unsigned) cfree_ + clstp - clstbase; } } /* * D U S E R * * * Display the currently loaded User Structure */ duser() { printf("Name: %-15s\n", u.u_comm); display( u_tab, 0 ); printf("\n\nSignal Disposition:"); col( 0, (unsigned *)u.u_signal, NSIG ); printf("\n\nOpen file pointers"); col( 0, (unsigned *)u.u_ofile, NOFILE ); /**** printf("\n\nuisa:"); col( 0, (unsigned *)u.u_uisa, 16 ); printf("\n\nuisd:"); col( 0, (unsigned *)u.u_uisd, 16); ****/ } /* * D P R O C S * * Display information on each process * * For every line entry in the process table, * the ptable entry is displayed. Then the * per-process user structure is loaded into * this program's incore copy of the user structure. * Selected tidbits are displayed by duser(). * Mike Muuss, 7/8/77. */ dprocs() { register struct proc *p; register unsigned i; register unsigned loc; long baddr; /* dump byte addr */ proc = (struct proc *) calloc(nproc, sizeof(struct proc)); if (proc == (struct proc *) NULL) { printf("crash: out of memory getting proc\n"); exit(1); } vf("_proc", (char *) proc, sizeof(struct proc)*nproc); loc = find( "_proc", 1 ); /* base dump addr */ subhead = "Information on Pid XXXXX"; /* page header */ /* Consider each active entry */ for( i=0, p=proc; ip_stat == 0 ) continue; prints( &subhead[19], p->p_pid); newpage(); printf("Entry for PID %d at ", p->p_pid); octout((unsigned) loc); printf("\nStatus was: %s on ", (p->p_stat > sizeof pmsg1 ? (char *)sprintf("%d", p->p_stat) : pmsg1[p->p_stat])); symbol(p->p_wchan, DSYM, 0); printf(" (0%o) wchan.\nFlags set were: ", p->p_wchan); putbits( p_bits, p->p_flag&FLAG_MASK ); /* interpret flags */ putchar('\n'); display( proc_tab, p ); putchar('\n'); /* Analyze the swapspace image */ if( p->p_flag & SLOAD ) { baddr = p->p_addr * 64L; } else { if( !dflg ) { printf("\nImage not avail.\n"); continue; } printf("\n Image retrieved from swapdev.\n"); baddr = ( p->p_addr ) * 512L; } lseek( kmem, (off_t) baddr, 0 ); read( kmem, &u, USIZEB ); /* Verify integrity of swap image, and if OK, print */ if( (unsigned)u.u_procp != loc ) { printf("\n----> Image Defective <----\n"); printf("\nu.u_procp="); octout((unsigned)u.u_procp); printf("\tbaddr="); loctout(baddr); continue; } duser(); /* eventually, I plan to print a core dump */ stackdump( (unsigned)u.u_rsave.val[R5], (unsigned)u.u_rsave.val[R6] ); } free(proc); } /* * print panicstr */ panicpr() { char pbuf[20]; register char ch; register int i; if (panicstr != (char *)0) { printf("\n\tpanicstr: \""); lseek(kmem,(off_t)(unsigned)panicstr,0); read(kmem,pbuf,sizeof pbuf); for (i=0; i 0177) { switch(ch) { case '\n': printf("\\n"); break; case '\r': printf("\\r"); break; case '\t': printf("\\t"); break; default: printf("\\%3o",ch); } } else { putchar(ch); } } putchar('"'); } else { printf("\n\tpanicstr is null"); } } /* * Print contents of msgbuf */ msgbufpr() { register int i; register int j; register char ch; printf("\n\nThe contents of msgbuf:"); for (i=0, j=0; i 0177) { switch(ch) { case '\n': printf(" \\n"); break; case '\r': printf(" \\r"); break; case '\t': printf(" \\t"); break; default: printf("\\%3o",ch); } } else { printf(" %c",ch); } } }