type Event interface {
Process() *Process;
- Thread() *Thread;
+ Goroutine() *Goroutine;
String() string;
}
type commonEvent struct {
// The process of this event
p *Process;
- // The thread of this event.
- t *Thread;
+ // The goroutine of this event.
+ t *Goroutine;
}
func (e *commonEvent) Process() *Process {
return e.p;
}
-func (e *commonEvent) Thread() *Thread {
+func (e *commonEvent) Goroutine() *Goroutine {
return e.t;
}
}
// 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.
+// program counter. When this event is handled, the current goroutine
+// will be the goroutine that reached the program counter.
type Breakpoint struct {
commonEvent;
osThread ptrace.Thread;
}
func (b *Breakpoint) String() string {
- // TODO(austin) Include process name and thread
+ // 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);
}
/*
- * Thread create/exit
+ * Goroutine create/exit
*/
-type threadCreateHook struct {
+type goroutineCreateHook struct {
commonHook;
}
-func (h *threadCreateHook) String() string {
- return "thread create";
+func (h *goroutineCreateHook) String() string {
+ return "goroutine 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 {
+// 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 *Thread;
+ parent *Goroutine;
}
-// 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 {
+// 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 *ThreadCreate) String() string {
+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 threadExitHook struct {
+type goroutineExitHook struct {
commonHook;
}
-func (h *threadExitHook) String() string {
- return "thread exit";
+func (h *goroutineExitHook) String() string {
+ return "goroutine exit";
}
-// A ThreadExit event occurs when a Go thread exits.
-type ThreadExit struct {
+// A GoroutineExit event occurs when a Go goroutine exits.
+type GoroutineExit struct {
commonEvent;
}
-func (e *ThreadExit) String() string {
+func (e *GoroutineExit) 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);
+ return fmt.Sprintf("goroutine %#x exited", e.t.g.addr().base);
}
"ptrace";
)
-// A Thread represents a Go thread.
-type Thread struct {
+// A Goroutine represents a goroutine in a remote process.
+type Goroutine struct {
g remoteStruct;
frame *Frame;
dead bool;
}
-func (t *Thread) String() string {
+func (t *Goroutine) String() string {
if t.dead {
return "<dead thread>";
}
- // TODO(austin) Give threads friendly ID's
+ // TODO(austin) Give threads friendly ID's, possibly including
+ // the name of the entry function.
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 {
+func (t *Goroutine) isG0() bool {
return t.g.addr().base == t.g.r.p.sys.g0.addr().base;
}
-func (t *Thread) resetFrame() {
+func (t *Goroutine) 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.
}
// Out selects the caller frame of the current frame.
-func (t *Thread) Out() os.Error {
+func (t *Goroutine) Out() os.Error {
// TODO(austin) Outer can abort
f := t.frame.Outer();
if f != nil {
}
// In selects the frame called by the current frame.
-func (t *Thread) In() os.Error {
+func (t *Goroutine) In() os.Error {
f := t.frame.Inner();
if f != nil {
t.frame = f;
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};
+ gp := arg.Get();
+ if gp == nil {
+ return EAStop, UnknownGoroutine{b.osThread, 0};
}
- gs := g.(remoteStruct);
- t := &Thread{gs, nil, false};
- p.threads[gs.addr().base] = t;
+ gs := gp.(remoteStruct);
+ g := &Goroutine{gs, nil, false};
+ p.goroutines[gs.addr().base] = g;
- // Enqueue thread creation event
- parent := b.Thread();
+ // Enqueue goroutine creation event
+ parent := b.Goroutine();
if parent.isG0() {
parent = nil;
}
- p.postEvent(&ThreadCreate{commonEvent{p, t}, parent});
+ p.postEvent(&GoroutineCreate{commonEvent{p, g}, parent});
// If we don't have any thread selected, select this one
- if p.curThread == nil {
- p.curThread = t;
+ if p.curGoroutine == nil {
+ p.curGoroutine = g;
}
return EADefault, nil;
b := ev.(*Breakpoint);
p := b.Process();
- t := b.Thread();
- t.dead = true;
+ g := b.Goroutine();
+ g.dead = true;
- addr := t.g.addr().base;
- p.threads[addr] = nil, false;
+ addr := g.g.addr().base;
+ p.goroutines[addr] = nil, false;
// Enqueue thread exit event
- p.postEvent(&ThreadExit{commonEvent{p, t}});
+ p.postEvent(&GoroutineExit{commonEvent{p, g}});
- // If we just exited our selected thread, selected another
- if p.curThread == t {
- p.selectSomeThread();
+ // If we just exited our selected goroutine, selected another
+ if p.curGoroutine == g {
+ p.selectSomeGoroutine();
}
return EADefault, nil;
return "process not stopped";
}
-// An UnknownThread error is an internal error representing an
+// An UnknownGoroutine error is an internal error representing an
// unrecognized G structure pointer.
-type UnknownThread struct {
+type UnknownGoroutine struct {
OSThread ptrace.Thread;
- GoThread ptrace.Word;
+ Goroutine ptrace.Word;
}
-func (e UnknownThread) String() string {
- return fmt.Sprintf("internal error: unknown thread (G %#x)", e.GoThread);
+func (e UnknownGoroutine) String() string {
+ return fmt.Sprintf("internal error: unknown goroutine (G %#x)", e.Goroutine);
}
-// A NoCurrentThread error occurs when no thread is currently selected
-// in a process (or when there are no threads in a process).
-type NoCurrentThread struct {}
+// A NoCurrentGoroutine error occurs when no goroutine is currently
+// selected in a process (or when there are no goroutines in a
+// process).
+type NoCurrentGoroutine struct {}
-func (e NoCurrentThread) String() string {
- return "no current thread";
+func (e NoCurrentGoroutine) String() string {
+ return "no current goroutine";
}
// A Process represents a remote attached process.
// Event hooks
breakpointHooks map[ptrace.Word] *breakpointHook;
- threadCreateHook *threadCreateHook;
- threadExitHook *threadExitHook;
+ goroutineCreateHook *goroutineCreateHook;
+ goroutineExitHook *goroutineExitHook;
- // Current thread, or nil if there are no threads
- curThread *Thread;
+ // Current goroutine, or nil if there are no goroutines
+ curGoroutine *Goroutine;
- // Threads by the address of their G structure
- threads map[ptrace.Word] *Thread;
+ // Goroutines by the address of their G structure
+ goroutines map[ptrace.Word] *Goroutine;
}
/*
syms: syms,
types: make(map[ptrace.Word] *remoteType),
breakpointHooks: make(map[ptrace.Word] *breakpointHook),
- threadCreateHook: new(threadCreateHook),
- threadExitHook: new(threadExitHook),
- threads: make(map[ptrace.Word] *Thread),
+ goroutineCreateHook: new(goroutineCreateHook),
+ goroutineExitHook: new(goroutineExitHook),
+ goroutines: make(map[ptrace.Word] *Goroutine),
};
// Fill in remote runtime
return nil, FormatError("failed to find runtime symbol 'sys.goexit'");
}
- // Get current threads
- p.threads[p.sys.g0.addr().base] = &Thread{p.sys.g0, nil, false};
+ // Get current goroutines
+ p.goroutines[p.sys.g0.addr().base] = &Goroutine{p.sys.g0, nil, false};
g := p.sys.allg.Get();
for g != nil {
gs := g.(remoteStruct);
- fmt.Printf("*** Found thread at %#x\n", gs.addr().base);
- p.threads[gs.addr().base] = &Thread{gs, nil, false};
+ fmt.Printf("*** Found goroutine at %#x\n", gs.addr().base);
+ p.goroutines[gs.addr().base] = &Goroutine{gs, nil, false};
g = gs.Field(p.f.G.Alllink).(remotePtr).Get();
}
- p.selectSomeThread();
+ p.selectSomeGoroutine();
- // Create internal breakpoints to catch new and exited threads
+ // Create internal breakpoints to catch new and exited goroutines
p.OnBreakpoint(ptrace.Word(p.sys.newprocreadylocked.Entry())).(*breakpointHook).addHandler(readylockedBP, true);
p.OnBreakpoint(ptrace.Word(p.sys.goexit.Entry())).(*breakpointHook).addHandler(goexitBP, true);
}
}
-func (p *Process) selectSomeThread() {
- // Once we have friendly thread ID's, there might be a more
+func (p *Process) selectSomeGoroutine() {
+ // Once we have friendly goroutine ID's, there might be a more
// reasonable behavior for this.
- p.curThread = nil;
- for _, t := range p.threads {
+ p.curGoroutine = nil;
+ for _, t := range p.goroutines {
if !t.isG0() {
- p.curThread = t;
+ p.curGoroutine = t;
return;
}
}
return &breakpointHook{commonHook{nil, 0}, p, pc};
}
-// OnThreadCreate returns the hook that is run when a Go thread is created.
-func (p *Process) OnThreadCreate() EventHook {
- return p.threadCreateHook;
+// OnGoroutineCreate returns the hook that is run when a goroutine is created.
+func (p *Process) OnGoroutineCreate() EventHook {
+ return p.goroutineCreateHook;
}
-// OnThreadExit returns the hook
-func (p *Process) OnThreadExit() EventHook {
- return p.threadExitHook;
+// OnGoroutineExit returns the hook that is run when a goroutine exits.
+func (p *Process) OnGoroutineExit() EventHook {
+ return p.goroutineExitHook;
}
-// osThreadToThread looks up the Go thread running on an OS thread.
-func (p *Process) osThreadToThread(t ptrace.Thread) (*Thread, os.Error) {
+// osThreadToGoroutine looks up the goroutine running on an OS thread.
+func (p *Process) osThreadToGoroutine(t ptrace.Thread) (*Goroutine, os.Error) {
regs, err := t.Regs();
if err != nil {
return nil, err;
}
g := p.G(regs);
- gt, ok := p.threads[g];
+ gt, ok := p.goroutines[g];
if !ok {
- return nil, UnknownThread{t, g};
+ return nil, UnknownGoroutine{t, g};
}
return gt, nil;
}
if c, err := t.Stopped(); err == nil {
switch c := c.(type) {
case ptrace.Breakpoint:
- gt, err := p.osThreadToThread(t);
+ gt, err := p.osThreadToGoroutine(t);
if err != nil {
return nil, err;
}
if !ok {
break;
}
- p.curThread = ev.Thread();
+ p.curGoroutine = ev.Goroutine();
action, err = hook.handle(ev);
- case *ThreadCreate:
- p.curThread = ev.Thread();
- action, err = p.threadCreateHook.handle(ev);
+ case *GoroutineCreate:
+ p.curGoroutine = ev.Goroutine();
+ action, err = p.goroutineCreateHook.handle(ev);
- case *ThreadExit:
- action, err = p.threadExitHook.handle(ev);
+ case *GoroutineExit:
+ action, err = p.goroutineExitHook.handle(ev);
default:
log.Crashf("Unknown event type %T in queue", p.event);
if err != nil {
return err;
}
- for _, t := range p.threads {
+ for _, t := range p.goroutines {
t.resetFrame();
}
p.pending, err = p.causesToEvents();
// Out selects the caller frame of the current frame.
func (p *Process) Out() os.Error {
- if p.curThread == nil {
- return NoCurrentThread{};
+ if p.curGoroutine == nil {
+ return NoCurrentGoroutine{};
}
- return p.curThread.Out();
+ return p.curGoroutine.Out();
}
// In selects the frame called by the current frame.
func (p *Process) In() os.Error {
- if p.curThread == nil {
- return NoCurrentThread{};
+ if p.curGoroutine == nil {
+ return NoCurrentGoroutine{};
}
- return p.curThread.In();
+ return p.curGoroutine.In();
}