mirror of
https://github.com/golang/go
synced 2024-11-25 17:47:57 -07:00
295 lines
6.8 KiB
Go
295 lines
6.8 KiB
Go
|
// 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);
|
||
|
}
|