summaryrefslogtreecommitdiff
path: root/usr/austin/ogle/event.go
diff options
context:
space:
mode:
Diffstat (limited to 'usr/austin/ogle/event.go')
-rw-r--r--usr/austin/ogle/event.go294
1 files changed, 294 insertions, 0 deletions
diff --git a/usr/austin/ogle/event.go b/usr/austin/ogle/event.go
new file mode 100644
index 000000000..dee1ba58e
--- /dev/null
+++ b/usr/austin/ogle/event.go
@@ -0,0 +1,294 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ogle
+
+import (
+ "fmt";
+ "os";
+ "ptrace";
+)
+
+/*
+ * Hooks and events
+ */
+
+// An EventHandler is a function that takes an event and returns a
+// response to that event and possibly an error. If an event handler
+// returns an error, the process stops and no other handlers for that
+// event are executed.
+type EventHandler func(e Event) (EventAction, os.Error)
+
+// An EventAction is an event handler's response to an event. If all
+// of an event's handlers execute without returning errors, their
+// results are combined as follows: If any handler returned
+// EAContinue, then the process resumes (without returning from
+// WaitStop); otherwise, if any handler returned EAStop, the process
+// remains stopped; otherwise, if all handlers returned EADefault, the
+// process resumes. A handler may return EARemoveSelf bit-wise or'd
+// with any other action to indicate that the handler should be
+// removed from the hook.
+type EventAction int
+
+const (
+ EARemoveSelf EventAction = 0x100;
+ EADefault EventAction = iota;
+ EAStop;
+ EAContinue;
+)
+
+// A EventHook allows event handlers to be added and removed.
+type EventHook interface {
+ AddHandler(EventHandler);
+ RemoveHandler(EventHandler);
+ NumHandler() int;
+ handle(e Event) (EventAction, os.Error);
+ String() string;
+}
+
+// EventHook is almost, but not quite, suitable for user-defined
+// events. If we want user-defined events, make EventHook a struct,
+// special-case adding and removing handlers in breakpoint hooks, and
+// provide a public interface for posting events to hooks.
+
+type Event interface {
+ Process() *Process;
+ Thread() *Thread;
+ String() string;
+}
+
+type commonHook struct {
+ // Head of handler chain
+ head *handler;
+ // Number of non-internal handlers
+ len int;
+}
+
+type handler struct {
+ eh EventHandler;
+ // True if this handler must be run before user-defined
+ // handlers in order to ensure correctness.
+ internal bool;
+ // True if this handler has been removed from the chain.
+ removed bool;
+ next *handler;
+}
+
+func (h *commonHook) AddHandler(eh EventHandler) {
+ h.addHandler(eh, false);
+}
+
+func (h *commonHook) addHandler(eh EventHandler, internal bool) {
+ // Ensure uniqueness of handlers
+ h.RemoveHandler(eh);
+
+ if !internal {
+ h.len++;
+ }
+ // Add internal handlers to the beginning
+ if internal || h.head == nil {
+ h.head = &handler{eh, internal, false, h.head};
+ return;
+ }
+ // Add handler after internal handlers
+ // TODO(austin) This should probably go on the end instead
+ prev := h.head;
+ for prev.next != nil && prev.internal {
+ prev = prev.next;
+ }
+ prev.next = &handler{eh, internal, false, prev.next};
+}
+
+func (h *commonHook) RemoveHandler(eh EventHandler) {
+ plink := &h.head;
+ for l := *plink; l != nil; plink, l = &l.next, l.next {
+ if l.eh == eh {
+ if !l.internal {
+ h.len--;
+ }
+ l.removed = true;
+ *plink = l.next;
+ break;
+ }
+ }
+}
+
+func (h *commonHook) NumHandler() int {
+ return h.len;
+}
+
+func (h *commonHook) handle(e Event) (EventAction, os.Error) {
+ action := EADefault;
+ plink := &h.head;
+ for l := *plink; l != nil; plink, l = &l.next, l.next {
+ if l.removed {
+ continue;
+ }
+ a, err := l.eh(e);
+ if a & EARemoveSelf == EARemoveSelf {
+ if !l.internal {
+ h.len--;
+ }
+ l.removed = true;
+ *plink = l.next;
+ a &^= EARemoveSelf;
+ }
+ if err != nil {
+ return EAStop, err;
+ }
+ if a > action {
+ action = a;
+ }
+ }
+ return action, nil;
+}
+
+type commonEvent struct {
+ // The process of this event
+ p *Process;
+ // The thread of this event.
+ t *Thread;
+}
+
+func (e *commonEvent) Process() *Process {
+ return e.p;
+}
+
+func (e *commonEvent) Thread() *Thread {
+ return e.t;
+}
+
+/*
+ * Standard event handlers
+ */
+
+// EventPrint is a standard event handler that prints events as they
+// occur. It will not cause the process to stop.
+func EventPrint(ev Event) (EventAction, os.Error) {
+ // TODO(austin) Include process name here?
+ fmt.Fprintf(os.Stderr, "*** %v\n", ev.String());
+ return EADefault, nil;
+}
+
+// EventStop is a standard event handler that causes the process to stop.
+func EventStop(ev Event) (EventAction, os.Error) {
+ return EAStop, nil;
+}
+
+/*
+ * Breakpoints
+ */
+
+type breakpointHook struct {
+ commonHook;
+ p *Process;
+ pc ptrace.Word;
+}
+
+// A Breakpoint event occurs when a process reaches a particular
+// program counter. When this event is handled, the current thread
+// will be the thread that reached the program counter.
+type Breakpoint struct {
+ commonEvent;
+ osThread ptrace.Thread;
+ pc ptrace.Word;
+}
+
+func (h *breakpointHook) AddHandler(eh EventHandler) {
+ h.addHandler(eh, false);
+}
+
+func (h *breakpointHook) addHandler(eh EventHandler, internal bool) {
+ // We register breakpoint events lazily to avoid holding
+ // references to breakpoints without handlers. Be sure to use
+ // the "canonical" breakpoint if there is one.
+ if cur, ok := h.p.breakpointHooks[h.pc]; ok {
+ h = cur;
+ }
+ oldhead := h.head;
+ h.commonHook.addHandler(eh, internal);
+ if oldhead == nil && h.head != nil {
+ h.p.proc.AddBreakpoint(h.pc);
+ h.p.breakpointHooks[h.pc] = h;
+ }
+}
+
+func (h *breakpointHook) RemoveHandler(eh EventHandler) {
+ oldhead := h.head;
+ h.commonHook.RemoveHandler(eh);
+ if oldhead != nil && h.head == nil {
+ h.p.proc.RemoveBreakpoint(h.pc);
+ h.p.breakpointHooks[h.pc] = nil, false;
+ }
+}
+
+func (h *breakpointHook) String() string {
+ // TODO(austin) Include process name?
+ // TODO(austin) Use line:pc or at least sym+%#x
+ return fmt.Sprintf("breakpoint at %#x", h.pc);
+}
+
+func (b *Breakpoint) PC() ptrace.Word {
+ return b.pc;
+}
+
+func (b *Breakpoint) String() string {
+ // TODO(austin) Include process name and thread
+ // TODO(austin) Use line:pc or at least sym+%#x
+ return fmt.Sprintf("breakpoint at %#x", b.pc);
+}
+
+/*
+ * Thread create/exit
+ */
+
+type threadCreateHook struct {
+ commonHook;
+}
+
+func (h *threadCreateHook) String() string {
+ return "thread create";
+}
+
+// A ThreadCreate event occurs when a process creates a new Go thread.
+// When this event is handled, the current thread will be the newly
+// created thread.
+type ThreadCreate struct {
+ commonEvent;
+ parent *Thread;
+}
+
+// Parent returns the thread that created this thread. May be nil if
+// this event is the creation of the first thread.
+func (e *ThreadCreate) Parent() *Thread {
+ return e.parent;
+}
+
+func (e *ThreadCreate) String() string {
+ // TODO(austin) Include process name
+ if e.parent == nil {
+ return fmt.Sprintf("%v created", e.t);
+ }
+ return fmt.Sprintf("%v created by %v", e.t, e.parent);
+}
+
+type threadExitHook struct {
+ commonHook;
+}
+
+func (h *threadExitHook) String() string {
+ return "thread exit";
+}
+
+// A ThreadExit event occurs when a Go thread exits.
+type ThreadExit struct {
+ commonEvent;
+}
+
+func (e *ThreadExit) String() string {
+ // TODO(austin) Include process name
+ //return fmt.Sprintf("%v exited", e.t);
+ // For debugging purposes
+ return fmt.Sprintf("thread %#x exited", e.t.g.addr().base);
+}