// 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 ( "eval"; "fmt"; "os"; "ptrace"; ) // A Goroutine represents a goroutine in a remote process. type Goroutine struct { g remoteStruct; frame *Frame; dead bool; } func (t *Goroutine) String() string { if t.dead { return ""; } // 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 *Goroutine) isG0() bool { return t.g.addr().base == t.g.r.p.sys.g0.addr().base; } func (t *Goroutine) resetFrame() (err os.Error) { // TODO(austin) Reuse any live part of the current frame stack // so existing references to Frame's keep working. t.frame, err = newFrame(t.g); return; } // Out selects the caller frame of the current frame. func (t *Goroutine) Out() os.Error { f, err := t.frame.Outer(); if f != nil { t.frame = f; } return err; } // In selects the frame called by the current frame. func (t *Goroutine) 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}; var gp eval.Value; err = try(func(a aborter) { gp = arg.aGet(a) }); if err != nil { return EAStop, err; } if gp == nil { return EAStop, UnknownGoroutine{b.osThread, 0}; } gs := gp.(remoteStruct); g := &Goroutine{gs, nil, false}; p.goroutines[gs.addr().base] = g; // Enqueue goroutine creation event parent := b.Goroutine(); if parent.isG0() { parent = nil; } p.postEvent(&GoroutineCreate{commonEvent{p, g}, parent}); // If we don't have any thread selected, select this one if p.curGoroutine == nil { p.curGoroutine = g; } return EADefault, nil; } func goexitBP(ev Event) (EventAction, os.Error) { b := ev.(*Breakpoint); p := b.Process(); g := b.Goroutine(); g.dead = true; addr := g.g.addr().base; p.goroutines[addr] = nil, false; // Enqueue thread exit event p.postEvent(&GoroutineExit{commonEvent{p, g}}); // If we just exited our selected goroutine, selected another if p.curGoroutine == g { p.selectSomeGoroutine(); } return EADefault, nil; }