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