1 static char *sccsid = "@(#)analyze.c 4.1 (Berkeley) 10/01/80"; 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 19 int Dflg; 20 int dflg; 21 int vflg; 22 int mflg; 23 int fflg; 24 int 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 31 struct proc proc[NPROC]; 32 struct text text[NTEXT]; 33 struct map swapmap[SMAPSIZ]; 34 struct cmap *cmap; 35 struct pte *usrpt; 36 struct pte *Usrptma; 37 int firstfree; 38 int maxfree; 39 int freemem; 40 struct pte p0br[ctopt(MAXTSIZ+MAXDSIZ+MAXSSIZ)][NPTEPG]; 41 int pid; 42 43 struct 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) 59 struct dblks { 60 short d_first; 61 short d_size; 62 char d_type; 63 char d_index; 64 } dblks[NDBLKS]; 65 int 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 74 union { 75 char buf[UPAGES][512]; 76 struct user U; 77 } u_area; 78 #define u u_area.U 79 80 int fcore = -1; 81 int fswap = -1; 82 83 struct 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 107 main(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) { 160 usage: 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 312 pdmap() 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 328 ptdmap(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 344 pdmseg(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 364 duse(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 381 dsort(d, e) 382 register struct dblks *d, *e; 383 { 384 385 return (e->d_first - d->d_first); 386 } 387 388 dmcheck() 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 420 char *dnames[] = { 421 "DFREE", 422 "DDATA", 423 "DSTACK", 424 "DTEXT", 425 "DUDOT", 426 "DPAGET", 427 }; 428 429 dprint(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 445 getpt(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 458 checkpg(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 514 getu(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 533 char *typepg[] = { 534 "lost", 535 "data", 536 "stack", 537 "udot", 538 "paget", 539 "text", 540 "free", 541 "intransit", 542 }; 543 544 count(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 582 bad(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 593 dump(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 600 summary() 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 618 char *tynames[] = { 619 "sys", 620 "text", 621 "data", 622 "stack" 623 }; 624 dumpcm(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 666 fixfree() 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 692 get(loc) 693 unsigned 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 */ 710 vtod(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 */ 729 ptetov(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 */ 747 vstodb(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 768 panic(cp) 769 char *cp; 770 { 771 printf("panic!: %s\n", cp); 772 } 773 774 min(a, b) 775 { 776 return (a < b ? a : b); 777 } 778