package ogle
import (
"debug/proc"
"fmt"
"os"
)
type EventHandler func(e Event) (EventAction, os.Error)
type EventAction int
const (
EARemoveSelf EventAction = 0x100
EADefault EventAction = iota
EAStop
EAContinue
)
type EventHook interface {
AddHandler(EventHandler)
RemoveHandler(EventHandler)
NumHandler() int
handle(e Event) (EventAction, os.Error)
String() string
}
type Event interface {
Process() *Process
Goroutine() *Goroutine
String() string
}
type commonHook struct {
head *handler
len int
}
type handler struct {
eh EventHandler
internal bool
removed bool
next *handler
}
func (h *commonHook) AddHandler(eh EventHandler) {
h.addHandler(eh, false)
}
func (h *commonHook) addHandler(eh EventHandler, internal bool) {
h.RemoveHandler(eh)
if !internal {
h.len++
}
if internal || h.head == nil {
h.head = &handler{eh, internal, false, h.head}
return
}
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 {
p *Process
t *Goroutine
}
func (e *commonEvent) Process() *Process { return e.p }
func (e *commonEvent) Goroutine() *Goroutine { return e.t }
func EventPrint(ev Event) (EventAction, os.Error) {
fmt.Fprintf(os.Stderr, "*** %v\n", ev.String())
return EADefault, nil
}
func EventStop(ev Event) (EventAction, os.Error) {
return EAStop, nil
}
type breakpointHook struct {
commonHook
p *Process
pc proc.Word
}
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) {
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 {
return fmt.Sprintf("breakpoint at %#x", h.pc)
}
func (b *Breakpoint) PC() proc.Word { return b.pc }
func (b *Breakpoint) String() string {
return fmt.Sprintf("breakpoint at %#x", b.pc)
}
type goroutineCreateHook struct {
commonHook
}
func (h *goroutineCreateHook) String() string { return "goroutine create" }
type GoroutineCreate struct {
commonEvent
parent *Goroutine
}
func (e *GoroutineCreate) Parent() *Goroutine { return e.parent }
func (e *GoroutineCreate) String() string {
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" }
type GoroutineExit struct {
commonEvent
}
func (e *GoroutineExit) String() string {
return fmt.Sprintf("goroutine %#x exited", e.t.g.addr().base)
}