| // 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) |
| } |