1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)events.c 1.2 12/15/82"; 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 * Print out what's currently being traced by looking at 306 * the currently active events. 307 * 308 * Some convolution here to translate internal representation 309 * of events back into something more palatable. 310 */ 311 312 public status() 313 { 314 Event e; 315 Command cmd; 316 317 foreach (Event, e, eventlist) 318 if (not e->temporary) { 319 if (not isredirected()) { 320 printf("(%d) ", e->id); 321 } 322 cmd = list_element(Command, list_head(e->actions)); 323 if (cmd->op == O_PRINTCALL) { 324 printf("trace "); 325 printname(stdout, cmd->value.sym); 326 } else { 327 if (list_size(e->actions) > 1) { 328 printf("{ "); 329 } 330 foreach (Command, cmd, e->actions) 331 printcmd(stdout, cmd); 332 if (not list_islast()) { 333 printf("; "); 334 } 335 endfor 336 if (list_size(e->actions) > 1) { 337 printf(" }"); 338 } 339 printcond(e->condition); 340 } 341 printf("\n"); 342 } 343 endfor 344 } 345 346 /* 347 * Print out a condition. 348 */ 349 350 private printcond(cond) 351 Node cond; 352 { 353 Symbol s; 354 Node place; 355 356 if (cond->op == O_EQ and cond->value.arg[0]->op == O_SYM) { 357 s = cond->value.arg[0]->value.sym; 358 place = cond->value.arg[1]; 359 if (s == procsym) { 360 if (place->value.sym != program) { 361 printf(" in "); 362 printname(stdout, place->value.sym); 363 } 364 } else if (s == linesym) { 365 printf(" at "); 366 prtree(stdout, place); 367 } else if (s == pcsym or s == retaddrsym) { 368 printf("i at "); 369 prtree(stdout, place); 370 } else { 371 printf(" when "); 372 prtree(stdout, cond); 373 } 374 } else { 375 printf(" when "); 376 prtree(stdout, cond); 377 } 378 } 379 380 /* 381 * Add a breakpoint to the list and return it. 382 */ 383 384 private Breakpoint bp_alloc(e, addr, line, actions) 385 Event e; 386 Address addr; 387 Lineno line; 388 Cmdlist actions; 389 { 390 register Breakpoint p; 391 392 p = new(Breakpoint); 393 p->event = e; 394 p->bpaddr = addr; 395 p->bpline = line; 396 p->actions = actions; 397 if (tracebpts) { 398 printf("new bp at 0x%x\n", addr); 399 fflush(stdout); 400 } 401 bplist_append(p, bplist); 402 return p; 403 } 404 405 /* 406 * Free all storage in the event and breakpoint tables. 407 */ 408 409 public bpfree() 410 { 411 register Event e; 412 413 fixbps(); 414 foreach (Event, e, eventlist) 415 delevent(e->id); 416 list_delete(list_curitem(eventlist), eventlist); 417 endfor 418 } 419 420 /* 421 * Determine if the program stopped at a known breakpoint 422 * and if so do the associated commands. 423 */ 424 425 public Boolean bpact() 426 { 427 register Breakpoint p; 428 Boolean found; 429 430 found = false; 431 foreach (Breakpoint, p, bplist) 432 if (p->bpaddr == pc) { 433 if (tracebpts) { 434 printf("breakpoint found at location 0x%x\n", pc); 435 } 436 found = true; 437 if (p->event->temporary) { 438 delevent(p->event->id); 439 } 440 evalcmdlist(p->actions); 441 } 442 endfor 443 if (isstopped) { 444 printstatus(); 445 } 446 fflush(stdout); 447 return found; 448 } 449 450 /* 451 * Begin single stepping and executing the given commands after each step. 452 * If the first argument is true step by instructions, otherwise 453 * step by source lines. 454 * 455 * We automatically set a breakpoint at the end of the current procedure 456 * to turn off the given tracing. 457 */ 458 459 public traceon(inst, event, cmdlist) 460 Boolean inst; 461 Event event; 462 Cmdlist cmdlist; 463 { 464 register Trcmd trcmd; 465 Breakpoint bp; 466 Node until; 467 Cmdlist actions; 468 469 trcmd = new(Trcmd); 470 ++trid; 471 trcmd->trid = trid; 472 trcmd->event = event; 473 trcmd->cmdlist = cmdlist; 474 single_stepping = true; 475 if (inst) { 476 inst_tracing = true; 477 list_append(list_item(trcmd), nil, eachinst); 478 } else { 479 list_append(list_item(trcmd), nil, eachline); 480 } 481 until = build(O_EQ, build(O_SYM, pcsym), build(O_LCON, return_addr())); 482 actions = buildcmdlist(build(O_TRACEOFF, trcmd->trid)); 483 event_once(until, actions); 484 if (tracebpts) { 485 printf("adding trace %d for event %d\n", trcmd->trid, event->id); 486 } 487 } 488 489 /* 490 * Turn off some kind of tracing. 491 * Strictly an internal command, this cannot be invoked by the user. 492 */ 493 494 public traceoff(id) 495 Integer id; 496 { 497 register Trcmd t; 498 register Boolean found; 499 500 found = false; 501 foreach (Trcmd, t, eachline) 502 if (t->trid == id) { 503 printrmtr(t); 504 list_delete(list_curitem(eachline), eachline); 505 found = true; 506 break; 507 } 508 endfor 509 if (not found) { 510 foreach (Trcmd, t, eachinst) 511 if (t->event->id == id) { 512 printrmtr(t); 513 list_delete(list_curitem(eachinst), eachinst); 514 found = true; 515 break; 516 } 517 endfor 518 if (not found) { 519 panic("missing trid %d", id); 520 } 521 } 522 if (list_size(eachinst) == 0) { 523 inst_tracing = false; 524 if (list_size(eachline) == 0) { 525 single_stepping = false; 526 } 527 } 528 } 529 530 /* 531 * If breakpoints are being traced, note that a Trcmd is being deleted. 532 */ 533 534 private printrmtr(t) 535 Trcmd t; 536 { 537 if (tracebpts) { 538 printf("removing trace %d for event %d\n", t->trid, t->event->id); 539 } 540 } 541 542 /* 543 * Print out news during single step tracing. 544 */ 545 546 public printnews() 547 { 548 register Trcmd t; 549 550 foreach (Trcmd, t, eachline) 551 evalcmdlist(t->cmdlist); 552 endfor 553 foreach (Trcmd, t, eachinst) 554 evalcmdlist(t->cmdlist); 555 endfor 556 bpact(); 557 } 558 559 /* 560 * A procedure call/return has occurred while single-stepping, 561 * note it if we're tracing lines. 562 */ 563 564 private Boolean chklist(); 565 566 public callnews(iscall) 567 Boolean iscall; 568 { 569 if (not chklist(eachline, iscall)) { 570 chklist(eachinst, iscall); 571 } 572 } 573 574 private Boolean chklist(list, iscall) 575 List list; 576 Boolean iscall; 577 { 578 register Trcmd t; 579 register Command cmd; 580 581 foreach (Trcmd, t, list) 582 foreach (Command, cmd, t->cmdlist) 583 if (cmd->op == O_PRINTSRCPOS and 584 (cmd->value.arg[0] == nil or cmd->value.arg[0]->op == O_QLINE)) { 585 curfunc = whatblock(pc); 586 if (iscall) { 587 printentry(curfunc); 588 } else { 589 printexit(curfunc); 590 } 591 return true; 592 } 593 endfor 594 endfor 595 return false; 596 } 597 598 /* 599 * When tracing variables we keep a copy of their most recent value 600 * and compare it to the current one each time a breakpoint occurs. 601 * MAXTRSIZE is the maximum size variable we allow. 602 */ 603 604 #define MAXTRSIZE 512 605 606 /* 607 * List of variables being watched. 608 */ 609 610 typedef struct Trinfo *Trinfo; 611 612 struct Trinfo { 613 Node variable; 614 Address traddr; 615 Symbol trblock; 616 char *trvalue; 617 }; 618 619 private List trinfolist; 620 621 /* 622 * Find the trace information record associated with the given record. 623 * If there isn't one then create it and add it to the list. 624 */ 625 626 private Trinfo findtrinfo(p) 627 Node p; 628 { 629 register Trinfo tp; 630 Boolean isnew; 631 632 isnew = true; 633 if (trinfolist == nil) { 634 trinfolist = list_alloc(); 635 } else { 636 foreach (Trinfo, tp, trinfolist) 637 if (tp->variable == p) { 638 isnew = false; 639 break; 640 } 641 endfor 642 } 643 if (isnew) { 644 if (tracebpts) { 645 printf("adding trinfo for \""); 646 prtree(stdout, p); 647 printf("\"\n"); 648 } 649 tp = new(Trinfo); 650 tp->variable = p; 651 tp->traddr = lval(p); 652 tp->trvalue = nil; 653 list_append(list_item(tp), nil, trinfolist); 654 } 655 return tp; 656 } 657 658 /* 659 * Print out the value of a variable if it has changed since the 660 * last time we checked. 661 */ 662 663 public printifchanged(p) 664 Node p; 665 { 666 register Trinfo tp; 667 register int n; 668 char buff[MAXTRSIZE]; 669 static Lineno prevline; 670 671 tp = findtrinfo(p); 672 n = size(p->nodetype); 673 dread(buff, tp->traddr, n); 674 if (tp->trvalue == nil) { 675 tp->trvalue = newarr(char, n); 676 mov(buff, tp->trvalue, n); 677 mov(buff, sp, n); 678 sp += n; 679 printf("initially (at line %d):\t", curline); 680 prtree(stdout, p); 681 printf(" = "); 682 printval(p->nodetype); 683 putchar('\n'); 684 } else if (cmp(tp->trvalue, buff, n) != 0) { 685 mov(buff, tp->trvalue, n); 686 mov(buff, sp, n); 687 sp += n; 688 printf("after line %d:\t", prevline); 689 prtree(stdout, p); 690 printf(" = "); 691 printval(p->nodetype); 692 putchar('\n'); 693 } 694 prevline = curline; 695 } 696 697 /* 698 * Stop if the value of the given expression has changed. 699 */ 700 701 public stopifchanged(p) 702 Node p; 703 { 704 register Trinfo tp; 705 register int n; 706 char buff[MAXTRSIZE]; 707 static Lineno prevline; 708 709 tp = findtrinfo(p); 710 n = size(p->nodetype); 711 dread(buff, tp->traddr, n); 712 if (tp->trvalue == nil) { 713 tp->trvalue = newarr(char, n); 714 mov(buff, tp->trvalue, n); 715 isstopped = true; 716 } else if (cmp(tp->trvalue, buff, n) != 0) { 717 mov(buff, tp->trvalue, n); 718 isstopped = true; 719 } 720 prevline = curline; 721 } 722 723 /* 724 * Free the tracing table. 725 */ 726 727 public trfree() 728 { 729 register Trinfo tp; 730 731 foreach (Trinfo, tp, trinfolist) 732 dispose(tp->trvalue); 733 dispose(tp); 734 list_delete(list_curitem(trinfolist), trinfolist); 735 endfor 736 } 737 738 /* 739 * Fix up breakpoint information before continuing execution. 740 * 741 * It's necessary to destroy events and breakpoints that were created 742 * temporarily and still exist because the program terminated abnormally. 743 */ 744 745 public fixbps() 746 { 747 register Event e; 748 register Trcmd t; 749 750 single_stepping = false; 751 inst_tracing = false; 752 trfree(); 753 foreach (Event, e, eventlist) 754 if (e->temporary) { 755 delevent(e->id); 756 } 757 endfor 758 foreach (Trcmd, t, eachline) 759 printrmtr(t); 760 list_delete(list_curitem(eachline), eachline); 761 endfor 762 foreach (Trcmd, t, eachinst) 763 printrmtr(t); 764 list_delete(list_curitem(eachinst), eachinst); 765 endfor 766 } 767 768 /* 769 * Set all breakpoints in object code. 770 */ 771 772 public setallbps() 773 { 774 register Breakpoint p; 775 776 foreach (Breakpoint, p, bplist) 777 setbp(p->bpaddr); 778 endfor 779 } 780 781 /* 782 * Undo damage done by "setallbps". 783 */ 784 785 public unsetallbps() 786 { 787 register Breakpoint p; 788 789 foreach (Breakpoint, p, bplist) 790 unsetbp(p->bpaddr); 791 endfor 792 } 793