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