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