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