|
|
1.1 ! root 1: /* Copyright (c) 1982 Regents of the University of California */ ! 2: ! 3: static char sccsid[] = "@(#)events.c 1.3 4/8/83"; ! 4: ! 5: /* ! 6: * Event/breakpoint managment. ! 7: */ ! 8: ! 9: #include "defs.h" ! 10: #include "events.h" ! 11: #include "main.h" ! 12: #include "symbols.h" ! 13: #include "tree.h" ! 14: #include "eval.h" ! 15: #include "source.h" ! 16: #include "mappings.h" ! 17: #include "process.h" ! 18: #include "machine.h" ! 19: #include "lists.h" ! 20: ! 21: #ifndef public ! 22: typedef struct Event *Event; ! 23: typedef struct Breakpoint *Breakpoint; ! 24: ! 25: Boolean inst_tracing; ! 26: Boolean single_stepping; ! 27: Boolean isstopped; ! 28: ! 29: #include "symbols.h" ! 30: ! 31: Symbol linesym; ! 32: Symbol procsym; ! 33: Symbol pcsym; ! 34: Symbol retaddrsym; ! 35: ! 36: #define addevent(cond, cmdlist) event_alloc(false, cond, cmdlist) ! 37: #define event_once(cond, cmdlist) event_alloc(true, cond, cmdlist) ! 38: ! 39: #endif ! 40: ! 41: struct Event { ! 42: unsigned int id; ! 43: Boolean temporary; ! 44: Node condition; ! 45: Cmdlist actions; ! 46: }; ! 47: ! 48: struct Breakpoint { ! 49: Event event; ! 50: Address bpaddr; ! 51: Lineno bpline; ! 52: Cmdlist actions; ! 53: }; ! 54: ! 55: typedef List Eventlist; ! 56: typedef List Bplist; ! 57: ! 58: #define eventlist_append(event, el) list_append(list_item(event), nil, el) ! 59: #define bplist_append(bp, bl) list_append(list_item(bp), nil, bl) ! 60: ! 61: private Eventlist eventlist; /* list of active events */ ! 62: private Bplist bplist; /* list of active breakpoints */ ! 63: private Integer eventid; /* id number of next allocated event */ ! 64: private Integer trid; /* id number of next allocated trace */ ! 65: ! 66: typedef struct Trcmd { ! 67: Integer trid; ! 68: Event event; ! 69: Cmdlist cmdlist; ! 70: } *Trcmd; ! 71: ! 72: private List eachline; /* commands to execute after each line */ ! 73: private List eachinst; /* commands to execute after each instruction */ ! 74: ! 75: private Breakpoint bp_alloc(); ! 76: ! 77: /* ! 78: * Initialize breakpoint information. ! 79: */ ! 80: ! 81: private Symbol builtinsym(str, class, type) ! 82: String str; ! 83: Symclass class; ! 84: Symbol type; ! 85: { ! 86: Symbol s; ! 87: ! 88: s = insert(identname(str, true)); ! 89: s->language = findlanguage(".s"); ! 90: s->class = class; ! 91: s->type = type; ! 92: return s; ! 93: } ! 94: ! 95: public bpinit() ! 96: { ! 97: linesym = builtinsym("$line", VAR, t_int); ! 98: procsym = builtinsym("$proc", PROC, nil); ! 99: pcsym = lookup(identname("$pc", true)); ! 100: if (pcsym == nil) { ! 101: panic("can't find $pc"); ! 102: } ! 103: retaddrsym = builtinsym("$retaddr", VAR, t_int); ! 104: eventlist = list_alloc(); ! 105: bplist = list_alloc(); ! 106: eachline = list_alloc(); ! 107: eachinst = list_alloc(); ! 108: } ! 109: ! 110: /* ! 111: * Trap an event and do the associated commands when it occurs. ! 112: */ ! 113: ! 114: public Event event_alloc(istmp, econd, cmdlist) ! 115: Boolean istmp; ! 116: Node econd; ! 117: Cmdlist cmdlist; ! 118: { ! 119: register Event e; ! 120: ! 121: e = new(Event); ! 122: ++eventid; ! 123: e->id = eventid; ! 124: e->temporary = istmp; ! 125: e->condition = econd; ! 126: e->actions = cmdlist; ! 127: eventlist_append(e, eventlist); ! 128: translate(e); ! 129: return e; ! 130: } ! 131: ! 132: /* ! 133: * Delete the event with the given id. ! 134: */ ! 135: ! 136: public delevent(id) ! 137: unsigned int id; ! 138: { ! 139: Event e; ! 140: Breakpoint bp; ! 141: Trcmd t; ! 142: ! 143: foreach (Event, e, eventlist) ! 144: if (e->id == id) { ! 145: list_delete(list_curitem(eventlist), eventlist); ! 146: foreach (Breakpoint, bp, bplist) ! 147: if (bp->event == e) { ! 148: list_delete(list_curitem(bplist), bplist); ! 149: } ! 150: endfor ! 151: break; ! 152: } ! 153: endfor ! 154: foreach (Trcmd, t, eachline) ! 155: if (t->event->id == id) { ! 156: printrmtr(t); ! 157: list_delete(list_curitem(eachline), eachline); ! 158: } ! 159: endfor ! 160: foreach (Trcmd, t, eachinst) ! 161: if (t->event->id == id) { ! 162: printrmtr(t); ! 163: list_delete(list_curitem(eachinst), eachinst); ! 164: } ! 165: endfor ! 166: if (list_size(eachinst) == 0) { ! 167: inst_tracing = false; ! 168: if (list_size(eachline) == 0) { ! 169: single_stepping = false; ! 170: } ! 171: } ! 172: } ! 173: ! 174: /* ! 175: * Translate an event into the appropriate breakpoints and actions. ! 176: * While we're at it, turn on the breakpoints if the condition is true. ! 177: */ ! 178: ! 179: private translate(e) ! 180: Event e; ! 181: { ! 182: Breakpoint bp; ! 183: Symbol s; ! 184: Node place; ! 185: Lineno line; ! 186: Address addr; ! 187: ! 188: checkref(e->condition); ! 189: switch (e->condition->op) { ! 190: case O_EQ: ! 191: if (e->condition->value.arg[0]->op == O_SYM) { ! 192: s = e->condition->value.arg[0]->value.sym; ! 193: place = e->condition->value.arg[1]; ! 194: if (s == linesym) { ! 195: if (place->op == O_QLINE) { ! 196: line = place->value.arg[1]->value.lcon; ! 197: addr = objaddr(line, ! 198: place->value.arg[0]->value.scon); ! 199: } else { ! 200: eval(place); ! 201: line = pop(long); ! 202: addr = objaddr(line, cursource); ! 203: } ! 204: if (addr == NOADDR) { ! 205: delevent(e->id); ! 206: beginerrmsg(); ! 207: fprintf(stderr, "no executable code at line "); ! 208: prtree(stderr, place); ! 209: enderrmsg(); ! 210: } ! 211: bp = bp_alloc(e, addr, line, e->actions); ! 212: } else if (s == procsym) { ! 213: eval(place); ! 214: s = pop(Symbol); ! 215: bp = bp_alloc(e, codeloc(s), 0, e->actions); ! 216: if (isactive(s) and pc != codeloc(program)) { ! 217: evalcmdlist(e->actions); ! 218: } ! 219: } else if (s == pcsym) { ! 220: eval(place); ! 221: bp = bp_alloc(e, pop(Address), 0, e->actions); ! 222: } else { ! 223: condbp(e); ! 224: } ! 225: } else { ! 226: condbp(e); ! 227: } ! 228: break; ! 229: ! 230: /* ! 231: * These should be handled specially. ! 232: * But for now I'm ignoring the problem. ! 233: */ ! 234: case O_AND: ! 235: case O_OR: ! 236: default: ! 237: condbp(e); ! 238: break; ! 239: } ! 240: } ! 241: ! 242: /* ! 243: * Create a breakpoint for a condition that cannot be pinpointed ! 244: * to happening at a particular address, but one for which we ! 245: * must single step and check the condition after each statement. ! 246: */ ! 247: ! 248: private condbp(e) ! 249: Event e; ! 250: { ! 251: Symbol p; ! 252: Breakpoint bp; ! 253: Cmdlist actions; ! 254: ! 255: p = tcontainer(e->condition); ! 256: if (p == nil) { ! 257: p = program; ! 258: } ! 259: actions = buildcmdlist(build(O_IF, e->condition, e->actions)); ! 260: actions = buildcmdlist(build(O_TRACEON, false, actions)); ! 261: bp = bp_alloc(e, codeloc(p), 0, actions); ! 262: } ! 263: ! 264: /* ! 265: * Determine the deepest nested subprogram that still contains ! 266: * all elements in the given expression. ! 267: */ ! 268: ! 269: public Symbol tcontainer(exp) ! 270: Node exp; ! 271: { ! 272: Integer i; ! 273: Symbol s, t, u, v; ! 274: ! 275: checkref(exp); ! 276: s = nil; ! 277: if (exp->op == O_SYM) { ! 278: s = container(exp->value.sym); ! 279: } else if (not isleaf(exp->op)) { ! 280: for (i = 0; i < nargs(exp->op); i++) { ! 281: t = tcontainer(exp->value.arg[i]); ! 282: if (t != nil) { ! 283: if (s == nil) { ! 284: s = t; ! 285: } else { ! 286: u = s; ! 287: v = t; ! 288: while (u != v and u != nil) { ! 289: u = container(u); ! 290: v = container(v); ! 291: } ! 292: if (u == nil) { ! 293: panic("bad ancestry for \"%s\"", symname(s)); ! 294: } else { ! 295: s = u; ! 296: } ! 297: } ! 298: } ! 299: } ! 300: } ! 301: return s; ! 302: } ! 303: ! 304: /* ! 305: * Determine if the given function can be executed at full speed. ! 306: * This can only be done if there are no breakpoints within the function. ! 307: */ ! 308: ! 309: public Boolean canskip(f) ! 310: Symbol f; ! 311: { ! 312: Breakpoint p; ! 313: Boolean ok; ! 314: ! 315: ok = true; ! 316: foreach (Breakpoint, p, bplist) ! 317: if (whatblock(p->bpaddr) == f) { ! 318: ok = false; ! 319: break; ! 320: } ! 321: endfor ! 322: return ok; ! 323: } ! 324: ! 325: /* ! 326: * Print out what's currently being traced by looking at ! 327: * the currently active events. ! 328: * ! 329: * Some convolution here to translate internal representation ! 330: * of events back into something more palatable. ! 331: */ ! 332: ! 333: public status() ! 334: { ! 335: Event e; ! 336: ! 337: foreach (Event, e, eventlist) ! 338: if (not e->temporary) { ! 339: printevent(e); ! 340: } ! 341: endfor ! 342: } ! 343: ! 344: public printevent(e) ! 345: Event e; ! 346: { ! 347: Command cmd; ! 348: ! 349: if (not isredirected()) { ! 350: printf("(%d) ", e->id); ! 351: } ! 352: cmd = list_element(Command, list_head(e->actions)); ! 353: if (cmd->op == O_PRINTCALL) { ! 354: printf("trace "); ! 355: printname(stdout, cmd->value.sym); ! 356: } else { ! 357: if (list_size(e->actions) > 1) { ! 358: printf("{ "); ! 359: } ! 360: foreach (Command, cmd, e->actions) ! 361: printcmd(stdout, cmd); ! 362: if (not list_islast()) { ! 363: printf("; "); ! 364: } ! 365: endfor ! 366: if (list_size(e->actions) > 1) { ! 367: printf(" }"); ! 368: } ! 369: printcond(e->condition); ! 370: } ! 371: printf("\n"); ! 372: } ! 373: ! 374: /* ! 375: * Print out a condition. ! 376: */ ! 377: ! 378: private printcond(cond) ! 379: Node cond; ! 380: { ! 381: Symbol s; ! 382: Node place; ! 383: ! 384: if (cond->op == O_EQ and cond->value.arg[0]->op == O_SYM) { ! 385: s = cond->value.arg[0]->value.sym; ! 386: place = cond->value.arg[1]; ! 387: if (s == procsym) { ! 388: if (place->value.sym != program) { ! 389: printf(" in "); ! 390: printname(stdout, place->value.sym); ! 391: } ! 392: } else if (s == linesym) { ! 393: printf(" at "); ! 394: prtree(stdout, place); ! 395: } else if (s == pcsym or s == retaddrsym) { ! 396: printf("i at "); ! 397: prtree(stdout, place); ! 398: } else { ! 399: printf(" when "); ! 400: prtree(stdout, cond); ! 401: } ! 402: } else { ! 403: printf(" when "); ! 404: prtree(stdout, cond); ! 405: } ! 406: } ! 407: ! 408: /* ! 409: * Add a breakpoint to the list and return it. ! 410: */ ! 411: ! 412: private Breakpoint bp_alloc(e, addr, line, actions) ! 413: Event e; ! 414: Address addr; ! 415: Lineno line; ! 416: Cmdlist actions; ! 417: { ! 418: register Breakpoint p; ! 419: ! 420: p = new(Breakpoint); ! 421: p->event = e; ! 422: p->bpaddr = addr; ! 423: p->bpline = line; ! 424: p->actions = actions; ! 425: if (tracebpts) { ! 426: printf("new bp at 0x%x\n", addr); ! 427: fflush(stdout); ! 428: } ! 429: bplist_append(p, bplist); ! 430: return p; ! 431: } ! 432: ! 433: /* ! 434: * Free all storage in the event and breakpoint tables. ! 435: */ ! 436: ! 437: public bpfree() ! 438: { ! 439: register Event e; ! 440: ! 441: fixbps(); ! 442: foreach (Event, e, eventlist) ! 443: delevent(e->id); ! 444: list_delete(list_curitem(eventlist), eventlist); ! 445: endfor ! 446: } ! 447: ! 448: /* ! 449: * Determine if the program stopped at a known breakpoint ! 450: * and if so do the associated commands. ! 451: */ ! 452: ! 453: public Boolean bpact() ! 454: { ! 455: register Breakpoint p; ! 456: Boolean found; ! 457: ! 458: found = false; ! 459: foreach (Breakpoint, p, bplist) ! 460: if (p->bpaddr == pc) { ! 461: if (tracebpts) { ! 462: printf("breakpoint found at location 0x%x\n", pc); ! 463: } ! 464: found = true; ! 465: if (p->event->temporary) { ! 466: delevent(p->event->id); ! 467: } ! 468: evalcmdlist(p->actions); ! 469: } ! 470: endfor ! 471: if (isstopped) { ! 472: printstatus(); ! 473: } ! 474: fflush(stdout); ! 475: return found; ! 476: } ! 477: ! 478: /* ! 479: * Begin single stepping and executing the given commands after each step. ! 480: * If the first argument is true step by instructions, otherwise ! 481: * step by source lines. ! 482: * ! 483: * We automatically set a breakpoint at the end of the current procedure ! 484: * to turn off the given tracing. ! 485: */ ! 486: ! 487: public traceon(inst, event, cmdlist) ! 488: Boolean inst; ! 489: Event event; ! 490: Cmdlist cmdlist; ! 491: { ! 492: register Trcmd trcmd; ! 493: Breakpoint bp; ! 494: Node until; ! 495: Cmdlist actions; ! 496: Address ret; ! 497: ! 498: trcmd = new(Trcmd); ! 499: ++trid; ! 500: trcmd->trid = trid; ! 501: trcmd->event = event; ! 502: trcmd->cmdlist = cmdlist; ! 503: single_stepping = true; ! 504: if (inst) { ! 505: inst_tracing = true; ! 506: list_append(list_item(trcmd), nil, eachinst); ! 507: } else { ! 508: list_append(list_item(trcmd), nil, eachline); ! 509: } ! 510: ret = return_addr(); ! 511: if (ret != 0) { ! 512: until = build(O_EQ, build(O_SYM, pcsym), build(O_LCON, ret)); ! 513: actions = buildcmdlist(build(O_TRACEOFF, trcmd->trid)); ! 514: event_once(until, actions); ! 515: } ! 516: if (tracebpts) { ! 517: printf("adding trace %d for event %d\n", trcmd->trid, event->id); ! 518: } ! 519: } ! 520: ! 521: /* ! 522: * Turn off some kind of tracing. ! 523: * Strictly an internal command, this cannot be invoked by the user. ! 524: */ ! 525: ! 526: public traceoff(id) ! 527: Integer id; ! 528: { ! 529: register Trcmd t; ! 530: register Boolean found; ! 531: ! 532: found = false; ! 533: foreach (Trcmd, t, eachline) ! 534: if (t->trid == id) { ! 535: printrmtr(t); ! 536: list_delete(list_curitem(eachline), eachline); ! 537: found = true; ! 538: break; ! 539: } ! 540: endfor ! 541: if (not found) { ! 542: foreach (Trcmd, t, eachinst) ! 543: if (t->event->id == id) { ! 544: printrmtr(t); ! 545: list_delete(list_curitem(eachinst), eachinst); ! 546: found = true; ! 547: break; ! 548: } ! 549: endfor ! 550: if (not found) { ! 551: panic("missing trid %d", id); ! 552: } ! 553: } ! 554: if (list_size(eachinst) == 0) { ! 555: inst_tracing = false; ! 556: if (list_size(eachline) == 0) { ! 557: single_stepping = false; ! 558: } ! 559: } ! 560: } ! 561: ! 562: /* ! 563: * If breakpoints are being traced, note that a Trcmd is being deleted. ! 564: */ ! 565: ! 566: private printrmtr(t) ! 567: Trcmd t; ! 568: { ! 569: if (tracebpts) { ! 570: printf("removing trace %d", t->trid); ! 571: if (t->event != nil) { ! 572: printf(" for event %d", t->event->id); ! 573: } ! 574: printf("\n"); ! 575: } ! 576: } ! 577: ! 578: /* ! 579: * Print out news during single step tracing. ! 580: */ ! 581: ! 582: public printnews() ! 583: { ! 584: register Trcmd t; ! 585: ! 586: foreach (Trcmd, t, eachline) ! 587: evalcmdlist(t->cmdlist); ! 588: endfor ! 589: foreach (Trcmd, t, eachinst) ! 590: evalcmdlist(t->cmdlist); ! 591: endfor ! 592: bpact(); ! 593: } ! 594: ! 595: /* ! 596: * A procedure call/return has occurred while single-stepping, ! 597: * note it if we're tracing lines. ! 598: */ ! 599: ! 600: private Boolean chklist(); ! 601: ! 602: public callnews(iscall) ! 603: Boolean iscall; ! 604: { ! 605: if (not chklist(eachline, iscall)) { ! 606: chklist(eachinst, iscall); ! 607: } ! 608: } ! 609: ! 610: private Boolean chklist(list, iscall) ! 611: List list; ! 612: Boolean iscall; ! 613: { ! 614: register Trcmd t; ! 615: register Command cmd; ! 616: ! 617: curfunc = whatblock(pc); ! 618: foreach (Trcmd, t, list) ! 619: foreach (Command, cmd, t->cmdlist) ! 620: if (cmd->op == O_PRINTSRCPOS and ! 621: (cmd->value.arg[0] == nil or cmd->value.arg[0]->op == O_QLINE)) { ! 622: if (iscall) { ! 623: printentry(curfunc); ! 624: } else { ! 625: printexit(curfunc); ! 626: } ! 627: return true; ! 628: } ! 629: endfor ! 630: endfor ! 631: return false; ! 632: } ! 633: ! 634: /* ! 635: * When tracing variables we keep a copy of their most recent value ! 636: * and compare it to the current one each time a breakpoint occurs. ! 637: * MAXTRSIZE is the maximum size variable we allow. ! 638: */ ! 639: ! 640: #define MAXTRSIZE 512 ! 641: ! 642: /* ! 643: * List of variables being watched. ! 644: */ ! 645: ! 646: typedef struct Trinfo *Trinfo; ! 647: ! 648: struct Trinfo { ! 649: Node variable; ! 650: Address traddr; ! 651: Symbol trblock; ! 652: char *trvalue; ! 653: }; ! 654: ! 655: private List trinfolist; ! 656: ! 657: /* ! 658: * Find the trace information record associated with the given record. ! 659: * If there isn't one then create it and add it to the list. ! 660: */ ! 661: ! 662: private Trinfo findtrinfo(p) ! 663: Node p; ! 664: { ! 665: register Trinfo tp; ! 666: Boolean isnew; ! 667: ! 668: isnew = true; ! 669: if (trinfolist == nil) { ! 670: trinfolist = list_alloc(); ! 671: } else { ! 672: foreach (Trinfo, tp, trinfolist) ! 673: if (tp->variable == p) { ! 674: isnew = false; ! 675: break; ! 676: } ! 677: endfor ! 678: } ! 679: if (isnew) { ! 680: if (tracebpts) { ! 681: printf("adding trinfo for \""); ! 682: prtree(stdout, p); ! 683: printf("\"\n"); ! 684: } ! 685: tp = new(Trinfo); ! 686: tp->variable = p; ! 687: tp->traddr = lval(p); ! 688: tp->trvalue = nil; ! 689: list_append(list_item(tp), nil, trinfolist); ! 690: } ! 691: return tp; ! 692: } ! 693: ! 694: /* ! 695: * Print out the value of a variable if it has changed since the ! 696: * last time we checked. ! 697: */ ! 698: ! 699: public printifchanged(p) ! 700: Node p; ! 701: { ! 702: register Trinfo tp; ! 703: register int n; ! 704: char buff[MAXTRSIZE]; ! 705: static Lineno prevline; ! 706: ! 707: tp = findtrinfo(p); ! 708: n = size(p->nodetype); ! 709: dread(buff, tp->traddr, n); ! 710: if (tp->trvalue == nil) { ! 711: tp->trvalue = newarr(char, n); ! 712: mov(buff, tp->trvalue, n); ! 713: mov(buff, sp, n); ! 714: sp += n; ! 715: printf("initially (at line %d):\t", curline); ! 716: prtree(stdout, p); ! 717: printf(" = "); ! 718: printval(p->nodetype); ! 719: putchar('\n'); ! 720: } else if (cmp(tp->trvalue, buff, n) != 0) { ! 721: mov(buff, tp->trvalue, n); ! 722: mov(buff, sp, n); ! 723: sp += n; ! 724: printf("after line %d:\t", prevline); ! 725: prtree(stdout, p); ! 726: printf(" = "); ! 727: printval(p->nodetype); ! 728: putchar('\n'); ! 729: } ! 730: prevline = curline; ! 731: } ! 732: ! 733: /* ! 734: * Stop if the value of the given expression has changed. ! 735: */ ! 736: ! 737: public stopifchanged(p) ! 738: Node p; ! 739: { ! 740: register Trinfo tp; ! 741: register int n; ! 742: char buff[MAXTRSIZE]; ! 743: static Lineno prevline; ! 744: ! 745: tp = findtrinfo(p); ! 746: n = size(p->nodetype); ! 747: dread(buff, tp->traddr, n); ! 748: if (tp->trvalue == nil) { ! 749: tp->trvalue = newarr(char, n); ! 750: mov(buff, tp->trvalue, n); ! 751: isstopped = true; ! 752: } else if (cmp(tp->trvalue, buff, n) != 0) { ! 753: mov(buff, tp->trvalue, n); ! 754: isstopped = true; ! 755: } ! 756: prevline = curline; ! 757: } ! 758: ! 759: /* ! 760: * Free the tracing table. ! 761: */ ! 762: ! 763: public trfree() ! 764: { ! 765: register Trinfo tp; ! 766: ! 767: foreach (Trinfo, tp, trinfolist) ! 768: dispose(tp->trvalue); ! 769: dispose(tp); ! 770: list_delete(list_curitem(trinfolist), trinfolist); ! 771: endfor ! 772: } ! 773: ! 774: /* ! 775: * Fix up breakpoint information before continuing execution. ! 776: * ! 777: * It's necessary to destroy events and breakpoints that were created ! 778: * temporarily and still exist because the program terminated abnormally. ! 779: */ ! 780: ! 781: public fixbps() ! 782: { ! 783: register Event e; ! 784: register Trcmd t; ! 785: ! 786: single_stepping = false; ! 787: inst_tracing = false; ! 788: trfree(); ! 789: foreach (Event, e, eventlist) ! 790: if (e->temporary) { ! 791: delevent(e->id); ! 792: } ! 793: endfor ! 794: foreach (Trcmd, t, eachline) ! 795: printrmtr(t); ! 796: list_delete(list_curitem(eachline), eachline); ! 797: endfor ! 798: foreach (Trcmd, t, eachinst) ! 799: printrmtr(t); ! 800: list_delete(list_curitem(eachinst), eachinst); ! 801: endfor ! 802: } ! 803: ! 804: /* ! 805: * Set all breakpoints in object code. ! 806: */ ! 807: ! 808: public setallbps() ! 809: { ! 810: register Breakpoint p; ! 811: ! 812: foreach (Breakpoint, p, bplist) ! 813: setbp(p->bpaddr); ! 814: endfor ! 815: } ! 816: ! 817: /* ! 818: * Undo damage done by "setallbps". ! 819: */ ! 820: ! 821: public unsetallbps() ! 822: { ! 823: register Breakpoint p; ! 824: ! 825: foreach (Breakpoint, p, bplist) ! 826: unsetbp(p->bpaddr); ! 827: endfor ! 828: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.