Commit db27b5bc authored by Austin Clements's avatar Austin Clements

Implement Go threads. Implement a general event system

including breakpoints and Go thread create/exit.

R=rsc
APPROVED=rsc
DELTA=751  (729 added, 6 deleted, 16 changed)
OCL=34345
CL=34351
parent 55ba20ec
// 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);
}
......@@ -47,7 +47,7 @@ func NewFrame(g remoteStruct) *Frame {
// figure out if it's on an OS thread or not. However, this
// is difficult because the state isn't updated atomically
// with scheduling changes.
for _, t := range p.Threads() {
for _, t := range p.proc.Threads() {
regs, err := t.Regs();
if err != nil {
// TODO(austin) What to do?
......@@ -182,6 +182,8 @@ func (f *Frame) Outer() *Frame {
return nil;
}
// TODO(austin) Register this frame for shoot-down.
f.outer = prepareFrame(pc, sp, f.stk, f);
return f.outer;
}
......
This diff is collapsed.
......@@ -110,20 +110,24 @@ type rt1ArrayType struct {
* See $GOROOT/src/pkg/runtime/runtime.h
*/
// Fields beginning with _ are only for padding
type rt1Stktop struct {
stackguard uintptr;
stackbase *rt1Stktop;
gobuf rt1Gobuf;
_args uint32;
_fp uintptr;
}
type rt1Gobuf struct {
sp uintptr;
pc uintptr;
g *rt1G;
r0 uintptr;
}
type rt1G struct {
// Fields beginning with _ are only for padding
_stackguard uintptr;
stackbase *rt1Stktop;
_defer uintptr;
......@@ -133,6 +137,7 @@ type rt1G struct {
alllink *rt1G;
_param uintptr;
status int16;
// Incomplete
}
var rt1GStatus = runtimeGStatus{
......@@ -193,7 +198,7 @@ type runtimeIndexes struct {
Sp, Pc, G int;
};
G struct {
Stackbase, Sched, Status int;
Stackbase, Sched, Status, Alllink int;
};
}
......
......@@ -54,6 +54,7 @@ func newManualType(t eval.Type, arch Arch) *remoteType {
basicType(eval.Uint8Type, mkUint8, 1, 0);
basicType(eval.Uint32Type, mkUint32, 4, 0);
basicType(eval.UintptrType, mkUintptr, arch.PtrSize(), 0);
basicType(eval.Int16Type, mkInt16, 2, 0);
basicType(eval.Int32Type, mkInt32, 4, 0);
basicType(eval.IntType, mkInt, arch.IntSize(), 0);
basicType(eval.StringType, mkString, arch.PtrSize() + arch.IntSize(), arch.PtrSize());
......
// 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";
)
// A Thread represents a Go thread.
type Thread struct {
g remoteStruct;
frame *Frame;
dead bool;
}
func (t *Thread) String() string {
if t.dead {
return "<dead thread>";
}
// TODO(austin) Give threads friendly ID's
return fmt.Sprintf("thread %#x", t.g.addr().base);
}
// isG0 returns true if this thread if the internal idle thread
func (t *Thread) isG0() bool {
return t.g.addr().base == t.g.r.p.sys.g0.addr().base;
}
func (t *Thread) resetFrame() {
// TODO(austin) NewFrame can abort
// TODO(austin) Reuse any live part of the current frame stack
// so existing references to Frame's keep working.
t.frame = NewFrame(t.g);
}
// Out selects the caller frame of the current frame.
func (t *Thread) Out() os.Error {
// TODO(austin) Outer can abort
f := t.frame.Outer();
if f != nil {
t.frame = f;
}
return nil;
}
// In selects the frame called by the current frame.
func (t *Thread) In() os.Error {
f := t.frame.Inner();
if f != nil {
t.frame = f;
}
return nil;
}
func readylockedBP(ev Event) (EventAction, os.Error) {
b := ev.(*Breakpoint);
p := b.Process();
// The new g is the only argument to this function, so the
// stack will have the return address, then the G*.
regs, err := b.osThread.Regs();
if err != nil {
return EAStop, err;
}
sp := regs.SP();
addr := sp + ptrace.Word(p.PtrSize());
arg := remotePtr{remote{addr, p}, p.runtime.G};
g := arg.Get();
if g == nil {
return EAStop, UnknownThread{b.osThread, 0};
}
gs := g.(remoteStruct);
t := &Thread{gs, nil, false};
p.threads[gs.addr().base] = t;
// Enqueue thread creation event
parent := b.Thread();
if parent.isG0() {
parent = nil;
}
p.postEvent(&ThreadCreate{commonEvent{p, t}, parent});
// If we don't have any thread selected, select this one
if p.curThread == nil {
p.curThread = t;
}
return EADefault, nil;
}
func goexitBP(ev Event) (EventAction, os.Error) {
b := ev.(*Breakpoint);
p := b.Process();
t := b.Thread();
t.dead = true;
addr := t.g.addr().base;
p.threads[addr] = nil, false;
// Enqueue thread exit event
p.postEvent(&ThreadExit{commonEvent{p, t}});
// If we just exited our selected thread, selected another
if p.curThread == t {
p.selectSomeThread();
}
return EADefault, nil;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment