| // 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 ( |
| "debug/proc"; |
| "fmt"; |
| "os"; |
| ) |
| |
| /* |
| * 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; |
| Goroutine() *Goroutine; |
| 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 goroutine of this event. |
| t *Goroutine; |
| } |
| |
| func (e *commonEvent) Process() *Process { return e.p } |
| |
| func (e *commonEvent) Goroutine() *Goroutine { 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 proc.Word; |
| } |
| |
| // A Breakpoint event occurs when a process reaches a particular |
| // program counter. When this event is handled, the current goroutine |
| // will be the goroutine that reached the program counter. |
| type Breakpoint struct { |
| commonEvent; |
| osThread proc.Thread; |
| pc proc.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() proc.Word { return b.pc } |
| |
| func (b *Breakpoint) String() string { |
| // TODO(austin) Include process name and goroutine |
| // TODO(austin) Use line:pc or at least sym+%#x |
| return fmt.Sprintf("breakpoint at %#x", b.pc) |
| } |
| |
| /* |
| * Goroutine create/exit |
| */ |
| |
| type goroutineCreateHook struct { |
| commonHook; |
| } |
| |
| func (h *goroutineCreateHook) String() string { return "goroutine create" } |
| |
| // A GoroutineCreate event occurs when a process creates a new |
| // goroutine. When this event is handled, the current goroutine will |
| // be the newly created goroutine. |
| type GoroutineCreate struct { |
| commonEvent; |
| parent *Goroutine; |
| } |
| |
| // Parent returns the goroutine that created this goroutine. May be |
| // nil if this event is the creation of the first goroutine. |
| func (e *GoroutineCreate) Parent() *Goroutine { return e.parent } |
| |
| func (e *GoroutineCreate) 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 goroutineExitHook struct { |
| commonHook; |
| } |
| |
| func (h *goroutineExitHook) String() string { return "goroutine exit" } |
| |
| // A GoroutineExit event occurs when a Go goroutine exits. |
| type GoroutineExit struct { |
| commonEvent; |
| } |
| |
| func (e *GoroutineExit) String() string { |
| // TODO(austin) Include process name |
| //return fmt.Sprintf("%v exited", e.t); |
| // For debugging purposes |
| return fmt.Sprintf("goroutine %#x exited", e.t.g.addr().base) |
| } |