/* Copyright (c) 1982 Regents of the University of California */
static char sccsid
[] = "@(#)events.c 1.4 (Berkeley) %G%";
* Event/breakpoint managment.
typedef struct Event
*Event
;
typedef struct Breakpoint
*Breakpoint
;
#define addevent(cond, cmdlist) event_alloc(false, cond, cmdlist)
#define event_once(cond, cmdlist) event_alloc(true, cond, cmdlist)
#define eventlist_append(event, el) list_append(list_item(event), nil, el)
#define bplist_append(bp, bl) list_append(list_item(bp), nil, bl)
private Eventlist eventlist
; /* list of active events */
private Bplist bplist
; /* list of active breakpoints */
private Integer eventid
; /* id number of next allocated event */
private Integer trid
; /* id number of next allocated trace */
private List eachline
; /* commands to execute after each line */
private List eachinst
; /* commands to execute after each instruction */
private Breakpoint
bp_alloc();
* Initialize breakpoint information.
private Symbol
builtinsym(str
, class, type
)
s
= insert(identname(str
, true));
s
->language
= findlanguage(".s");
linesym
= builtinsym("$line", VAR
, t_int
);
procsym
= builtinsym("$proc", PROC
, nil
);
pcsym
= lookup(identname("$pc", true));
retaddrsym
= builtinsym("$retaddr", VAR
, t_int
);
eventlist
= list_alloc();
* Trap an event and do the associated commands when it occurs.
public Event
event_alloc(istmp
, econd
, cmdlist
)
eventlist_append(e
, eventlist
);
* Delete the event with the given id.
* Returns whether it's successful or not.
public boolean
delevent (id
)
foreach (Event
, e
, eventlist
)
foreach (Breakpoint
, bp
, bplist
)
printf("deleting breakpoint at 0x%x\n", bp
->bpaddr
);
list_delete(list_curitem(bplist
), bplist
);
list_delete(list_curitem(eventlist
), eventlist
);
foreach (Trcmd
, t
, eachline
)
if (t
->event
->id
== id
) {
list_delete(list_curitem(eachline
), eachline
);
foreach (Trcmd
, t
, eachinst
)
if (t
->event
->id
== id
) {
list_delete(list_curitem(eachinst
), eachinst
);
if (list_size(eachinst
) == 0) {
if (list_size(eachline
) == 0) {
* Translate an event into the appropriate breakpoints and actions.
* While we're at it, turn on the breakpoints if the condition is true.
switch (e
->condition
->op
) {
if (e
->condition
->value
.arg
[0]->op
== O_SYM
) {
s
= e
->condition
->value
.arg
[0]->value
.sym
;
place
= e
->condition
->value
.arg
[1];
if (place
->op
== O_QLINE
) {
line
= place
->value
.arg
[1]->value
.lcon
;
addr
= objaddr(line
, place
->value
.arg
[0]->value
.scon
);
addr
= objaddr(line
, cursource
);
if (not delevent(e
->id
)) {
printf("!! dbx.translate: can't undo event %d?\n",
fprintf(stderr
, "no executable code at line ");
bp
= bp_alloc(e
, addr
, line
, e
->actions
);
} else if (s
== procsym
) {
bp
= bp_alloc(e
, codeloc(s
), 0, e
->actions
);
if (isactive(s
) and pc
!= codeloc(program
)) {
bp
= bp_alloc(e
, pop(Address
), 0, e
->actions
);
* These should be handled specially.
* But for now I'm ignoring the problem.
* Create a breakpoint for a condition that cannot be pinpointed
* to happening at a particular address, but one for which we
* must single step and check the condition after each statement.
p
= tcontainer(e
->condition
);
actions
= buildcmdlist(build(O_IF
, e
->condition
, e
->actions
));
actions
= buildcmdlist(build(O_TRACEON
, false, actions
));
bp
= bp_alloc(e
, codeloc(p
), 0, actions
);
* Determine the deepest nested subprogram that still contains
* all elements in the given expression.
public Symbol
tcontainer(exp
)
s
= container(exp
->value
.sym
);
} else if (not isleaf(exp
->op
)) {
for (i
= 0; i
< nargs(exp
->op
); i
++) {
t
= tcontainer(exp
->value
.arg
[i
]);
while (u
!= v
and u
!= nil
) {
panic("bad ancestry for \"%s\"", symname(s
));
* Determine if the given function can be executed at full speed.
* This can only be done if there are no breakpoints within the function.
public Boolean
canskip(f
)
foreach (Breakpoint
, p
, bplist
)
if (whatblock(p
->bpaddr
) == f
) {
* Print out what's currently being traced by looking at
* the currently active events.
* Some convolution here to translate internal representation
* of events back into something more palatable.
foreach (Event
, e
, eventlist
)
if (not isredirected()) {
cmd
= list_element(Command
, list_head(e
->actions
));
if (cmd
->op
== O_PRINTCALL
) {
printname(stdout
, cmd
->value
.sym
);
if (list_size(e
->actions
) > 1) {
foreach (Command
, cmd
, e
->actions
)
if (list_size(e
->actions
) > 1) {
private printeventid (id
)
if (cond
->op
== O_EQ
and cond
->value
.arg
[0]->op
== O_SYM
) {
s
= cond
->value
.arg
[0]->value
.sym
;
place
= cond
->value
.arg
[1];
if (place
->value
.sym
!= program
) {
printname(stdout
, place
->value
.sym
);
} else if (s
== linesym
) {
} else if (s
== pcsym
or s
== retaddrsym
) {
* Add a breakpoint to the list and return it.
private Breakpoint
bp_alloc(e
, addr
, line
, actions
)
printf("new bp at 0x%x for event ??\n", addr
, e
->id
);
printf("new bp at 0x%x for event %d\n", addr
, e
->id
);
bplist_append(p
, bplist
);
* Free all storage in the event and breakpoint tables.
foreach (Event
, e
, eventlist
)
if (not delevent(e
->id
)) {
printf("!! dbx.bpfree: can't delete event %d\n", e
->id
);
list_delete(list_curitem(eventlist
), eventlist
);
* Determine if the program stopped at a known breakpoint
* and if so do the associated commands.
foreach (Breakpoint
, p
, bplist
)
printf("breakpoint for event %d found at location 0x%x\n",
if (p
->event
->temporary
) {
if (not delevent(p
->event
->id
)) {
printf("!! dbx.bpact: can't find event %d\n",
* Begin single stepping and executing the given commands after each step.
* If the first argument is true step by instructions, otherwise
* We automatically set a breakpoint at the end of the current procedure
* to turn off the given tracing.
public traceon(inst
, event
, cmdlist
)
trcmd
->cmdlist
= cmdlist
;
list_append(list_item(trcmd
), nil
, eachinst
);
list_append(list_item(trcmd
), nil
, eachline
);
actions
= buildcmdlist(build(O_TRACEOFF
, trcmd
->trid
));
bp
= bp_alloc(event
, (Address
) ret
, 0, actions
);
printf("adding trace %d for event %d\n", trcmd
->trid
, event
->id
);
* Turn off some kind of tracing.
* Strictly an internal command, this cannot be invoked by the user.
foreach (Trcmd
, t
, eachline
)
list_delete(list_curitem(eachline
), eachline
);
foreach (Trcmd
, t
, eachinst
)
if (t
->event
->id
== id
) {
list_delete(list_curitem(eachinst
), eachinst
);
panic("missing trid %d", id
);
if (list_size(eachinst
) == 0) {
if (list_size(eachline
) == 0) {
* If breakpoints are being traced, note that a Trcmd is being deleted.
printf("removing trace %d", t
->trid
);
printf(" for event %d", t
->event
->id
);
* Print out news during single step tracing.
foreach (Trcmd
, t
, eachline
)
foreach (Trcmd
, t
, eachinst
)
* A procedure call/return has occurred while single-stepping,
* note it if we're tracing lines.
private Boolean
chklist();
if (not chklist(eachline
, iscall
)) {
chklist(eachinst
, iscall
);
private Boolean
chklist(list
, iscall
)
setcurfunc(whatblock(pc
));
foreach (Command
, cmd
, t
->cmdlist
)
if (cmd
->op
== O_PRINTSRCPOS
and
(cmd
->value
.arg
[0] == nil
or cmd
->value
.arg
[0]->op
== O_QLINE
)) {
* When tracing variables we keep a copy of their most recent value
* and compare it to the current one each time a breakpoint occurs.
* MAXTRSIZE is the maximum size variable we allow.
* List of variables being watched.
typedef struct Trinfo
*Trinfo
;
* Find the trace information record associated with the given record.
* If there isn't one then create it and add it to the list.
private Trinfo
findtrinfo(p
)
trinfolist
= list_alloc();
foreach (Trinfo
, tp
, trinfolist
)
printf("adding trinfo for \"");
list_append(list_item(tp
), nil
, trinfolist
);
* Print out the value of a variable if it has changed since the
dread(buff
, tp
->traddr
, n
);
if (tp
->trvalue
== nil
) {
tp
->trvalue
= newarr(char, n
);
mov(buff
, tp
->trvalue
, n
);
printf("initially (at line %d):\t", curline
);
} else if (cmp(tp
->trvalue
, buff
, n
) != 0) {
mov(buff
, tp
->trvalue
, n
);
printf("after line %d:\t", prevline
);
* Stop if the value of the given expression has changed.
dread(buff
, tp
->traddr
, n
);
if (tp
->trvalue
== nil
) {
tp
->trvalue
= newarr(char, n
);
mov(buff
, tp
->trvalue
, n
);
} else if (cmp(tp
->trvalue
, buff
, n
) != 0) {
mov(buff
, tp
->trvalue
, n
);
printf("after line %d:\t", prevline
);
* Free the tracing table.
foreach (Trinfo
, tp
, trinfolist
)
list_delete(list_curitem(trinfolist
), trinfolist
);
* Fix up breakpoint information before continuing execution.
* It's necessary to destroy events and breakpoints that were created
* temporarily and still exist because the program terminated abnormally.
foreach (Event
, e
, eventlist
)
if (not delevent(e
->id
)) {
printf("!! dbx.fixbps: can't find event %d\n", e
->id
);
foreach (Trcmd
, t
, eachline
)
list_delete(list_curitem(eachline
), eachline
);
foreach (Trcmd
, t
, eachinst
)
list_delete(list_curitem(eachinst
), eachinst
);
* Set all breakpoints in object code.
foreach (Breakpoint
, p
, bplist
)
* Undo damage done by "setallbps".
foreach (Breakpoint
, p
, bplist
)