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