prints some more information before prompting now
[unix-history] / usr / src / old / dbx / events.c
CommitLineData
4298caf5
ML
1/* Copyright (c) 1982 Regents of the University of California */
2
550fe947 3static char sccsid[] = "@(#)events.c 1.2 %G%";
4298caf5
ML
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
22typedef struct Event *Event;
23typedef struct Breakpoint *Breakpoint;
24
25Boolean inst_tracing;
26Boolean single_stepping;
27Boolean isstopped;
28
29#include "symbols.h"
30
31Symbol linesym;
32Symbol procsym;
33Symbol pcsym;
34Symbol 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
41struct Event {
42 unsigned int id;
43 Boolean temporary;
44 Node condition;
45 Cmdlist actions;
46};
47
48struct Breakpoint {
49 Event event;
50 Address bpaddr;
51 Lineno bpline;
52 Cmdlist actions;
53};
54
55typedef List Eventlist;
56typedef 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
61private Eventlist eventlist; /* list of active events */
62private Bplist bplist; /* list of active breakpoints */
63private Integer eventid; /* id number of next allocated event */
64private Integer trid; /* id number of next allocated trace */
65
66typedef struct Trcmd {
67 Integer trid;
68 Event event;
69 Cmdlist cmdlist;
70} *Trcmd;
71
72private List eachline; /* commands to execute after each line */
73private List eachinst; /* commands to execute after each instruction */
74
75private Breakpoint bp_alloc();
76
77/*
78 * Initialize breakpoint information.
79 */
80
81private Symbol builtinsym(str, class, type)
82String str;
83Symclass class;
84Symbol 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
95public 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
114public Event event_alloc(istmp, econd, cmdlist)
115Boolean istmp;
116Node econd;
117Cmdlist 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
136public delevent(id)
137unsigned 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
179private translate(e)
180Event 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
248private condbp(e)
249Event 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
269public Symbol tcontainer(exp)
270Node 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
312public 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
350private printcond(cond)
351Node 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
384private Breakpoint bp_alloc(e, addr, line, actions)
385Event e;
386Address addr;
387Lineno line;
388Cmdlist 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
409public 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
425public 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
459public traceon(inst, event, cmdlist)
460Boolean inst;
461Event event;
462Cmdlist 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
494public traceoff(id)
495Integer 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
534private printrmtr(t)
535Trcmd 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
546public 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
564private Boolean chklist();
565
566public callnews(iscall)
567Boolean iscall;
568{
569 if (not chklist(eachline, iscall)) {
570 chklist(eachinst, iscall);
571 }
572}
573
574private Boolean chklist(list, iscall)
575List list;
576Boolean 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
610typedef struct Trinfo *Trinfo;
611
612struct Trinfo {
613 Node variable;
614 Address traddr;
615 Symbol trblock;
616 char *trvalue;
617};
618
619private 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
626private Trinfo findtrinfo(p)
627Node 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
663public printifchanged(p)
664Node 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
701public stopifchanged(p)
702Node 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
727public 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
745public 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
772public 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
785public unsetallbps()
786{
787 register Breakpoint p;
788
789 foreach (Breakpoint, p, bplist)
790 unsetbp(p->bpaddr);
791 endfor
792}