1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)events.c 1.3 04/08/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 } 829