]> Cypherpunks repositories - gostls13.git/commitdiff
Implement Go threads. Implement a general event system
authorAustin Clements <aclements@csail.mit.edu>
Thu, 3 Sep 2009 23:59:41 +0000 (16:59 -0700)
committerAustin Clements <aclements@csail.mit.edu>
Thu, 3 Sep 2009 23:59:41 +0000 (16:59 -0700)
including breakpoints and Go thread create/exit.

R=rsc
APPROVED=rsc
DELTA=751  (729 added, 6 deleted, 16 changed)
OCL=34345
CL=34351

usr/austin/ogle/event.go [new file with mode: 0644]
usr/austin/ogle/frame.go
usr/austin/ogle/process.go
usr/austin/ogle/rruntime.go
usr/austin/ogle/rtype.go
usr/austin/ogle/thread.go [new file with mode: 0644]

diff --git a/usr/austin/ogle/event.go b/usr/austin/ogle/event.go
new file mode 100644 (file)
index 0000000..dee1ba5
--- /dev/null
@@ -0,0 +1,294 @@
+// 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);
+}
index 4a4fd9a43b2d96610afd2c6d58e576ee7589300f..522c263b1c54b87f754a234e01c5b7f684f07f28 100644 (file)
@@ -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;
 }
index 9dc5bc909052fd02425f117a8e3672afedd5eb4a..f1e7524b4807b532cd382a6e169e5c8f61a0e4df 100644 (file)
@@ -6,9 +6,11 @@ package ogle
 
 import (
        "eval";
+       "fmt";
+       "log";
+       "os";
        "ptrace";
        "reflect";
-       "os";
        "sym";
 )
 
@@ -37,17 +39,33 @@ func (e ProcessNotStopped) String() string {
        return "process not stopped";
 }
 
+// An UnknownThread error is an internal error representing an
+// unrecognized G structure pointer.
+type UnknownThread struct {
+       OSThread ptrace.Thread;
+       GoThread ptrace.Word;
+}
+
+func (e UnknownThread) String() string {
+       return fmt.Sprintf("internal error: unknown thread (G %#x)", e.GoThread);
+}
+
+// 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 {}
+
+func (e NoCurrentThread) String() string {
+       return "no current thread";
+}
+
 // A Process represents a remote attached process.
 type Process struct {
        Arch;
-       ptrace.Process;
+       proc ptrace.Process;
 
        // The symbol table of this process
        syms *sym.GoSymTable;
 
-       // Current frame, or nil if the current thread is not stopped
-       frame *Frame;
-
        // A possibly-stopped OS thread, or nil
        threadCache ptrace.Thread;
 
@@ -62,24 +80,76 @@ type Process struct {
 
        // Globals from the sys package (or from no package)
        sys struct {
-               lessstack, goexit, newproc, deferproc *sym.TextSym;
+               lessstack, goexit, newproc, deferproc, newprocreadylocked *sym.TextSym;
+               allg remotePtr;
+               g0 remoteStruct;
        };
+
+       // Event queue
+       posted []Event;
+       pending []Event;
+       event Event;
+
+       // Event hooks
+       breakpointHooks map[ptrace.Word] *breakpointHook;
+       threadCreateHook *threadCreateHook;
+       threadExitHook *threadExitHook;
+
+       // Current thread, or nil if there are no threads
+       curThread *Thread;
+
+       // Threads by the address of their G structure
+       threads map[ptrace.Word] *Thread;
 }
 
+/*
+ * Process creation
+ */
+
 // NewProcess constructs a new remote process around a ptrace'd
 // process, an architecture, and a symbol table.
-func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) *Process {
+func NewProcess(proc ptrace.Process, arch Arch, syms *sym.GoSymTable) (*Process, os.Error) {
        p := &Process{
                Arch: arch,
-               Process: proc,
+               proc: proc,
                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),
        };
 
-       // TODO(austin) Set p.frame if proc is stopped
-
+       // Fill in remote runtime
        p.bootstrap();
-       return p;
+
+       switch {
+       case p.sys.allg.addr().base == 0:
+               return nil, FormatError("failed to find runtime symbol 'allg'");
+       case p.sys.g0.addr().base == 0:
+               return nil, FormatError("failed to find runtime symbol 'g0'");
+       case p.sys.newprocreadylocked == nil:
+               return nil, FormatError("failed to find runtime symbol 'newprocreadylocked'");
+       case p.sys.goexit == nil:
+               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};
+       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};
+               g = gs.Field(p.f.G.Alllink).(remotePtr).Get();
+       }
+       p.selectSomeThread();
+
+       // Create internal breakpoints to catch new and exited threads
+       p.OnBreakpoint(ptrace.Word(p.sys.newprocreadylocked.Entry())).(*breakpointHook).addHandler(readylockedBP, true);
+       p.OnBreakpoint(ptrace.Word(p.sys.goexit.Entry())).(*breakpointHook).addHandler(goexitBP, true);
+
+       return p, nil;
 }
 
 // NewProcessElf constructs a new remote process around a ptrace'd
@@ -99,7 +169,7 @@ func NewProcessElf(proc ptrace.Process, elf *sym.Elf) (*Process, os.Error) {
        default:
                return nil, UnknownArchitecture(elf.Machine);
        }
-       return NewProcess(proc, arch, syms), nil;
+       return NewProcess(proc, arch, syms);
 }
 
 // bootstrap constructs the runtime structure of a remote process.
@@ -154,16 +224,39 @@ func (p *Process) bootstrap() {
        p.sys.goexit = globalFn("goexit");
        p.sys.newproc = globalFn("sys·newproc");
        p.sys.deferproc = globalFn("sys·deferproc");
+       p.sys.newprocreadylocked = globalFn("newprocreadylocked");
+       if allg := p.syms.SymFromName("allg"); allg != nil {
+               p.sys.allg = remotePtr{remote{ptrace.Word(allg.Common().Value), p}, p.runtime.G};
+       }
+       if g0 := p.syms.SymFromName("g0"); g0 != nil {
+               p.sys.g0 = p.runtime.G.mk(remote{ptrace.Word(g0.Common().Value), p}).(remoteStruct);
+       }
+}
+
+func (p *Process) selectSomeThread() {
+       // Once we have friendly thread ID's, there might be a more
+       // reasonable behavior for this.
+       p.curThread = nil;
+       for _, t := range p.threads {
+               if !t.isG0() {
+                       p.curThread = t;
+                       return;
+               }
+       }
 }
 
-func (p *Process) someStoppedThread() ptrace.Thread {
+/*
+ * Process memory
+ */
+
+func (p *Process) someStoppedOSThread() ptrace.Thread {
        if p.threadCache != nil {
                if _, err := p.threadCache.Stopped(); err == nil {
                        return p.threadCache;
                }
        }
 
-       for _, t := range p.Threads() {
+       for _, t := range p.proc.Threads() {
                if _, err := t.Stopped(); err == nil {
                        p.threadCache = t;
                        return t;
@@ -173,7 +266,7 @@ func (p *Process) someStoppedThread() ptrace.Thread {
 }
 
 func (p *Process) Peek(addr ptrace.Word, out []byte) (int, os.Error) {
-       thr := p.someStoppedThread();
+       thr := p.someStoppedOSThread();
        if thr == nil {
                return 0, ProcessNotStopped{};
        }
@@ -181,7 +274,7 @@ func (p *Process) Peek(addr ptrace.Word, out []byte) (int, os.Error) {
 }
 
 func (p *Process) Poke(addr ptrace.Word, b []byte) (int, os.Error) {
-       thr := p.someStoppedThread();
+       thr := p.someStoppedOSThread();
        if thr == nil {
                return 0, ProcessNotStopped{};
        }
@@ -191,3 +284,225 @@ func (p *Process) Poke(addr ptrace.Word, b []byte) (int, os.Error) {
 func (p *Process) peekUintptr(addr ptrace.Word) ptrace.Word {
        return ptrace.Word(mkUintptr(remote{addr, p}).(remoteUint).Get());
 }
+
+/*
+ * Events
+ */
+
+// OnBreakpoint returns the hook that is run when the program reaches
+// the given program counter.
+func (p *Process) OnBreakpoint(pc ptrace.Word) EventHook {
+       if bp, ok := p.breakpointHooks[pc]; ok {
+               return bp;
+       }
+       // The breakpoint will register itself when a handler is added
+       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;
+}
+
+// OnThreadExit returns the hook 
+func (p *Process) OnThreadExit() EventHook {
+       return p.threadExitHook;
+}
+
+// osThreadToThread looks up the Go thread running on an OS thread.
+func (p *Process) osThreadToThread(t ptrace.Thread) (*Thread, os.Error) {
+       regs, err := t.Regs();
+       if err != nil {
+               return nil, err;
+       }
+       g := p.G(regs);
+       gt, ok := p.threads[g];
+       if !ok {
+               return nil, UnknownThread{t, g};
+       }
+       return gt, nil;
+}
+
+// causesToEvents translates the stop causes of the underlying process
+// into an event queue.
+func (p *Process) causesToEvents() ([]Event, os.Error) {
+       // Count causes we're interested in
+       nev := 0;
+       for _, t := range p.proc.Threads() {
+               if c, err := t.Stopped(); err == nil {
+                       switch c := c.(type) {
+                       case ptrace.Breakpoint:
+                               nev++;
+                       case ptrace.Signal:
+                               // TODO(austin)
+                               //nev++;
+                       }
+               }
+       }
+
+       // Translate causes to events
+       events := make([]Event, nev);
+       i := 0;
+       for _, t := range p.proc.Threads() {
+               if c, err := t.Stopped(); err == nil {
+                       switch c := c.(type) {
+                       case ptrace.Breakpoint:
+                               gt, err := p.osThreadToThread(t);
+                               if err != nil {
+                                       return nil, err;
+                               }
+                               events[i] = &Breakpoint{commonEvent{p, gt}, t, ptrace.Word(c)};
+                               i++;
+                       case ptrace.Signal:
+                               // TODO(austin)
+                       }
+               }
+       }
+
+       return events, nil;
+}
+
+// postEvent appends an event to the posted queue.  These events will
+// be processed before any currently pending events.
+func (p *Process) postEvent(ev Event) {
+       n := len(p.posted);
+       m := n*2;
+       if m == 0 {
+               m = 4;
+       }
+       posted := make([]Event, n+1, m);
+       for i, p := range p.posted {
+               posted[i] = p;
+       }
+       posted[n] = ev;
+       p.posted = posted;
+}
+
+// processEvents processes events in the event queue until no events
+// remain, a handler returns EAStop, or a handler returns an error.
+// It returns either EAStop or EAContinue and possibly an error.
+func (p *Process) processEvents() (EventAction, os.Error) {
+       var ev Event;
+       for len(p.posted) > 0 {
+               ev, p.posted = p.posted[0], p.posted[1:len(p.posted)];
+               action, err := p.processEvent(ev);
+               if action == EAStop {
+                       return action, err;
+               }
+       }
+
+       for len(p.pending) > 0 {
+               ev, p.pending = p.pending[0], p.pending[1:len(p.pending)];
+               action, err := p.processEvent(ev);
+               if action == EAStop {
+                       return action, err;
+               }
+       }
+
+       return EAContinue, nil;
+}
+
+// processEvent processes a single event, without manipulating the
+// event queues.  It returns either EAStop or EAContinue and possibly
+// an error.
+func (p *Process) processEvent(ev Event) (EventAction, os.Error) {
+       p.event = ev;
+
+       var action EventAction;
+       var err os.Error;
+       switch ev := p.event.(type) {
+       case *Breakpoint:
+               hook, ok := p.breakpointHooks[ev.pc];
+               if !ok {
+                       break;
+               }
+               p.curThread = ev.Thread();
+               action, err = hook.handle(ev);
+
+       case *ThreadCreate:
+               p.curThread = ev.Thread();
+               action, err = p.threadCreateHook.handle(ev);
+
+       case *ThreadExit:
+               action, err = p.threadExitHook.handle(ev);
+
+       default:
+               log.Crashf("Unknown event type %T in queue", p.event);
+       }
+
+       if err != nil {
+               return EAStop, err;
+       } else if action == EAStop {
+               return EAStop, nil;
+       }
+       return EAContinue, nil;
+}
+
+// Event returns the last event that caused the process to stop.  This
+// may return nil if the process has never been stopped by an event.
+//
+// TODO(austin) Return nil if the user calls p.Stop()?
+func (p *Process) Event() Event {
+       return p.event;
+}
+
+/*
+ * Process control
+ */
+
+// TODO(austin) Cont, WaitStop, and Stop.  Need to figure out how
+// event handling works with these.  Originally I did it only in
+// WaitStop, but if you Cont and there are pending events, then you
+// have to not actually continue and wait until a WaitStop to process
+// them, even if the event handlers will tell you to continue.  We
+// could handle them in both Cont and WaitStop to avoid this problem,
+// but it's still weird if an event happens after the Cont and before
+// the WaitStop that the handlers say to continue from.  Or we could
+// handle them on a separate thread.  Then obviously you get weird
+// asynchrony things, like prints while the user it typing a command,
+// but that's not necessarily a bad thing.
+
+// ContWait resumes process execution and waits for an event to occur
+// that stops the process.
+func (p *Process) ContWait() os.Error {
+       for {
+               a, err := p.processEvents();
+               if err != nil {
+                       return err;
+               } else if a == EAStop {
+                       break;
+               }
+               err = p.proc.Continue();
+               if err != nil {
+                       return err;
+               }
+               err = p.proc.WaitStop();
+               if err != nil {
+                       return err;
+               }
+               for _, t := range p.threads {
+                       t.resetFrame();
+               }
+               p.pending, err = p.causesToEvents();
+               if err != nil {
+                       return err;
+               }
+       }
+       return nil;
+}
+
+// Out selects the caller frame of the current frame.
+func (p *Process) Out() os.Error {
+       if p.curThread == nil {
+               return NoCurrentThread{};
+       }
+       return p.curThread.Out();
+}
+
+// In selects the frame called by the current frame.
+func (p *Process) In() os.Error {
+       if p.curThread == nil {
+               return NoCurrentThread{};
+       }
+       return p.curThread.In();
+}
index e0a6546912722297c16aa55b25f8f171bbb8669f..758f1c7084f879c903ff03de8ac6b2f1a50ad58c 100644 (file)
@@ -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;
        };
 }
 
index 8565949369ee6288050b4bbf72785baaeccae453..5bca923ce8e92fdb3a52c01af95d2ccce8ccb340 100644 (file)
@@ -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());
diff --git a/usr/austin/ogle/thread.go b/usr/austin/ogle/thread.go
new file mode 100644 (file)
index 0000000..888d01e
--- /dev/null
@@ -0,0 +1,114 @@
+// 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;
+}