BSD 4 release
[unix-history] / usr / src / cmd / analyze.c
CommitLineData
31cef89c 1static char *sccsid = "@(#)analyze.c 4.1 (Berkeley) 10/1/80";
c4589a32
BJ
2#include <stdio.h>
3#include <sys/param.h>
4#include <sys/dir.h>
5#include <sys/pte.h>
6#include <nlist.h>
7#include <sys/map.h>
8#include <sys/user.h>
9#include <sys/proc.h>
10#include <sys/text.h>
11#include <sys/cmap.h>
12#include <sys/vm.h>
13
14/*
15 * Analyze - analyze a core (and optional paging area) saved from
16 * a virtual Unix system crash.
17 */
18
19int Dflg;
20int dflg;
21int vflg;
22int mflg;
23int fflg;
24int sflg;
25
26/* use vprintf with care; it plays havoc with ``else's'' */
27#define vprintf if (vflg) printf
28
29#define clear(x) ((int)x & 0x7fffffff)
30
31struct proc proc[NPROC];
32struct text text[NTEXT];
33struct map swapmap[SMAPSIZ];
34struct cmap *cmap;
35struct pte *usrpt;
36struct pte *Usrptma;
37int firstfree;
38int maxfree;
39int freemem;
40struct pte p0br[ctopt(MAXTSIZ+MAXDSIZ+MAXSSIZ)][NPTEPG];
41int pid;
42
43struct paginfo {
44 char z_type;
45 char z_count;
46 short z_pid;
47 struct pte z_pte;
48} *paginfo;
49#define ZLOST 0
50#define ZDATA 1
51#define ZSTACK 2
52#define ZUDOT 3
53#define ZPAGET 4
54#define ZTEXT 5
55#define ZFREE 6
56#define ZINTRAN 7
57
58#define NDBLKS (2*SMAPSIZ)
59struct dblks {
60 short d_first;
61 short d_size;
62 char d_type;
63 char d_index;
64} dblks[NDBLKS];
65int ndblks;
66
67#define DFREE 0
68#define DDATA 1
69#define DSTACK 2
70#define DTEXT 3
71#define DUDOT 4
72#define DPAGET 5
73
74union {
75 char buf[UPAGES][512];
76 struct user U;
77} u_area;
78#define u u_area.U
79
80int fcore = -1;
81int fswap = -1;
82
83struct nlist nl[] = {
84#define X_PROC 0
85 { "_proc" },
86#define X_USRPT 1
87 { "_usrpt" },
88#define X_PTMA 2
89 { "_Usrptma" },
90#define X_FIRSTFREE 3
91 { "_firstfr" },
92#define X_MAXFREE 4
93 { "_maxfree" },
94#define X_TEXT 5
95 { "_text" },
96#define X_FREEMEM 6
97 { "_freemem" },
98#define X_CMAP 7
99 { "_cmap" },
100#define X_ECMAP 8
101 { "_ecmap" },
102#define X_SWAPMAP 9
103 { "_swapmap" },
104 { 0 }
105};
106
107main(argc, argv)
108 int argc;
109 char **argv;
110{
111 register struct nlist *np;
112 register struct proc *p;
113 register struct text *xp;
114 register struct pte *pte;
115 register int i;
116 int w, a;
117
118 argc--, argv++;
119 while (argc > 0 && argv[0][0] == '-') {
120 register char *cp = *argv++;
121 argc--;
122 while (*++cp) switch (*cp) {
123
124 case 'm':
125 mflg++;
126 break;
127
128 case 'v':
129 vflg++;
130 break;
131
132 case 's':
133 if (argc < 2)
134 goto usage;
135 if ((fswap = open(argv[0], 0)) < 0) {
136 perror(argv[0]);
137 exit(1);
138 }
139 argc--,argv++;
140 sflg++;
141 break;
142
143 case 'f':
144 fflg++;
145 break;
146
147 case 'D':
148 Dflg++;
149 break;
150
151 case 'd':
152 dflg++;
153 break;
154
155 default:
156 goto usage;
157 }
158 }
159 if (argc < 1) {
160usage:
161 fprintf(stderr, "usage: analyze [ -vmfd ] [ -s swapfile ] corefile [ system ]\n");
162 exit(1);
163 }
164 close(0);
165 if ((fcore = open(argv[0], 0)) < 0) {
166 perror(argv[0]);
167 exit(1);
168 }
169 nlist(argc > 1 ? argv[1] : "/vmunix", nl);
170 if (nl[0].n_value == 0) {
171 fprintf(stderr, "%s: bad namelist\n",
172 argc > 1 ? argv[1] : "/vmunix");
173 exit(1);
174 }
175 for (np = nl; np->n_name[0]; np++)
176 vprintf("%8.8s %x\n", np->n_name ,np->n_value );
177 usrpt = (struct pte *)clear(nl[X_USRPT].n_value);
178 Usrptma = (struct pte *)clear(nl[X_PTMA].n_value);
179 firstfree = get(nl[X_FIRSTFREE].n_value);
180 maxfree = get(nl[X_MAXFREE].n_value);
181 freemem = get(nl[X_FREEMEM].n_value);
182 paginfo = (struct paginfo *)calloc(maxfree, sizeof (struct paginfo));
183 if (paginfo == NULL) {
184 fprintf(stderr, "maxfree %x?... out of mem!\n", maxfree);
185 exit(1);
186 }
187 vprintf("usrpt %x\nUsrptma %x\nfirstfree %x\nmaxfree %x\nfreemem %x\n",
188 usrpt, Usrptma, firstfree, maxfree, freemem);
189 lseek(fcore, (long)clear(nl[X_PROC].n_value), 0);
190 if (read(fcore, (char *)proc, sizeof proc) != sizeof proc) {
191 perror("proc read");
192 exit(1);
193 }
194 lseek(fcore, (long)clear(nl[X_TEXT].n_value), 0);
195 if (read(fcore, (char *)text, sizeof text) != sizeof text) {
196 perror("text read");
197 exit(1);
198 }
199 i = (get(nl[X_ECMAP].n_value) - get(nl[X_CMAP].n_value));
200 cmap = (struct cmap *)calloc(i, 1);
201 if (cmap == NULL) {
202 fprintf(stderr, "not enough mem for %x bytes of cmap\n", i);
203 exit(1);
204 }
205 lseek(fcore, (long)clear(get(nl[X_CMAP].n_value)), 0);
206 if (read(fcore, (char *)cmap, i) != i) {
207 perror("cmap read");
208 exit(1);
209 }
210 lseek(fcore, (long)clear(nl[X_SWAPMAP].n_value), 0);
211 if (read(fcore, (char *)swapmap, sizeof swapmap) != sizeof swapmap) {
212 perror("swapmap read");
213 exit(1);
214 }
215 for (p = &proc[1]; p < &proc[NPROC]; p++) {
216 p->p_p0br = (struct pte *)clear(p->p_p0br);
217 p->p_addr = (struct pte *)clear(p->p_addr);
218 if (p->p_stat == 0)
219 continue;
220 printf("proc %d ", p->p_pid);
221 if (p->p_stat == SZOMB) {
222 printf("zombie\n");
223 continue;
224 }
225 if (p->p_flag & SLOAD) {
226 printf("loaded, p0br %x, ", p->p_p0br);
227 printf("%d pages of page tables:", p->p_szpt);
228 a = btokmx(p->p_p0br);
229 for (i = 0; i < p->p_szpt; i++) {
230 w = get(&Usrptma[a + i]);
231 printf(" %x", w & PG_PFNUM);
232 }
233 printf("\n");
234 for(i = 0; i < p->p_szpt; i++) {
235 w = get(&Usrptma[a + i]);
236 if (getpt(w, i))
237 count(p, (struct pte *)&w, ZPAGET);
238 }
239 } else {
240 /* i = ctopt(btoc(u.u_exdata.ux_dsize)); */
241 i = clrnd(ctopt(p->p_tsize + p->p_dsize + p->p_ssize));
242 printf("swapped, swaddr %x\n", p->p_swaddr);
243 duse(p->p_swaddr, clrnd(ctod(UPAGES)), DUDOT, p - proc);
244 duse(p->p_swaddr + ctod(UPAGES),
245 clrnd(i - p->p_tsize / NPTEPG), DPAGET, p - proc);
246 /* i, DPAGET, p - proc); */
247 }
248 p->p_p0br = (struct pte *)p0br;
249 p->p_addr = uaddr(p);
250 p->p_textp = &text[p->p_textp - (struct text *)nl[X_TEXT].n_value];
251 if (p->p_pid == 2)
252 continue;
253 if (getu(p))
254 continue;
255 u.u_procp = p;
256 pdmap();
257 if ((p->p_flag & SLOAD) == 0)
258 continue;
259 pid = p->p_pid;
260 for (i = 0; i < p->p_tsize; i++) {
261 pte = tptopte(p, i);
262 if (pte->pg_fod || pte->pg_pfnum == 0)
263 continue;
264 if (pte->pg_pfnum >= firstfree && pte->pg_pfnum < maxfree && cmap[pgtocm(pte->pg_pfnum)].c_intrans)
265 count(p, pte, ZINTRAN);
266 else
267 count(p, pte, ZTEXT);
268 }
269 vprintf("\n");
270 for (i = 0; i < p->p_dsize; i++) {
271 pte = dptopte(p, i);
272 if (pte->pg_fod || pte->pg_pfnum == 0)
273 continue;
274 if (pte->pg_pfnum >= firstfree && pte->pg_pfnum < maxfree && cmap[pgtocm(pte->pg_pfnum)].c_intrans)
275 count(p, pte, ZINTRAN);
276 else
277 count(p, pte, ZDATA);
278 }
279 vprintf("\n");
280 for (i = 0; i < p->p_ssize; i++) {
281 pte = sptopte(p, i);
282 if (pte->pg_fod || pte->pg_pfnum == 0)
283 continue;
284 if (pte->pg_pfnum >= firstfree && pte->pg_pfnum < maxfree && cmap[pgtocm(pte->pg_pfnum)].c_intrans)
285 count(p, pte, ZINTRAN);
286 else
287 count(p, pte, ZSTACK);
288 }
289 vprintf("\n");
290 for (i = 0; i < UPAGES; i++)
291 count(p, &p->p_addr[i], ZUDOT);
292 vprintf("\n");
293 vprintf("\n");
294 }
295 for (xp = &text[0]; xp < &text[NTEXT]; xp++)
296 if (xp->x_iptr) {
297 for (i = 0; i < xp->x_size; i += DMTEXT)
298 duse(xp->x_daddr[i],
299 (xp->x_size - i) > DMTEXT
300 ? DMTEXT : xp->x_size - i,
301 DTEXT, xp - text);
302 if (xp->x_flag & XPAGI)
303 duse(xp->x_ptdaddr, clrnd(ctopt(xp->x_size)),
304 DTEXT, xp - text);
305 }
306 dmcheck();
307 fixfree();
308 summary();
309 exit(0);
310}
311
312pdmap()
313{
314 register struct text *xp;
315
316 if (fswap == -1 && (u.u_procp->p_flag & SLOAD) == 0)
317 return;
318 if (Dflg)
319 printf("disk for pid %d", u.u_procp->p_pid);
320 if ((xp = u.u_procp->p_textp) && Dflg)
321 ptdmap(xp->x_daddr, xp->x_size);
322 pdmseg("data", &u.u_dmap, DDATA);
323 pdmseg("stack", &u.u_smap, DSTACK);
324 if (Dflg)
325 printf("\n");
326}
327
328ptdmap(dp, size)
329 register daddr_t *dp;
330 int size;
331{
332 register int i;
333 int rem;
334
335 if (Dflg)
336 printf(" text:");
337 for (i = 0, rem = size; rem > 0; i++) {
338 if (Dflg)
339 printf(" %x<%x>", dp[i], rem < DMTEXT ? rem : DMTEXT);
340 rem -= rem < DMTEXT ? rem : DMTEXT;
341 }
342}
343
344pdmseg(cp, dmp, type)
345 char *cp;
346 struct dmap *dmp;
347{
348 register int i;
349 int b, rem;
350
351 if (Dflg)
352 printf(", %s:", cp);
353 b = DMMIN;
354 for (i = 0, rem = dmp->dm_size; rem > 0; i++) {
355 if (Dflg)
356 printf(" %x<%x>", dmp->dm_map[i], rem < b ? rem : b);
357 duse(dmp->dm_map[i], b, type, u.u_procp - proc);
358 rem -= b;
359 if (b < DMMAX)
360 b *= 2;
361 }
362}
363
364duse(first, size, type, index)
365{
366 register struct dblks *dp;
367
368 if (fswap == -1)
369 return;
370 dp = &dblks[ndblks];
371 if (++ndblks > NDBLKS) {
372 fprintf(stderr, "too many disk blocks, increase NDBLKS\n");
373 exit(1);
374 }
375 dp->d_first = first;
376 dp->d_size = size;
377 dp->d_type = type;
378 dp->d_index = index;
379}
380
381dsort(d, e)
382 register struct dblks *d, *e;
383{
384
385 return (e->d_first - d->d_first);
386}
387
388dmcheck()
389{
390 register struct map *smp;
391 register struct dblks *d, *e;
392
393 for (smp = swapmap; smp->m_size; smp++)
394 duse(smp->m_addr, smp->m_size, DFREE, 0);
395 duse(CLSIZE, DMTEXT - CLSIZE, DFREE, 0);
396 qsort(dblks, ndblks, sizeof (struct dblks), dsort);
397 d = &dblks[ndblks - 1];
398 if (d->d_first > 1)
399 printf("lost swap map: start %x size %x\n", 1, d->d_first);
400 for (; d > dblks; d--) {
401 if (dflg)
402 dprint(d);
403 e = d - 1;
404 if (d->d_first + d->d_size > e->d_first) {
405 printf("overlap in swap mappings:\n");
406 dprint(d);
407 dprint(e);
408 } else if (d->d_first + d->d_size < e->d_first) {
409 printf("lost swap map: start %x size %x\n",
410 d->d_first + d->d_size,
411 e->d_first - (d->d_first + d->d_size));
412 }
413 }
414 if (dflg)
415 dprint(dblks);
416 if (sflg)
417 printf("swap space ends at %x\n", d->d_first + d->d_size);
418}
419
420char *dnames[] = {
421 "DFREE",
422 "DDATA",
423 "DSTACK",
424 "DTEXT",
425 "DUDOT",
426 "DPAGET",
427};
428
429dprint(d)
430 register struct dblks *d;
431{
432
433 printf("at %4x size %4x type %s", d->d_first, d->d_size,
434 dnames[d->d_type]);
435 switch (d->d_type) {
436
437 case DSTACK:
438 case DDATA:
439 printf(" pid %d", proc[d->d_index].p_pid);
440 break;
441 }
442 printf("\n");
443}
444
445getpt(x, i)
446 int x, i;
447{
448
449 lseek(fcore, (long)ctob((x & PG_PFNUM)), 0);
450 if (read(fcore, (char *)(p0br[i]), NBPG) != NBPG) {
451 perror("read");
452 fprintf(stderr, "getpt error reading frame %x\n", clear(x));
453 return (0);
454 }
455 return (1);
456}
457
458checkpg(p, pte, type)
459 register struct pte *pte;
460 register struct proc *p;
461 int type;
462{
463 char corepg[NBPG], swapg[NBPG];
464 register int i, count, dblock;
465 register int pfnum = pte->pg_pfnum;
466
467 if (type == ZPAGET || type == ZUDOT)
468 return (0);
469 lseek(fcore, (long)(NBPG * pfnum), 0);
470 if (read(fcore, corepg, NBPG) != NBPG){
471 perror("read");
472 fprintf(stderr, "Error reading core page %x\n", pfnum);
473 return (0);
474 }
475 switch (type) {
476
477 case ZDATA:
478 if (ptetodp(p, pte) >= u.u_dmap.dm_size)
479 return (0);
480 break;
481
482 case ZTEXT:
483 break;
484
485 case ZSTACK:
486 if (ptetosp(p, pte) >= u.u_smap.dm_size)
487 return (0);
488 break;
489
490 default:
491 return(0);
492 break;
493 }
494 dblock = vtod(p, ptetov(p, pte), &u.u_dmap, &u.u_smap);
495 vprintf(" %x", dblock);
496 if (pte->pg_fod || pte->pg_pfnum == 0)
497 return (0);
498 if (cmap[pgtocm(pte->pg_pfnum)].c_intrans || pte->pg_m || pte->pg_swapm)
499 return (0);
500 lseek(fswap, (long)(NBPG * dblock), 0);
501 if (read(fswap, swapg, NBPG) != NBPG) {
502 fprintf(stderr,"swap page %x: ", dblock);
503 perror("read");
504 }
505 count = 0;
506 for (i = 0; i < NBPG; i++)
507 if (corepg[i] != swapg[i])
508 count++;
509 if (count == 0)
510 vprintf("\tsame");
511 return (count);
512}
513
514getu(p)
515 register struct proc *p;
516{
517 int i, w, cc, errs = 0;
518
519 for (i = 0; i < UPAGES; i++) {
520 if (p->p_flag & SLOAD) {
521 lseek(fcore, ctob(p->p_addr[i].pg_pfnum), 0);
522 if (read(fcore, u_area.buf[i], NBPG) != NBPG)
523 perror("core u. read"), errs++;
524 } else if (fswap >= 0) {
525 lseek(fswap, (long)(NBPG * (p->p_swaddr+i)), 0);
526 if (read(fswap, u_area.buf[i], NBPG) != NBPG)
527 perror("swap u. read"), errs++;
528 }
529 }
530 return (errs);
531}
532
533char *typepg[] = {
534 "lost",
535 "data",
536 "stack",
537 "udot",
538 "paget",
539 "text",
540 "free",
541 "intransit",
542};
543
544count(p, pte, type)
545 struct proc *p;
546 register struct pte *pte;
547 int type;
548{
549 register int pfnum = pte->pg_pfnum;
550 register struct paginfo *zp = &paginfo[pfnum];
551 int ndif;
552#define zprintf if (type==ZINTRAN || vflg) printf
553
554 if (type == ZINTRAN && pfnum == 0)
555 return;
556 zprintf("page %x %s", pfnum, typepg[type]);
557 if (sflg == 0 || (ndif = checkpg(p, pte, type)) == 0) {
558 zprintf("\n");
559 } else {
560 if (vflg == 0 && type != ZINTRAN)
561 printf("page %x %s,", pfnum, typepg[type]);
562 printf(" %d bytes differ\n",ndif);
563 }
564 if (pfnum < firstfree || pfnum > maxfree) {
565 printf("page number out of range:\n");
566 printf("\tpage %x type %s pid %d\n", pfnum, typepg[type], pid);
567 return;
568 }
569 if (bad(zp, type)) {
570 printf("dup page pte %x", *(int *)pte);
571 dumpcm("", pte->pg_pfnum);
572 dump(zp);
573 printf("pte %x and as %s in pid %d\n", zp->z_pte, typepg[type], pid);
574 return;
575 }
576 zp->z_type = type;
577 zp->z_count++;
578 zp->z_pid = pid;
579 zp->z_pte = *pte;
580}
581
582bad(zp, type)
583 struct paginfo *zp;
584{
585 if (type == ZTEXT) {
586 if (zp->z_type != 0 && zp->z_type != ZTEXT)
587 return (1);
588 return (0);
589 }
590 return (zp->z_count);
591}
592
593dump(zp)
594 struct paginfo *zp;
595{
596
597 printf("page %x type %s pid %d ", zp - paginfo, typepg[zp->z_type], zp->z_pid);
598}
599
600summary()
601{
602 register int i;
603 register struct paginfo *zp;
604 register int pfnum;
605
606 for (i = firstfree + UPAGES; i < maxfree; i++) {
607 zp = &paginfo[i];
608 if (zp->z_type == ZLOST)
609 dumpcm("lost", i);
610 pfnum = pgtocm(i);
611 if (cmap[pfnum].c_lock && cmap[pfnum].c_type != CSYS)
612 dumpcm("locked", i);
613 if (mflg)
614 dumpcm("mem", i);
615 }
616}
617
618char *tynames[] = {
619 "sys",
620 "text",
621 "data",
622 "stack"
623};
624dumpcm(cp, pg)
625 char *cp;
626 int pg;
627{
628 int pslot;
629 int cm;
630 register struct cmap *c;
631
632 printf("%s page %x ", cp, pg);
633 cm = pgtocm(pg);
634 c = &cmap[cm];
635 printf("\t[%x, %x", c->c_page, c->c_ndx);
636 if (c->c_type != CTEXT)
637 printf(" (=pid %d)", proc[c->c_ndx].p_pid);
638 else {
639 pslot=(text[c->c_ndx].x_caddr - (struct proc *)nl[X_PROC].n_value);
640 printf(" (=pid");
641 for(;;) {
642 printf(" %d", proc[pslot].p_pid);
643 if (proc[pslot].p_xlink == 0)
644 break;
645 pslot=(proc[pslot].p_xlink - (struct proc *)nl[X_PROC].n_value);
646 }
647 printf(")");
648 }
649 printf("] ");
650 printf(tynames[c->c_type]);
651 if (c->c_free)
652 printf(" free");
653 if (c->c_gone)
654 printf(" gone");
655 if (c->c_lock)
656 printf(" lock");
657 if (c->c_want)
658 printf(" want");
659 if (c->c_intrans)
660 printf(" intrans");
661 if (c->c_blkno)
662 printf(" blkno %x mdev %d", c->c_blkno, c->c_mdev);
663 printf("\n");
664}
665
666fixfree()
667{
668 register int i, next, prev;
669
670 next = CMHEAD;
671 for (i=freemem/CLSIZE; --i >=0; ) {
672 prev = next;
673 next = cmap[next].c_next;
674 if (cmap[next].c_free == 0) {
675 printf("link to non free block: in %x to %x\n", cmtopg(prev), cmtopg(next));
676 dumpcm("bad free link in", cmtopg(prev));
677 dumpcm("to non free block", cmtopg(next));
678 }
679 if (cmtopg(next) > maxfree) {
680 printf("free list link out of range: in %x to %x\n", cmtopg(prev), cmtopg(next));
681 dumpcm("bad link in", cmtopg(prev));
682 }
683 paginfo[cmtopg(next)].z_type = ZFREE;
684 if (fflg)
685 dumpcm("free", cmtopg(next));
686 paginfo[cmtopg(next)+1].z_type = ZFREE;
687 if (fflg)
688 dumpcm("free", cmtopg(next)+1);
689 }
690}
691
692get(loc)
693unsigned loc;
694{
695 int x;
696
697 lseek(fcore, (long)clear(loc), 0);
698 if (read(fcore, (char *)&x, sizeof (int)) != sizeof (int)) {
699 perror("read");
700 fprintf(stderr, "get failed on %x\n", clear(loc));
701 return (0);
702 }
703 return (x);
704}
705/*
706 * Convert a virtual page number
707 * to its corresponding disk block number.
708 * Used in pagein/pageout to initiate single page transfers.
709 */
710vtod(p, v, dmap, smap)
711 register struct proc *p;
712 register struct dmap *dmap, *smap;
713{
714 struct dblock db;
715
716 if (v < p->p_tsize)
717 return(p->p_textp->x_daddr[v / DMTEXT] + v % DMTEXT);
718 if (isassv(p, v))
719 vstodb(vtosp(p, v), 1, smap, &db, 1);
720 else
721 vstodb(vtodp(p, v), 1, dmap, &db, 0);
722 return (db.db_base);
723}
724
725/*
726 * Convert a pte pointer to
727 * a virtual page number.
728 */
729ptetov(p, pte)
730 register struct proc *p;
731 register struct pte *pte;
732{
733
734 if (isatpte(p, pte))
735 return (tptov(p, ptetotp(p, pte)));
736 else if (isadpte(p, pte))
737 return (dptov(p, ptetodp(p, pte)));
738 else
739 return (sptov(p, ptetosp(p, pte)));
740}
741
742/*
743 * Given a base/size pair in virtual swap area,
744 * return a physical base/size pair which is the
745 * (largest) initial, physically contiguous block.
746 */
747vstodb(vsbase, vssize, dmp, dbp, rev)
748 register int vsbase;
749 int vssize;
750 register struct dmap *dmp;
751 register struct dblock *dbp;
752{
753 register int blk = DMMIN;
754 register swblk_t *ip = dmp->dm_map;
755
756 if (vsbase < 0 || vsbase + vssize > dmp->dm_size)
757 panic("vstodb");
758 while (vsbase >= blk) {
759 vsbase -= blk;
760 if (blk < DMMAX)
761 blk *= 2;
762 ip++;
763 }
764 dbp->db_size = min(vssize, blk - vsbase);
765 dbp->db_base = *ip + (rev ? blk - (vsbase + vssize) : vsbase);
766}
767
768panic(cp)
769 char *cp;
770{
771 printf("panic!: %s\n", cp);
772}
773
774min(a, b)
775{
776 return (a < b ? a : b);
777}