xref: /csrg-svn/old/dbx/events.c (revision 11869)
19663Slinton /* Copyright (c) 1982 Regents of the University of California */
29663Slinton 
3*11869Slinton static char sccsid[] = "@(#)events.c 1.3 04/08/83";
49663Slinton 
59663Slinton /*
69663Slinton  * Event/breakpoint managment.
79663Slinton  */
89663Slinton 
99663Slinton #include "defs.h"
109663Slinton #include "events.h"
119663Slinton #include "main.h"
129663Slinton #include "symbols.h"
139663Slinton #include "tree.h"
149663Slinton #include "eval.h"
159663Slinton #include "source.h"
169663Slinton #include "mappings.h"
179663Slinton #include "process.h"
189663Slinton #include "machine.h"
199663Slinton #include "lists.h"
209663Slinton 
219663Slinton #ifndef public
229663Slinton typedef struct Event *Event;
239663Slinton typedef struct Breakpoint *Breakpoint;
249663Slinton 
259663Slinton Boolean inst_tracing;
269663Slinton Boolean single_stepping;
279663Slinton Boolean isstopped;
289663Slinton 
299663Slinton #include "symbols.h"
309663Slinton 
319663Slinton Symbol linesym;
329663Slinton Symbol procsym;
339663Slinton Symbol pcsym;
349663Slinton Symbol retaddrsym;
359663Slinton 
369663Slinton #define addevent(cond, cmdlist) event_alloc(false, cond, cmdlist)
379663Slinton #define event_once(cond, cmdlist) event_alloc(true, cond, cmdlist)
389663Slinton 
399663Slinton #endif
409663Slinton 
419663Slinton struct Event {
429663Slinton     unsigned int id;
439663Slinton     Boolean temporary;
449663Slinton     Node condition;
459663Slinton     Cmdlist actions;
469663Slinton };
479663Slinton 
489663Slinton struct Breakpoint {
499663Slinton     Event event;
509663Slinton     Address bpaddr;
519663Slinton     Lineno bpline;
529663Slinton     Cmdlist actions;
539663Slinton };
549663Slinton 
559663Slinton typedef List Eventlist;
569663Slinton typedef List Bplist;
579663Slinton 
589663Slinton #define eventlist_append(event, el) list_append(list_item(event), nil, el)
599663Slinton #define bplist_append(bp, bl) list_append(list_item(bp), nil, bl)
609663Slinton 
619663Slinton private Eventlist eventlist;		/* list of active events */
629663Slinton private Bplist bplist;			/* list of active breakpoints */
639663Slinton private Integer eventid;		/* id number of next allocated event */
649663Slinton private Integer trid;			/* id number of next allocated trace */
659663Slinton 
669663Slinton typedef struct Trcmd {
679663Slinton     Integer trid;
689663Slinton     Event event;
699663Slinton     Cmdlist cmdlist;
709663Slinton } *Trcmd;
719663Slinton 
729663Slinton private List eachline;		/* commands to execute after each line */
739663Slinton private List eachinst;		/* commands to execute after each instruction */
749663Slinton 
759663Slinton private Breakpoint bp_alloc();
769663Slinton 
779663Slinton /*
789663Slinton  * Initialize breakpoint information.
799663Slinton  */
809663Slinton 
819663Slinton private Symbol builtinsym(str, class, type)
829663Slinton String str;
839663Slinton Symclass class;
849663Slinton Symbol type;
859663Slinton {
869663Slinton     Symbol s;
879663Slinton 
889663Slinton     s = insert(identname(str, true));
899663Slinton     s->language = findlanguage(".s");
909663Slinton     s->class = class;
919663Slinton     s->type = type;
929663Slinton     return s;
939663Slinton }
949663Slinton 
959663Slinton public bpinit()
969663Slinton {
979663Slinton     linesym = builtinsym("$line", VAR, t_int);
989663Slinton     procsym = builtinsym("$proc", PROC, nil);
999663Slinton     pcsym = lookup(identname("$pc", true));
1009663Slinton     if (pcsym == nil) {
1019663Slinton 	panic("can't find $pc");
1029663Slinton     }
1039663Slinton     retaddrsym = builtinsym("$retaddr", VAR, t_int);
1049663Slinton     eventlist = list_alloc();
1059663Slinton     bplist = list_alloc();
1069663Slinton     eachline = list_alloc();
1079663Slinton     eachinst = list_alloc();
1089663Slinton }
1099663Slinton 
1109663Slinton /*
1119663Slinton  * Trap an event and do the associated commands when it occurs.
1129663Slinton  */
1139663Slinton 
1149663Slinton public Event event_alloc(istmp, econd, cmdlist)
1159663Slinton Boolean istmp;
1169663Slinton Node econd;
1179663Slinton Cmdlist cmdlist;
1189663Slinton {
1199663Slinton     register Event e;
1209663Slinton 
1219663Slinton     e = new(Event);
1229663Slinton     ++eventid;
1239663Slinton     e->id = eventid;
1249663Slinton     e->temporary = istmp;
1259663Slinton     e->condition = econd;
1269663Slinton     e->actions = cmdlist;
1279663Slinton     eventlist_append(e, eventlist);
1289663Slinton     translate(e);
1299663Slinton     return e;
1309663Slinton }
1319663Slinton 
1329663Slinton /*
1339663Slinton  * Delete the event with the given id.
1349663Slinton  */
1359663Slinton 
1369663Slinton public delevent(id)
1379663Slinton unsigned int id;
1389663Slinton {
1399663Slinton     Event e;
1409663Slinton     Breakpoint bp;
1419663Slinton     Trcmd t;
1429663Slinton 
1439663Slinton     foreach (Event, e, eventlist)
1449663Slinton 	if (e->id == id) {
1459663Slinton 	    list_delete(list_curitem(eventlist), eventlist);
1469663Slinton 	    foreach (Breakpoint, bp, bplist)
1479663Slinton 		if (bp->event == e) {
1489663Slinton 		    list_delete(list_curitem(bplist), bplist);
1499663Slinton 		}
1509663Slinton 	    endfor
1519663Slinton 	    break;
1529663Slinton 	}
1539663Slinton     endfor
1549663Slinton     foreach (Trcmd, t, eachline)
1559663Slinton 	if (t->event->id == id) {
1569663Slinton 	    printrmtr(t);
1579663Slinton 	    list_delete(list_curitem(eachline), eachline);
1589663Slinton 	}
1599663Slinton     endfor
1609663Slinton     foreach (Trcmd, t, eachinst)
1619663Slinton 	if (t->event->id == id) {
1629663Slinton 	    printrmtr(t);
1639663Slinton 	    list_delete(list_curitem(eachinst), eachinst);
1649663Slinton 	}
1659663Slinton     endfor
1669663Slinton     if (list_size(eachinst) == 0) {
1679663Slinton 	inst_tracing = false;
1689663Slinton 	if (list_size(eachline) == 0) {
1699663Slinton 	    single_stepping = false;
1709663Slinton 	}
1719663Slinton     }
1729663Slinton }
1739663Slinton 
1749663Slinton /*
1759663Slinton  * Translate an event into the appropriate breakpoints and actions.
1769663Slinton  * While we're at it, turn on the breakpoints if the condition is true.
1779663Slinton  */
1789663Slinton 
1799663Slinton private translate(e)
1809663Slinton Event e;
1819663Slinton {
1829663Slinton     Breakpoint bp;
1839663Slinton     Symbol s;
1849663Slinton     Node place;
1859663Slinton     Lineno line;
1869663Slinton     Address addr;
1879663Slinton 
1889663Slinton     checkref(e->condition);
1899663Slinton     switch (e->condition->op) {
1909663Slinton 	case O_EQ:
1919663Slinton 	    if (e->condition->value.arg[0]->op == O_SYM) {
1929663Slinton 		s = e->condition->value.arg[0]->value.sym;
1939663Slinton 		place = e->condition->value.arg[1];
1949663Slinton 		if (s == linesym) {
1959663Slinton 		    if (place->op == O_QLINE) {
1969663Slinton 			line = place->value.arg[1]->value.lcon;
1979663Slinton 			addr = objaddr(line,
1989663Slinton 			    place->value.arg[0]->value.scon);
1999663Slinton 		    } else {
2009663Slinton 			eval(place);
2019663Slinton 			line = pop(long);
2029663Slinton 			addr = objaddr(line, cursource);
2039663Slinton 		    }
2049663Slinton 		    if (addr == NOADDR) {
2059663Slinton 			delevent(e->id);
2069663Slinton 			beginerrmsg();
2079663Slinton 			fprintf(stderr, "no executable code at line ");
2089663Slinton 			prtree(stderr, place);
2099663Slinton 			enderrmsg();
2109663Slinton 		    }
2119663Slinton 		    bp = bp_alloc(e, addr, line, e->actions);
2129663Slinton 		} else if (s == procsym) {
2139663Slinton 		    eval(place);
2149663Slinton 		    s = pop(Symbol);
2159663Slinton 		    bp = bp_alloc(e, codeloc(s), 0, e->actions);
2169663Slinton 		    if (isactive(s) and pc != codeloc(program)) {
2179663Slinton 			evalcmdlist(e->actions);
2189663Slinton 		    }
2199663Slinton 		} else if (s == pcsym) {
2209663Slinton 		    eval(place);
2219663Slinton 		    bp = bp_alloc(e, pop(Address), 0, e->actions);
2229663Slinton 		} else {
2239663Slinton 		    condbp(e);
2249663Slinton 		}
2259663Slinton 	    } else {
2269663Slinton 		condbp(e);
2279663Slinton 	    }
2289663Slinton 	    break;
2299663Slinton 
2309663Slinton 	/*
2319663Slinton 	 * These should be handled specially.
2329663Slinton 	 * But for now I'm ignoring the problem.
2339663Slinton 	 */
2349663Slinton 	case O_AND:
2359663Slinton 	case O_OR:
2369663Slinton 	default:
2379663Slinton 	    condbp(e);
2389663Slinton 	    break;
2399663Slinton     }
2409663Slinton }
2419663Slinton 
2429663Slinton /*
2439663Slinton  * Create a breakpoint for a condition that cannot be pinpointed
2449663Slinton  * to happening at a particular address, but one for which we
2459663Slinton  * must single step and check the condition after each statement.
2469663Slinton  */
2479663Slinton 
2489663Slinton private condbp(e)
2499663Slinton Event e;
2509663Slinton {
2519663Slinton     Symbol p;
2529663Slinton     Breakpoint bp;
2539663Slinton     Cmdlist actions;
2549663Slinton 
2559663Slinton     p = tcontainer(e->condition);
2569663Slinton     if (p == nil) {
2579663Slinton 	p = program;
2589663Slinton     }
2599663Slinton     actions = buildcmdlist(build(O_IF, e->condition, e->actions));
2609663Slinton     actions = buildcmdlist(build(O_TRACEON, false, actions));
2619663Slinton     bp = bp_alloc(e, codeloc(p), 0, actions);
2629663Slinton }
2639663Slinton 
2649663Slinton /*
2659663Slinton  * Determine the deepest nested subprogram that still contains
2669663Slinton  * all elements in the given expression.
2679663Slinton  */
2689663Slinton 
2699663Slinton public Symbol tcontainer(exp)
2709663Slinton Node exp;
2719663Slinton {
2729663Slinton     Integer i;
2739663Slinton     Symbol s, t, u, v;
2749663Slinton 
2759663Slinton     checkref(exp);
2769663Slinton     s = nil;
2779663Slinton     if (exp->op == O_SYM) {
2789663Slinton 	s = container(exp->value.sym);
2799663Slinton     } else if (not isleaf(exp->op)) {
2809663Slinton 	for (i = 0; i < nargs(exp->op); i++) {
2819663Slinton 	    t = tcontainer(exp->value.arg[i]);
2829663Slinton 	    if (t != nil) {
2839663Slinton 		if (s == nil) {
2849663Slinton 		    s = t;
2859663Slinton 		} else {
2869663Slinton 		    u = s;
2879663Slinton 		    v = t;
2889663Slinton 		    while (u != v and u != nil) {
2899663Slinton 			u = container(u);
2909663Slinton 			v = container(v);
2919663Slinton 		    }
2929663Slinton 		    if (u == nil) {
2939663Slinton 			panic("bad ancestry for \"%s\"", symname(s));
2949663Slinton 		    } else {
2959663Slinton 			s = u;
2969663Slinton 		    }
2979663Slinton 		}
2989663Slinton 	    }
2999663Slinton 	}
3009663Slinton     }
3019663Slinton     return s;
3029663Slinton }
3039663Slinton 
3049663Slinton /*
305*11869Slinton  * Determine if the given function can be executed at full speed.
306*11869Slinton  * This can only be done if there are no breakpoints within the function.
307*11869Slinton  */
308*11869Slinton 
309*11869Slinton public Boolean canskip(f)
310*11869Slinton Symbol f;
311*11869Slinton {
312*11869Slinton     Breakpoint p;
313*11869Slinton     Boolean ok;
314*11869Slinton 
315*11869Slinton     ok = true;
316*11869Slinton     foreach (Breakpoint, p, bplist)
317*11869Slinton 	if (whatblock(p->bpaddr) == f) {
318*11869Slinton 	    ok = false;
319*11869Slinton 	    break;
320*11869Slinton 	}
321*11869Slinton     endfor
322*11869Slinton     return ok;
323*11869Slinton }
324*11869Slinton 
325*11869Slinton /*
3269663Slinton  * Print out what's currently being traced by looking at
3279663Slinton  * the currently active events.
3289663Slinton  *
3299663Slinton  * Some convolution here to translate internal representation
3309663Slinton  * of events back into something more palatable.
3319663Slinton  */
3329663Slinton 
3339663Slinton public status()
3349663Slinton {
3359663Slinton     Event e;
3369663Slinton 
3379663Slinton     foreach (Event, e, eventlist)
3389663Slinton 	if (not e->temporary) {
339*11869Slinton 	    printevent(e);
3409663Slinton 	}
3419663Slinton     endfor
3429663Slinton }
3439663Slinton 
344*11869Slinton public printevent(e)
345*11869Slinton Event e;
346*11869Slinton {
347*11869Slinton     Command cmd;
348*11869Slinton 
349*11869Slinton     if (not isredirected()) {
350*11869Slinton 	printf("(%d) ", e->id);
351*11869Slinton     }
352*11869Slinton     cmd = list_element(Command, list_head(e->actions));
353*11869Slinton     if (cmd->op == O_PRINTCALL) {
354*11869Slinton 	printf("trace ");
355*11869Slinton 	printname(stdout, cmd->value.sym);
356*11869Slinton     } else {
357*11869Slinton 	if (list_size(e->actions) > 1) {
358*11869Slinton 	    printf("{ ");
359*11869Slinton 	}
360*11869Slinton 	foreach (Command, cmd, e->actions)
361*11869Slinton 	    printcmd(stdout, cmd);
362*11869Slinton 	    if (not list_islast()) {
363*11869Slinton 		printf("; ");
364*11869Slinton 	    }
365*11869Slinton 	endfor
366*11869Slinton 	if (list_size(e->actions) > 1) {
367*11869Slinton 	    printf(" }");
368*11869Slinton 	}
369*11869Slinton 	printcond(e->condition);
370*11869Slinton     }
371*11869Slinton     printf("\n");
372*11869Slinton }
373*11869Slinton 
3749663Slinton /*
3759663Slinton  * Print out a condition.
3769663Slinton  */
3779663Slinton 
3789663Slinton private printcond(cond)
3799663Slinton Node cond;
3809663Slinton {
3819663Slinton     Symbol s;
3829663Slinton     Node place;
3839663Slinton 
3849663Slinton     if (cond->op == O_EQ and cond->value.arg[0]->op == O_SYM) {
3859663Slinton 	s = cond->value.arg[0]->value.sym;
3869663Slinton 	place = cond->value.arg[1];
3879663Slinton 	if (s == procsym) {
3889663Slinton 	    if (place->value.sym != program) {
3899663Slinton 		printf(" in ");
3909663Slinton 		printname(stdout, place->value.sym);
3919663Slinton 	    }
3929663Slinton 	} else if (s == linesym) {
3939663Slinton 	    printf(" at ");
3949663Slinton 	    prtree(stdout, place);
3959663Slinton 	} else if (s == pcsym or s == retaddrsym) {
3969663Slinton 	    printf("i at ");
3979663Slinton 	    prtree(stdout, place);
3989663Slinton 	} else {
3999663Slinton 	    printf(" when ");
4009663Slinton 	    prtree(stdout, cond);
4019663Slinton 	}
4029663Slinton     } else {
4039663Slinton 	printf(" when ");
4049663Slinton 	prtree(stdout, cond);
4059663Slinton     }
4069663Slinton }
4079663Slinton 
4089663Slinton /*
4099663Slinton  * Add a breakpoint to the list and return it.
4109663Slinton  */
4119663Slinton 
4129663Slinton private Breakpoint bp_alloc(e, addr, line, actions)
4139663Slinton Event e;
4149663Slinton Address addr;
4159663Slinton Lineno line;
4169663Slinton Cmdlist actions;
4179663Slinton {
4189663Slinton     register Breakpoint p;
4199663Slinton 
4209663Slinton     p = new(Breakpoint);
4219663Slinton     p->event = e;
4229663Slinton     p->bpaddr = addr;
4239663Slinton     p->bpline = line;
4249663Slinton     p->actions = actions;
4259663Slinton     if (tracebpts) {
4269663Slinton 	printf("new bp at 0x%x\n", addr);
4279663Slinton 	fflush(stdout);
4289663Slinton     }
4299663Slinton     bplist_append(p, bplist);
4309663Slinton     return p;
4319663Slinton }
4329663Slinton 
4339663Slinton /*
4349663Slinton  * Free all storage in the event and breakpoint tables.
4359663Slinton  */
4369663Slinton 
4379663Slinton public bpfree()
4389663Slinton {
4399663Slinton     register Event e;
4409663Slinton 
4419663Slinton     fixbps();
4429663Slinton     foreach (Event, e, eventlist)
4439663Slinton 	delevent(e->id);
4449663Slinton 	list_delete(list_curitem(eventlist), eventlist);
4459663Slinton     endfor
4469663Slinton }
4479663Slinton 
4489663Slinton /*
4499663Slinton  * Determine if the program stopped at a known breakpoint
4509663Slinton  * and if so do the associated commands.
4519663Slinton  */
4529663Slinton 
4539663Slinton public Boolean bpact()
4549663Slinton {
4559663Slinton     register Breakpoint p;
4569663Slinton     Boolean found;
4579663Slinton 
4589663Slinton     found = false;
4599663Slinton     foreach (Breakpoint, p, bplist)
4609663Slinton 	if (p->bpaddr == pc) {
4619663Slinton 	    if (tracebpts) {
4629663Slinton 		printf("breakpoint found at location 0x%x\n", pc);
4639663Slinton 	    }
4649663Slinton 	    found = true;
4659663Slinton 	    if (p->event->temporary) {
4669663Slinton 		delevent(p->event->id);
4679663Slinton 	    }
4689663Slinton 	    evalcmdlist(p->actions);
4699663Slinton 	}
4709663Slinton     endfor
4719663Slinton     if (isstopped) {
4729663Slinton 	printstatus();
4739663Slinton     }
4749663Slinton     fflush(stdout);
4759663Slinton     return found;
4769663Slinton }
4779663Slinton 
4789663Slinton /*
4799663Slinton  * Begin single stepping and executing the given commands after each step.
4809663Slinton  * If the first argument is true step by instructions, otherwise
4819663Slinton  * step by source lines.
4829663Slinton  *
4839663Slinton  * We automatically set a breakpoint at the end of the current procedure
4849663Slinton  * to turn off the given tracing.
4859663Slinton  */
4869663Slinton 
4879663Slinton public traceon(inst, event, cmdlist)
4889663Slinton Boolean inst;
4899663Slinton Event event;
4909663Slinton Cmdlist cmdlist;
4919663Slinton {
4929663Slinton     register Trcmd trcmd;
4939663Slinton     Breakpoint bp;
4949663Slinton     Node until;
4959663Slinton     Cmdlist actions;
496*11869Slinton     Address ret;
4979663Slinton 
4989663Slinton     trcmd = new(Trcmd);
4999663Slinton     ++trid;
5009663Slinton     trcmd->trid = trid;
5019663Slinton     trcmd->event = event;
5029663Slinton     trcmd->cmdlist = cmdlist;
5039663Slinton     single_stepping = true;
5049663Slinton     if (inst) {
5059663Slinton 	inst_tracing = true;
5069663Slinton 	list_append(list_item(trcmd), nil, eachinst);
5079663Slinton     } else {
5089663Slinton 	list_append(list_item(trcmd), nil, eachline);
5099663Slinton     }
510*11869Slinton     ret = return_addr();
511*11869Slinton     if (ret != 0) {
512*11869Slinton 	until = build(O_EQ, build(O_SYM, pcsym), build(O_LCON, ret));
513*11869Slinton 	actions = buildcmdlist(build(O_TRACEOFF, trcmd->trid));
514*11869Slinton 	event_once(until, actions);
515*11869Slinton     }
5169663Slinton     if (tracebpts) {
5179663Slinton 	printf("adding trace %d for event %d\n", trcmd->trid, event->id);
5189663Slinton     }
5199663Slinton }
5209663Slinton 
5219663Slinton /*
5229663Slinton  * Turn off some kind of tracing.
5239663Slinton  * Strictly an internal command, this cannot be invoked by the user.
5249663Slinton  */
5259663Slinton 
5269663Slinton public traceoff(id)
5279663Slinton Integer id;
5289663Slinton {
5299663Slinton     register Trcmd t;
5309663Slinton     register Boolean found;
5319663Slinton 
5329663Slinton     found = false;
5339663Slinton     foreach (Trcmd, t, eachline)
5349663Slinton 	if (t->trid == id) {
5359663Slinton 	    printrmtr(t);
5369663Slinton 	    list_delete(list_curitem(eachline), eachline);
5379663Slinton 	    found = true;
5389663Slinton 	    break;
5399663Slinton 	}
5409663Slinton     endfor
5419663Slinton     if (not found) {
5429663Slinton 	foreach (Trcmd, t, eachinst)
5439663Slinton 	    if (t->event->id == id) {
5449663Slinton 		printrmtr(t);
5459663Slinton 		list_delete(list_curitem(eachinst), eachinst);
5469663Slinton 		found = true;
5479663Slinton 		break;
5489663Slinton 	    }
5499663Slinton 	endfor
5509663Slinton 	if (not found) {
5519663Slinton 	    panic("missing trid %d", id);
5529663Slinton 	}
5539663Slinton     }
5549663Slinton     if (list_size(eachinst) == 0) {
5559663Slinton 	inst_tracing = false;
5569663Slinton 	if (list_size(eachline) == 0) {
5579663Slinton 	    single_stepping = false;
5589663Slinton 	}
5599663Slinton     }
5609663Slinton }
5619663Slinton 
5629663Slinton /*
5639663Slinton  * If breakpoints are being traced, note that a Trcmd is being deleted.
5649663Slinton  */
5659663Slinton 
5669663Slinton private printrmtr(t)
5679663Slinton Trcmd t;
5689663Slinton {
5699663Slinton     if (tracebpts) {
570*11869Slinton 	printf("removing trace %d", t->trid);
571*11869Slinton 	if (t->event != nil) {
572*11869Slinton 	    printf(" for event %d", t->event->id);
573*11869Slinton 	}
574*11869Slinton 	printf("\n");
5759663Slinton     }
5769663Slinton }
5779663Slinton 
5789663Slinton /*
5799663Slinton  * Print out news during single step tracing.
5809663Slinton  */
5819663Slinton 
5829663Slinton public printnews()
5839663Slinton {
5849663Slinton     register Trcmd t;
5859663Slinton 
5869663Slinton     foreach (Trcmd, t, eachline)
5879663Slinton 	evalcmdlist(t->cmdlist);
5889663Slinton     endfor
5899663Slinton     foreach (Trcmd, t, eachinst)
5909663Slinton 	evalcmdlist(t->cmdlist);
5919663Slinton     endfor
5929663Slinton     bpact();
5939663Slinton }
5949663Slinton 
5959663Slinton /*
5969663Slinton  * A procedure call/return has occurred while single-stepping,
5979663Slinton  * note it if we're tracing lines.
5989663Slinton  */
5999663Slinton 
6009663Slinton private Boolean chklist();
6019663Slinton 
6029663Slinton public callnews(iscall)
6039663Slinton Boolean iscall;
6049663Slinton {
6059663Slinton     if (not chklist(eachline, iscall)) {
6069663Slinton 	chklist(eachinst, iscall);
6079663Slinton     }
6089663Slinton }
6099663Slinton 
6109663Slinton private Boolean chklist(list, iscall)
6119663Slinton List list;
6129663Slinton Boolean iscall;
6139663Slinton {
6149663Slinton     register Trcmd t;
6159663Slinton     register Command cmd;
6169663Slinton 
617*11869Slinton     curfunc = whatblock(pc);
6189663Slinton     foreach (Trcmd, t, list)
6199663Slinton 	foreach (Command, cmd, t->cmdlist)
6209663Slinton 	    if (cmd->op == O_PRINTSRCPOS and
6219663Slinton 	      (cmd->value.arg[0] == nil or cmd->value.arg[0]->op == O_QLINE)) {
6229663Slinton 		if (iscall) {
6239663Slinton 		    printentry(curfunc);
6249663Slinton 		} else {
6259663Slinton 		    printexit(curfunc);
6269663Slinton 		}
6279663Slinton 		return true;
6289663Slinton 	    }
6299663Slinton 	endfor
6309663Slinton     endfor
6319663Slinton     return false;
6329663Slinton }
6339663Slinton 
6349663Slinton /*
6359663Slinton  * When tracing variables we keep a copy of their most recent value
6369663Slinton  * and compare it to the current one each time a breakpoint occurs.
6379663Slinton  * MAXTRSIZE is the maximum size variable we allow.
6389663Slinton  */
6399663Slinton 
6409663Slinton #define MAXTRSIZE 512
6419663Slinton 
6429663Slinton /*
6439663Slinton  * List of variables being watched.
6449663Slinton  */
6459663Slinton 
6469663Slinton typedef struct Trinfo *Trinfo;
6479663Slinton 
6489663Slinton struct Trinfo {
6499663Slinton     Node variable;
6509663Slinton     Address traddr;
6519663Slinton     Symbol trblock;
6529663Slinton     char *trvalue;
6539663Slinton };
6549663Slinton 
6559663Slinton private List trinfolist;
6569663Slinton 
6579663Slinton /*
6589663Slinton  * Find the trace information record associated with the given record.
6599663Slinton  * If there isn't one then create it and add it to the list.
6609663Slinton  */
6619663Slinton 
6629663Slinton private Trinfo findtrinfo(p)
6639663Slinton Node p;
6649663Slinton {
6659663Slinton     register Trinfo tp;
6669663Slinton     Boolean isnew;
6679663Slinton 
6689663Slinton     isnew = true;
6699663Slinton     if (trinfolist == nil) {
6709663Slinton 	trinfolist = list_alloc();
6719663Slinton     } else {
6729663Slinton 	foreach (Trinfo, tp, trinfolist)
6739663Slinton 	    if (tp->variable == p) {
6749663Slinton 		isnew = false;
6759663Slinton 		break;
6769663Slinton 	    }
6779663Slinton 	endfor
6789663Slinton     }
6799663Slinton     if (isnew) {
6809663Slinton 	if (tracebpts) {
6819663Slinton 	    printf("adding trinfo for \"");
6829663Slinton 	    prtree(stdout, p);
6839663Slinton 	    printf("\"\n");
6849663Slinton 	}
6859663Slinton 	tp = new(Trinfo);
6869663Slinton 	tp->variable = p;
6879663Slinton 	tp->traddr = lval(p);
6889663Slinton 	tp->trvalue = nil;
6899663Slinton 	list_append(list_item(tp), nil, trinfolist);
6909663Slinton     }
6919663Slinton     return tp;
6929663Slinton }
6939663Slinton 
6949663Slinton /*
6959663Slinton  * Print out the value of a variable if it has changed since the
6969663Slinton  * last time we checked.
6979663Slinton  */
6989663Slinton 
6999663Slinton public printifchanged(p)
7009663Slinton Node p;
7019663Slinton {
7029663Slinton     register Trinfo tp;
7039663Slinton     register int n;
7049663Slinton     char buff[MAXTRSIZE];
7059663Slinton     static Lineno prevline;
7069663Slinton 
7079663Slinton     tp = findtrinfo(p);
7089663Slinton     n = size(p->nodetype);
7099663Slinton     dread(buff, tp->traddr, n);
7109663Slinton     if (tp->trvalue == nil) {
7119663Slinton 	tp->trvalue = newarr(char, n);
7129663Slinton 	mov(buff, tp->trvalue, n);
7139663Slinton 	mov(buff, sp, n);
7149663Slinton 	sp += n;
7159663Slinton 	printf("initially (at line %d):\t", curline);
7169663Slinton 	prtree(stdout, p);
7179663Slinton 	printf(" = ");
7189663Slinton 	printval(p->nodetype);
7199663Slinton 	putchar('\n');
7209663Slinton     } else if (cmp(tp->trvalue, buff, n) != 0) {
7219663Slinton 	mov(buff, tp->trvalue, n);
7229663Slinton 	mov(buff, sp, n);
7239663Slinton 	sp += n;
7249663Slinton 	printf("after line %d:\t", prevline);
7259663Slinton 	prtree(stdout, p);
7269663Slinton 	printf(" = ");
7279663Slinton 	printval(p->nodetype);
7289663Slinton 	putchar('\n');
7299663Slinton     }
7309663Slinton     prevline = curline;
7319663Slinton }
7329663Slinton 
7339663Slinton /*
7349663Slinton  * Stop if the value of the given expression has changed.
7359663Slinton  */
7369663Slinton 
7379663Slinton public stopifchanged(p)
7389663Slinton Node p;
7399663Slinton {
7409663Slinton     register Trinfo tp;
7419663Slinton     register int n;
7429663Slinton     char buff[MAXTRSIZE];
7439663Slinton     static Lineno prevline;
7449663Slinton 
7459663Slinton     tp = findtrinfo(p);
7469663Slinton     n = size(p->nodetype);
7479663Slinton     dread(buff, tp->traddr, n);
7489663Slinton     if (tp->trvalue == nil) {
7499663Slinton 	tp->trvalue = newarr(char, n);
7509663Slinton 	mov(buff, tp->trvalue, n);
7519663Slinton 	isstopped = true;
7529663Slinton     } else if (cmp(tp->trvalue, buff, n) != 0) {
7539663Slinton 	mov(buff, tp->trvalue, n);
7549663Slinton 	isstopped = true;
7559663Slinton     }
7569663Slinton     prevline = curline;
7579663Slinton }
7589663Slinton 
7599663Slinton /*
7609663Slinton  * Free the tracing table.
7619663Slinton  */
7629663Slinton 
7639663Slinton public trfree()
7649663Slinton {
7659663Slinton     register Trinfo tp;
7669663Slinton 
7679663Slinton     foreach (Trinfo, tp, trinfolist)
7689663Slinton 	dispose(tp->trvalue);
7699663Slinton 	dispose(tp);
7709663Slinton 	list_delete(list_curitem(trinfolist), trinfolist);
7719663Slinton     endfor
7729663Slinton }
7739663Slinton 
7749663Slinton /*
7759663Slinton  * Fix up breakpoint information before continuing execution.
7769663Slinton  *
7779663Slinton  * It's necessary to destroy events and breakpoints that were created
7789663Slinton  * temporarily and still exist because the program terminated abnormally.
7799663Slinton  */
7809663Slinton 
7819663Slinton public fixbps()
7829663Slinton {
7839663Slinton     register Event e;
7849663Slinton     register Trcmd t;
7859663Slinton 
7869663Slinton     single_stepping = false;
7879663Slinton     inst_tracing = false;
7889663Slinton     trfree();
7899663Slinton     foreach (Event, e, eventlist)
7909663Slinton 	if (e->temporary) {
7919663Slinton 	    delevent(e->id);
7929663Slinton 	}
7939663Slinton     endfor
7949663Slinton     foreach (Trcmd, t, eachline)
7959663Slinton 	printrmtr(t);
7969663Slinton 	list_delete(list_curitem(eachline), eachline);
7979663Slinton     endfor
7989663Slinton     foreach (Trcmd, t, eachinst)
7999663Slinton 	printrmtr(t);
8009663Slinton 	list_delete(list_curitem(eachinst), eachinst);
8019663Slinton     endfor
8029663Slinton }
8039663Slinton 
8049663Slinton /*
8059663Slinton  * Set all breakpoints in object code.
8069663Slinton  */
8079663Slinton 
8089663Slinton public setallbps()
8099663Slinton {
8109663Slinton     register Breakpoint p;
8119663Slinton 
8129663Slinton     foreach (Breakpoint, p, bplist)
8139663Slinton 	setbp(p->bpaddr);
8149663Slinton     endfor
8159663Slinton }
8169663Slinton 
8179663Slinton /*
8189663Slinton  * Undo damage done by "setallbps".
8199663Slinton  */
8209663Slinton 
8219663Slinton public unsetallbps()
8229663Slinton {
8239663Slinton     register Breakpoint p;
8249663Slinton 
8259663Slinton     foreach (Breakpoint, p, bplist)
8269663Slinton 	unsetbp(p->bpaddr);
8279663Slinton     endfor
8289663Slinton }
829