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