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