diff options
Diffstat (limited to 'libgo/go/exp/ogle')
-rw-r--r-- | libgo/go/exp/ogle/abort.go | 35 | ||||
-rw-r--r-- | libgo/go/exp/ogle/arch.go | 125 | ||||
-rw-r--r-- | libgo/go/exp/ogle/cmd.go | 373 | ||||
-rw-r--r-- | libgo/go/exp/ogle/event.go | 280 | ||||
-rw-r--r-- | libgo/go/exp/ogle/frame.go | 212 | ||||
-rw-r--r-- | libgo/go/exp/ogle/goroutine.go | 117 | ||||
-rw-r--r-- | libgo/go/exp/ogle/main.go | 9 | ||||
-rw-r--r-- | libgo/go/exp/ogle/process.go | 521 | ||||
-rw-r--r-- | libgo/go/exp/ogle/rruntime.go | 271 | ||||
-rw-r--r-- | libgo/go/exp/ogle/rtype.go | 288 | ||||
-rw-r--r-- | libgo/go/exp/ogle/rvalue.go | 515 | ||||
-rw-r--r-- | libgo/go/exp/ogle/vars.go | 272 |
12 files changed, 3018 insertions, 0 deletions
diff --git a/libgo/go/exp/ogle/abort.go b/libgo/go/exp/ogle/abort.go new file mode 100644 index 000000000..311a7b38e --- /dev/null +++ b/libgo/go/exp/ogle/abort.go @@ -0,0 +1,35 @@ +// 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 ( + "os" + "runtime" +) + +// An aborter aborts the thread's current computation, usually +// passing the error to a waiting thread. +type aborter interface { + Abort(err os.Error) +} + +type ogleAborter chan os.Error + +func (a ogleAborter) Abort(err os.Error) { + a <- err + runtime.Goexit() +} + +// try executes a computation; if the computation Aborts, try returns +// the error passed to abort. +func try(f func(a aborter)) os.Error { + a := make(ogleAborter) + go func() { + f(a) + a <- nil + }() + err := <-a + return err +} diff --git a/libgo/go/exp/ogle/arch.go b/libgo/go/exp/ogle/arch.go new file mode 100644 index 000000000..52b1c9757 --- /dev/null +++ b/libgo/go/exp/ogle/arch.go @@ -0,0 +1,125 @@ +// 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 ( + "debug/proc" + "math" +) + +type Arch interface { + // ToWord converts an array of up to 8 bytes in memory order + // to a word. + ToWord(data []byte) proc.Word + // FromWord converts a word to an array of up to 8 bytes in + // memory order. + FromWord(v proc.Word, out []byte) + // ToFloat32 converts a word to a float. The order of this + // word will be the order returned by ToWord on the memory + // representation of a float, and thus may require reversing. + ToFloat32(bits uint32) float32 + // FromFloat32 converts a float to a word. This should return + // a word that can be passed to FromWord to get the memory + // representation of a float on this architecture. + FromFloat32(f float32) uint32 + // ToFloat64 is to float64 as ToFloat32 is to float32. + ToFloat64(bits uint64) float64 + // FromFloat64 is to float64 as FromFloat32 is to float32. + FromFloat64(f float64) uint64 + + // IntSize returns the number of bytes in an 'int'. + IntSize() int + // PtrSize returns the number of bytes in a 'uintptr'. + PtrSize() int + // FloatSize returns the number of bytes in a 'float'. + FloatSize() int + // Align rounds offset up to the appropriate offset for a + // basic type with the given width. + Align(offset, width int) int + + // G returns the current G pointer. + G(regs proc.Regs) proc.Word + + // ClosureSize returns the number of bytes expected by + // ParseClosure. + ClosureSize() int + // ParseClosure takes ClosureSize bytes read from a return PC + // in a remote process, determines if the code is a closure, + // and returns the frame size of the closure if it is. + ParseClosure(data []byte) (frame int, ok bool) +} + +type ArchLSB struct{} + +func (ArchLSB) ToWord(data []byte) proc.Word { + var v proc.Word + for i, b := range data { + v |= proc.Word(b) << (uint(i) * 8) + } + return v +} + +func (ArchLSB) FromWord(v proc.Word, out []byte) { + for i := range out { + out[i] = byte(v) + v >>= 8 + } +} + +func (ArchLSB) ToFloat32(bits uint32) float32 { + // TODO(austin) Do these definitions depend on my current + // architecture? + return math.Float32frombits(bits) +} + +func (ArchLSB) FromFloat32(f float32) uint32 { return math.Float32bits(f) } + +func (ArchLSB) ToFloat64(bits uint64) float64 { return math.Float64frombits(bits) } + +func (ArchLSB) FromFloat64(f float64) uint64 { return math.Float64bits(f) } + +type ArchAlignedMultiple struct{} + +func (ArchAlignedMultiple) Align(offset, width int) int { + return ((offset - 1) | (width - 1)) + 1 +} + +type amd64 struct { + ArchLSB + ArchAlignedMultiple + gReg int +} + +func (a *amd64) IntSize() int { return 4 } + +func (a *amd64) PtrSize() int { return 8 } + +func (a *amd64) FloatSize() int { return 4 } + +func (a *amd64) G(regs proc.Regs) proc.Word { + // See src/pkg/runtime/mkasmh + if a.gReg == -1 { + ns := regs.Names() + for i, n := range ns { + if n == "r15" { + a.gReg = i + break + } + } + } + + return regs.Get(a.gReg) +} + +func (a *amd64) ClosureSize() int { return 8 } + +func (a *amd64) ParseClosure(data []byte) (int, bool) { + if data[0] == 0x48 && data[1] == 0x81 && data[2] == 0xc4 && data[7] == 0xc3 { + return int(a.ToWord(data[3:7]) + 8), true + } + return 0, false +} + +var Amd64 = &amd64{gReg: -1} diff --git a/libgo/go/exp/ogle/cmd.go b/libgo/go/exp/ogle/cmd.go new file mode 100644 index 000000000..4f67032d0 --- /dev/null +++ b/libgo/go/exp/ogle/cmd.go @@ -0,0 +1,373 @@ +// 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. + +// Ogle is the beginning of a debugger for Go. +package ogle + +import ( + "bufio" + "debug/elf" + "debug/proc" + "exp/eval" + "fmt" + "go/scanner" + "go/token" + "os" + "strconv" + "strings" +) + +var fset = token.NewFileSet() +var world *eval.World +var curProc *Process + +func Main() { + world = eval.NewWorld() + defineFuncs() + r := bufio.NewReader(os.Stdin) + for { + print("; ") + line, err := r.ReadSlice('\n') + if err != nil { + break + } + + // Try line as a command + cmd, rest := getCmd(line) + if cmd != nil { + err := cmd.handler(rest) + if err != nil { + scanner.PrintError(os.Stderr, err) + } + continue + } + + // Try line as code + code, err := world.Compile(fset, string(line)) + if err != nil { + scanner.PrintError(os.Stderr, err) + continue + } + v, err := code.Run() + if err != nil { + fmt.Fprintf(os.Stderr, err.String()) + continue + } + if v != nil { + println(v.String()) + } + } +} + +// newScanner creates a new scanner that scans that given input bytes. +func newScanner(input []byte) (*scanner.Scanner, *scanner.ErrorVector) { + sc := new(scanner.Scanner) + ev := new(scanner.ErrorVector) + file := fset.AddFile("input", fset.Base(), len(input)) + sc.Init(file, input, ev, 0) + return sc, ev +} + +/* + * Commands + */ + +// A UsageError occurs when a command is called with illegal arguments. +type UsageError string + +func (e UsageError) String() string { return string(e) } + +// A cmd represents a single command with a handler. +type cmd struct { + cmd string + handler func([]byte) os.Error +} + +var cmds = []cmd{ + {"load", cmdLoad}, + {"bt", cmdBt}, +} + +// getCmd attempts to parse an input line as a registered command. If +// successful, it returns the command and the bytes remaining after +// the command, which should be passed to the command. +func getCmd(line []byte) (*cmd, []byte) { + sc, _ := newScanner(line) + pos, tok, lit := sc.Scan() + if sc.ErrorCount != 0 || tok != token.IDENT { + return nil, nil + } + + slit := string(lit) + for i := range cmds { + if cmds[i].cmd == slit { + return &cmds[i], line[fset.Position(pos).Offset+len(lit):] + } + } + return nil, nil +} + +// cmdLoad starts or attaches to a process. Its form is similar to +// import: +// +// load [sym] "path" [;] +// +// sym specifies the name to give to the process. If not given, the +// name is derived from the path of the process. If ".", then the +// packages from the remote process are defined into the current +// namespace. If given, this symbol is defined as a package +// containing the process' packages. +// +// path gives the path of the process to start or attach to. If it is +// "pid:<num>", then attach to the given PID. Otherwise, treat it as +// a file path and space-separated arguments and start a new process. +// +// load always sets the current process to the loaded process. +func cmdLoad(args []byte) os.Error { + ident, path, err := parseLoad(args) + if err != nil { + return err + } + if curProc != nil { + return UsageError("multiple processes not implemented") + } + if ident != "." { + return UsageError("process identifiers not implemented") + } + + // Parse argument and start or attach to process + var fname string + var tproc proc.Process + if len(path) >= 4 && path[0:4] == "pid:" { + pid, err := strconv.Atoi(path[4:]) + if err != nil { + return err + } + fname, err = os.Readlink(fmt.Sprintf("/proc/%d/exe", pid)) + if err != nil { + return err + } + tproc, err = proc.Attach(pid) + if err != nil { + return err + } + println("Attached to", pid) + } else { + parts := strings.Split(path, " ", -1) + if len(parts) == 0 { + fname = "" + } else { + fname = parts[0] + } + tproc, err = proc.ForkExec(fname, parts, os.Environ(), "", []*os.File{os.Stdin, os.Stdout, os.Stderr}) + if err != nil { + return err + } + println("Started", path) + // TODO(austin) If we fail after this point, kill tproc + // before detaching. + } + + // Get symbols + f, err := os.Open(fname, os.O_RDONLY, 0) + if err != nil { + tproc.Detach() + return err + } + defer f.Close() + elf, err := elf.NewFile(f) + if err != nil { + tproc.Detach() + return err + } + curProc, err = NewProcessElf(tproc, elf) + if err != nil { + tproc.Detach() + return err + } + + // Prepare new process + curProc.OnGoroutineCreate().AddHandler(EventPrint) + curProc.OnGoroutineExit().AddHandler(EventPrint) + + err = curProc.populateWorld(world) + if err != nil { + tproc.Detach() + return err + } + + return nil +} + +func parseLoad(args []byte) (ident string, path string, err os.Error) { + err = UsageError("Usage: load [sym] \"path\"") + sc, ev := newScanner(args) + + var toks [4]token.Token + var lits [4][]byte + for i := range toks { + _, toks[i], lits[i] = sc.Scan() + } + if sc.ErrorCount != 0 { + err = ev.GetError(scanner.NoMultiples) + return + } + + i := 0 + switch toks[i] { + case token.PERIOD, token.IDENT: + ident = string(lits[i]) + i++ + } + + if toks[i] != token.STRING { + return + } + path, uerr := strconv.Unquote(string(lits[i])) + if uerr != nil { + err = uerr + return + } + i++ + + if toks[i] == token.SEMICOLON { + i++ + } + if toks[i] != token.EOF { + return + } + + return ident, path, nil +} + +// cmdBt prints a backtrace for the current goroutine. It takes no +// arguments. +func cmdBt(args []byte) os.Error { + err := parseNoArgs(args, "Usage: bt") + if err != nil { + return err + } + + if curProc == nil || curProc.curGoroutine == nil { + return NoCurrentGoroutine{} + } + + f := curProc.curGoroutine.frame + if f == nil { + fmt.Println("No frames on stack") + return nil + } + + for f.Inner() != nil { + f = f.Inner() + } + + for i := 0; i < 100; i++ { + if f == curProc.curGoroutine.frame { + fmt.Printf("=> ") + } else { + fmt.Printf(" ") + } + fmt.Printf("%8x %v\n", f.pc, f) + f, err = f.Outer() + if err != nil { + return err + } + if f == nil { + return nil + } + } + + fmt.Println("...") + return nil +} + +func parseNoArgs(args []byte, usage string) os.Error { + sc, ev := newScanner(args) + _, tok, _ := sc.Scan() + if sc.ErrorCount != 0 { + return ev.GetError(scanner.NoMultiples) + } + if tok != token.EOF { + return UsageError(usage) + } + return nil +} + +/* + * Functions + */ + +// defineFuncs populates world with the built-in functions. +func defineFuncs() { + t, v := eval.FuncFromNativeTyped(fnOut, fnOutSig) + world.DefineConst("Out", t, v) + t, v = eval.FuncFromNativeTyped(fnContWait, fnContWaitSig) + world.DefineConst("ContWait", t, v) + t, v = eval.FuncFromNativeTyped(fnBpSet, fnBpSetSig) + world.DefineConst("BpSet", t, v) +} + +// printCurFrame prints the current stack frame, as it would appear in +// a backtrace. +func printCurFrame() { + if curProc == nil || curProc.curGoroutine == nil { + return + } + f := curProc.curGoroutine.frame + if f == nil { + return + } + fmt.Printf("=> %8x %v\n", f.pc, f) +} + +// fnOut moves the current frame to the caller of the current frame. +func fnOutSig() {} +func fnOut(t *eval.Thread, args []eval.Value, res []eval.Value) { + if curProc == nil { + t.Abort(NoCurrentGoroutine{}) + } + err := curProc.Out() + if err != nil { + t.Abort(err) + } + // TODO(austin) Only in the command form + printCurFrame() +} + +// fnContWait continues the current process and waits for a stopping event. +func fnContWaitSig() {} +func fnContWait(t *eval.Thread, args []eval.Value, res []eval.Value) { + if curProc == nil { + t.Abort(NoCurrentGoroutine{}) + } + err := curProc.ContWait() + if err != nil { + t.Abort(err) + } + // TODO(austin) Only in the command form + ev := curProc.Event() + if ev != nil { + fmt.Printf("%v\n", ev) + } + printCurFrame() +} + +// fnBpSet sets a breakpoint at the entry to the named function. +func fnBpSetSig(string) {} +func fnBpSet(t *eval.Thread, args []eval.Value, res []eval.Value) { + // TODO(austin) This probably shouldn't take a symbol name. + // Perhaps it should take an interface that provides PC's. + // Functions and instructions can implement that interface and + // we can have something to translate file:line pairs. + if curProc == nil { + t.Abort(NoCurrentGoroutine{}) + } + name := args[0].(eval.StringValue).Get(t) + fn := curProc.syms.LookupFunc(name) + if fn == nil { + t.Abort(UsageError("no such function " + name)) + } + curProc.OnBreakpoint(proc.Word(fn.Entry)).AddHandler(EventStop) +} diff --git a/libgo/go/exp/ogle/event.go b/libgo/go/exp/ogle/event.go new file mode 100644 index 000000000..d7092ded3 --- /dev/null +++ b/libgo/go/exp/ogle/event.go @@ -0,0 +1,280 @@ +// 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 ( + "debug/proc" + "fmt" + "os" +) + +/* + * 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 + Goroutine() *Goroutine + 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 goroutine of this event. + t *Goroutine +} + +func (e *commonEvent) Process() *Process { return e.p } + +func (e *commonEvent) Goroutine() *Goroutine { 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 proc.Word +} + +// A Breakpoint event occurs when a process reaches a particular +// program counter. When this event is handled, the current goroutine +// will be the goroutine that reached the program counter. +type Breakpoint struct { + commonEvent + osThread proc.Thread + pc proc.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() proc.Word { return b.pc } + +func (b *Breakpoint) String() string { + // 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) +} + +/* + * Goroutine create/exit + */ + +type goroutineCreateHook struct { + commonHook +} + +func (h *goroutineCreateHook) String() string { return "goroutine create" } + +// 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 *Goroutine +} + +// 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 *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 goroutineExitHook struct { + commonHook +} + +func (h *goroutineExitHook) String() string { return "goroutine exit" } + +// A GoroutineExit event occurs when a Go goroutine exits. +type GoroutineExit struct { + commonEvent +} + +func (e *GoroutineExit) String() string { + // TODO(austin) Include process name + //return fmt.Sprintf("%v exited", e.t); + // For debugging purposes + return fmt.Sprintf("goroutine %#x exited", e.t.g.addr().base) +} diff --git a/libgo/go/exp/ogle/frame.go b/libgo/go/exp/ogle/frame.go new file mode 100644 index 000000000..1538362ba --- /dev/null +++ b/libgo/go/exp/ogle/frame.go @@ -0,0 +1,212 @@ +// 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 ( + "debug/gosym" + "debug/proc" + "fmt" + "os" +) + +// A Frame represents a single frame on a remote call stack. +type Frame struct { + // pc is the PC of the next instruction that will execute in + // this frame. For lower frames, this is the instruction + // following the CALL instruction. + pc, sp, fp proc.Word + // The runtime.Stktop of the active stack segment + stk remoteStruct + // The function this stack frame is in + fn *gosym.Func + // The path and line of the CALL or current instruction. Note + // that this differs slightly from the meaning of Frame.pc. + path string + line int + // The inner and outer frames of this frame. outer is filled + // in lazily. + inner, outer *Frame +} + +// newFrame returns the top-most Frame of the given g's thread. +func newFrame(g remoteStruct) (*Frame, os.Error) { + var f *Frame + err := try(func(a aborter) { f = aNewFrame(a, g) }) + return f, err +} + +func aNewFrame(a aborter, g remoteStruct) *Frame { + p := g.r.p + var pc, sp proc.Word + + // Is this G alive? + switch g.field(p.f.G.Status).(remoteInt).aGet(a) { + case p.runtime.Gidle, p.runtime.Gmoribund, p.runtime.Gdead: + return nil + } + + // Find the OS thread for this G + + // TODO(austin) Ideally, we could look at the G's state and + // 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.proc.Threads() { + regs, err := t.Regs() + if err != nil { + // TODO(austin) What to do? + continue + } + thisg := p.G(regs) + if thisg == g.addr().base { + // Found this G's OS thread + pc = regs.PC() + sp = regs.SP() + + // If this thread crashed, try to recover it + if pc == 0 { + pc = p.peekUintptr(a, pc) + sp += 8 + } + + break + } + } + + if pc == 0 && sp == 0 { + // G is not mapped to an OS thread. Use the + // scheduler's stored PC and SP. + sched := g.field(p.f.G.Sched).(remoteStruct) + pc = proc.Word(sched.field(p.f.Gobuf.Pc).(remoteUint).aGet(a)) + sp = proc.Word(sched.field(p.f.Gobuf.Sp).(remoteUint).aGet(a)) + } + + // Get Stktop + stk := g.field(p.f.G.Stackbase).(remotePtr).aGet(a).(remoteStruct) + + return prepareFrame(a, pc, sp, stk, nil) +} + +// prepareFrame creates a Frame from the PC and SP within that frame, +// as well as the active stack segment. This function takes care of +// traversing stack breaks and unwinding closures. +func prepareFrame(a aborter, pc, sp proc.Word, stk remoteStruct, inner *Frame) *Frame { + // Based on src/pkg/runtime/amd64/traceback.c:traceback + p := stk.r.p + top := inner == nil + + // Get function + var path string + var line int + var fn *gosym.Func + + for i := 0; i < 100; i++ { + // Traverse segmented stack breaks + if p.sys.lessstack != nil && pc == proc.Word(p.sys.lessstack.Value) { + // Get stk->gobuf.pc + pc = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Pc).(remoteUint).aGet(a)) + // Get stk->gobuf.sp + sp = proc.Word(stk.field(p.f.Stktop.Gobuf).(remoteStruct).field(p.f.Gobuf.Sp).(remoteUint).aGet(a)) + // Get stk->stackbase + stk = stk.field(p.f.Stktop.Stackbase).(remotePtr).aGet(a).(remoteStruct) + continue + } + + // Get the PC of the call instruction + callpc := pc + if !top && (p.sys.goexit == nil || pc != proc.Word(p.sys.goexit.Value)) { + callpc-- + } + + // Look up function + path, line, fn = p.syms.PCToLine(uint64(callpc)) + if fn != nil { + break + } + + // Closure? + var buf = make([]byte, p.ClosureSize()) + if _, err := p.Peek(pc, buf); err != nil { + break + } + spdelta, ok := p.ParseClosure(buf) + if ok { + sp += proc.Word(spdelta) + pc = p.peekUintptr(a, sp-proc.Word(p.PtrSize())) + } + } + if fn == nil { + return nil + } + + // Compute frame pointer + var fp proc.Word + if fn.FrameSize < p.PtrSize() { + fp = sp + proc.Word(p.PtrSize()) + } else { + fp = sp + proc.Word(fn.FrameSize) + } + // TODO(austin) To really figure out if we're in the prologue, + // we need to disassemble the function and look for the call + // to morestack. For now, just special case the entry point. + // + // TODO(austin) What if we're in the call to morestack in the + // prologue? Then top == false. + if top && pc == proc.Word(fn.Entry) { + // We're in the function prologue, before SP + // has been adjusted for the frame. + fp -= proc.Word(fn.FrameSize - p.PtrSize()) + } + + return &Frame{pc, sp, fp, stk, fn, path, line, inner, nil} +} + +// Outer returns the Frame that called this Frame, or nil if this is +// the outermost frame. +func (f *Frame) Outer() (*Frame, os.Error) { + var fr *Frame + err := try(func(a aborter) { fr = f.aOuter(a) }) + return fr, err +} + +func (f *Frame) aOuter(a aborter) *Frame { + // Is there a cached outer frame + if f.outer != nil { + return f.outer + } + + p := f.stk.r.p + + sp := f.fp + if f.fn == p.sys.newproc && f.fn == p.sys.deferproc { + // TODO(rsc) The compiler inserts two push/pop's + // around calls to go and defer. Russ says this + // should get fixed in the compiler, but we account + // for it for now. + sp += proc.Word(2 * p.PtrSize()) + } + + pc := p.peekUintptr(a, f.fp-proc.Word(p.PtrSize())) + if pc < 0x1000 { + return nil + } + + // TODO(austin) Register this frame for shoot-down. + + f.outer = prepareFrame(a, pc, sp, f.stk, f) + return f.outer +} + +// Inner returns the Frame called by this Frame, or nil if this is the +// innermost frame. +func (f *Frame) Inner() *Frame { return f.inner } + +func (f *Frame) String() string { + res := f.fn.Name + if f.pc > proc.Word(f.fn.Value) { + res += fmt.Sprintf("+%#x", f.pc-proc.Word(f.fn.Entry)) + } + return res + fmt.Sprintf(" %s:%d", f.path, f.line) +} diff --git a/libgo/go/exp/ogle/goroutine.go b/libgo/go/exp/ogle/goroutine.go new file mode 100644 index 000000000..5104ec6d4 --- /dev/null +++ b/libgo/go/exp/ogle/goroutine.go @@ -0,0 +1,117 @@ +// 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 ( + "debug/proc" + "exp/eval" + "fmt" + "os" +) + +// 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 "<dead thread>" + } + // 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 + proc.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 +} diff --git a/libgo/go/exp/ogle/main.go b/libgo/go/exp/ogle/main.go new file mode 100644 index 000000000..1999eccca --- /dev/null +++ b/libgo/go/exp/ogle/main.go @@ -0,0 +1,9 @@ +// 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 main + +import "exp/ogle" + +func main() { ogle.Main() } diff --git a/libgo/go/exp/ogle/process.go b/libgo/go/exp/ogle/process.go new file mode 100644 index 000000000..58e830aa6 --- /dev/null +++ b/libgo/go/exp/ogle/process.go @@ -0,0 +1,521 @@ +// 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 ( + "debug/elf" + "debug/gosym" + "debug/proc" + "exp/eval" + "fmt" + "log" + "os" + "reflect" +) + +// A FormatError indicates a failure to process information in or +// about a remote process, such as unexpected or missing information +// in the object file or runtime structures. +type FormatError string + +func (e FormatError) String() string { return string(e) } + +// An UnknownArchitecture occurs when trying to load an object file +// that indicates an architecture not supported by the debugger. +type UnknownArchitecture elf.Machine + +func (e UnknownArchitecture) String() string { + return "unknown architecture: " + elf.Machine(e).String() +} + +// A ProcessNotStopped error occurs when attempting to read or write +// memory or registers of a process that is not stopped. +type ProcessNotStopped struct{} + +func (e ProcessNotStopped) String() string { return "process not stopped" } + +// An UnknownGoroutine error is an internal error representing an +// unrecognized G structure pointer. +type UnknownGoroutine struct { + OSThread proc.Thread + Goroutine proc.Word +} + +func (e UnknownGoroutine) String() string { + return fmt.Sprintf("internal error: unknown goroutine (G %#x)", e.Goroutine) +} + +// 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 NoCurrentGoroutine) String() string { return "no current goroutine" } + +// A Process represents a remote attached process. +type Process struct { + Arch + proc proc.Process + + // The symbol table of this process + syms *gosym.Table + + // A possibly-stopped OS thread, or nil + threadCache proc.Thread + + // Types parsed from the remote process + types map[proc.Word]*remoteType + + // Types and values from the remote runtime package + runtime runtimeValues + + // Runtime field indexes + f runtimeIndexes + + // Globals from the sys package (or from no package) + sys struct { + lessstack, goexit, newproc, deferproc, newprocreadylocked *gosym.Func + allg remotePtr + g0 remoteStruct + } + + // Event queue + posted []Event + pending []Event + event Event + + // Event hooks + breakpointHooks map[proc.Word]*breakpointHook + goroutineCreateHook *goroutineCreateHook + goroutineExitHook *goroutineExitHook + + // Current goroutine, or nil if there are no goroutines + curGoroutine *Goroutine + + // Goroutines by the address of their G structure + goroutines map[proc.Word]*Goroutine +} + +/* + * Process creation + */ + +// NewProcess constructs a new remote process around a traced +// process, an architecture, and a symbol table. +func NewProcess(tproc proc.Process, arch Arch, syms *gosym.Table) (*Process, os.Error) { + p := &Process{ + Arch: arch, + proc: tproc, + syms: syms, + types: make(map[proc.Word]*remoteType), + breakpointHooks: make(map[proc.Word]*breakpointHook), + goroutineCreateHook: new(goroutineCreateHook), + goroutineExitHook: new(goroutineExitHook), + goroutines: make(map[proc.Word]*Goroutine), + } + + // Fill in remote runtime + p.bootstrap() + + 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 goroutines + p.goroutines[p.sys.g0.addr().base] = &Goroutine{p.sys.g0, nil, false} + err := try(func(a aborter) { + g := p.sys.allg.aGet(a) + for g != nil { + gs := g.(remoteStruct) + 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).aGet(a) + } + }) + if err != nil { + return nil, err + } + + // Create internal breakpoints to catch new and exited goroutines + p.OnBreakpoint(proc.Word(p.sys.newprocreadylocked.Entry)).(*breakpointHook).addHandler(readylockedBP, true) + p.OnBreakpoint(proc.Word(p.sys.goexit.Entry)).(*breakpointHook).addHandler(goexitBP, true) + + // Select current frames + for _, g := range p.goroutines { + g.resetFrame() + } + + p.selectSomeGoroutine() + + return p, nil +} + +func elfGoSyms(f *elf.File) (*gosym.Table, os.Error) { + text := f.Section(".text") + symtab := f.Section(".gosymtab") + pclntab := f.Section(".gopclntab") + if text == nil || symtab == nil || pclntab == nil { + return nil, nil + } + + symdat, err := symtab.Data() + if err != nil { + return nil, err + } + pclndat, err := pclntab.Data() + if err != nil { + return nil, err + } + + pcln := gosym.NewLineTable(pclndat, text.Addr) + tab, err := gosym.NewTable(symdat, pcln) + if err != nil { + return nil, err + } + + return tab, nil +} + +// NewProcessElf constructs a new remote process around a traced +// process and the process' ELF object. +func NewProcessElf(tproc proc.Process, f *elf.File) (*Process, os.Error) { + syms, err := elfGoSyms(f) + if err != nil { + return nil, err + } + if syms == nil { + return nil, FormatError("Failed to find symbol table") + } + var arch Arch + switch f.Machine { + case elf.EM_X86_64: + arch = Amd64 + default: + return nil, UnknownArchitecture(f.Machine) + } + return NewProcess(tproc, arch, syms) +} + +// bootstrap constructs the runtime structure of a remote process. +func (p *Process) bootstrap() { + // Manually construct runtime types + p.runtime.String = newManualType(eval.TypeOfNative(rt1String{}), p.Arch) + p.runtime.Slice = newManualType(eval.TypeOfNative(rt1Slice{}), p.Arch) + p.runtime.Eface = newManualType(eval.TypeOfNative(rt1Eface{}), p.Arch) + + p.runtime.Type = newManualType(eval.TypeOfNative(rt1Type{}), p.Arch) + p.runtime.CommonType = newManualType(eval.TypeOfNative(rt1CommonType{}), p.Arch) + p.runtime.UncommonType = newManualType(eval.TypeOfNative(rt1UncommonType{}), p.Arch) + p.runtime.StructField = newManualType(eval.TypeOfNative(rt1StructField{}), p.Arch) + p.runtime.StructType = newManualType(eval.TypeOfNative(rt1StructType{}), p.Arch) + p.runtime.PtrType = newManualType(eval.TypeOfNative(rt1PtrType{}), p.Arch) + p.runtime.ArrayType = newManualType(eval.TypeOfNative(rt1ArrayType{}), p.Arch) + p.runtime.SliceType = newManualType(eval.TypeOfNative(rt1SliceType{}), p.Arch) + + p.runtime.Stktop = newManualType(eval.TypeOfNative(rt1Stktop{}), p.Arch) + p.runtime.Gobuf = newManualType(eval.TypeOfNative(rt1Gobuf{}), p.Arch) + p.runtime.G = newManualType(eval.TypeOfNative(rt1G{}), p.Arch) + + // Get addresses of type.*runtime.XType for discrimination. + rtv := reflect.Indirect(reflect.NewValue(&p.runtime)).(*reflect.StructValue) + rtvt := rtv.Type().(*reflect.StructType) + for i := 0; i < rtv.NumField(); i++ { + n := rtvt.Field(i).Name + if n[0] != 'P' || n[1] < 'A' || n[1] > 'Z' { + continue + } + sym := p.syms.LookupSym("type.*runtime." + n[1:]) + if sym == nil { + continue + } + rtv.Field(i).(*reflect.UintValue).Set(sym.Value) + } + + // Get runtime field indexes + fillRuntimeIndexes(&p.runtime, &p.f) + + // Fill G status + p.runtime.runtimeGStatus = rt1GStatus + + // Get globals + p.sys.lessstack = p.syms.LookupFunc("sys.lessstack") + p.sys.goexit = p.syms.LookupFunc("goexit") + p.sys.newproc = p.syms.LookupFunc("sys.newproc") + p.sys.deferproc = p.syms.LookupFunc("sys.deferproc") + p.sys.newprocreadylocked = p.syms.LookupFunc("newprocreadylocked") + if allg := p.syms.LookupSym("allg"); allg != nil { + p.sys.allg = remotePtr{remote{proc.Word(allg.Value), p}, p.runtime.G} + } + if g0 := p.syms.LookupSym("g0"); g0 != nil { + p.sys.g0 = p.runtime.G.mk(remote{proc.Word(g0.Value), p}).(remoteStruct) + } +} + +func (p *Process) selectSomeGoroutine() { + // Once we have friendly goroutine ID's, there might be a more + // reasonable behavior for this. + p.curGoroutine = nil + for _, g := range p.goroutines { + if !g.isG0() && g.frame != nil { + p.curGoroutine = g + return + } + } +} + +/* + * Process memory + */ + +func (p *Process) someStoppedOSThread() proc.Thread { + if p.threadCache != nil { + if _, err := p.threadCache.Stopped(); err == nil { + return p.threadCache + } + } + + for _, t := range p.proc.Threads() { + if _, err := t.Stopped(); err == nil { + p.threadCache = t + return t + } + } + return nil +} + +func (p *Process) Peek(addr proc.Word, out []byte) (int, os.Error) { + thr := p.someStoppedOSThread() + if thr == nil { + return 0, ProcessNotStopped{} + } + return thr.Peek(addr, out) +} + +func (p *Process) Poke(addr proc.Word, b []byte) (int, os.Error) { + thr := p.someStoppedOSThread() + if thr == nil { + return 0, ProcessNotStopped{} + } + return thr.Poke(addr, b) +} + +func (p *Process) peekUintptr(a aborter, addr proc.Word) proc.Word { + return proc.Word(mkUintptr(remote{addr, p}).(remoteUint).aGet(a)) +} + +/* + * Events + */ + +// OnBreakpoint returns the hook that is run when the program reaches +// the given program counter. +func (p *Process) OnBreakpoint(pc proc.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} +} + +// OnGoroutineCreate returns the hook that is run when a goroutine is created. +func (p *Process) OnGoroutineCreate() EventHook { + return p.goroutineCreateHook +} + +// OnGoroutineExit returns the hook that is run when a goroutine exits. +func (p *Process) OnGoroutineExit() EventHook { return p.goroutineExitHook } + +// osThreadToGoroutine looks up the goroutine running on an OS thread. +func (p *Process) osThreadToGoroutine(t proc.Thread) (*Goroutine, os.Error) { + regs, err := t.Regs() + if err != nil { + return nil, err + } + g := p.G(regs) + gt, ok := p.goroutines[g] + if !ok { + return nil, UnknownGoroutine{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 proc.Breakpoint: + nev++ + case proc.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 proc.Breakpoint: + gt, err := p.osThreadToGoroutine(t) + if err != nil { + return nil, err + } + events[i] = &Breakpoint{commonEvent{p, gt}, t, proc.Word(c)} + i++ + case proc.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) { + p.posted = append(p.posted, ev) +} + +// 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:] + 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:] + 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.curGoroutine = ev.Goroutine() + action, err = hook.handle(ev) + + case *GoroutineCreate: + p.curGoroutine = ev.Goroutine() + action, err = p.goroutineCreateHook.handle(ev) + + case *GoroutineExit: + action, err = p.goroutineExitHook.handle(ev) + + default: + log.Panicf("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 +// asynchronous 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 _, g := range p.goroutines { + g.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.curGoroutine == nil { + return NoCurrentGoroutine{} + } + return p.curGoroutine.Out() +} + +// In selects the frame called by the current frame. +func (p *Process) In() os.Error { + if p.curGoroutine == nil { + return NoCurrentGoroutine{} + } + return p.curGoroutine.In() +} diff --git a/libgo/go/exp/ogle/rruntime.go b/libgo/go/exp/ogle/rruntime.go new file mode 100644 index 000000000..33f1935b8 --- /dev/null +++ b/libgo/go/exp/ogle/rruntime.go @@ -0,0 +1,271 @@ +// 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 ( + "debug/proc" + "exp/eval" + "reflect" +) + +// This file contains remote runtime definitions. Using reflection, +// we convert all of these to interpreter types and layout their +// remote representations using the architecture rules. +// +// We could get most of these definitions from our own runtime +// package; however, some of them differ in convenient ways, some of +// them are not defined or exported by the runtime, and having our own +// definitions makes it easy to support multiple remote runtime +// versions. This may turn out to be overkill. +// +// All of these structures are prefixed with rt1 to indicate the +// runtime version and to mark them as types used only as templates +// for remote types. + +/* + * Runtime data headers + * + * See $GOROOT/src/pkg/runtime/runtime.h + */ + +type rt1String struct { + str uintptr + len int +} + +type rt1Slice struct { + array uintptr + len int + cap int +} + +type rt1Eface struct { + typ uintptr + ptr uintptr +} + +/* + * Runtime type structures + * + * See $GOROOT/src/pkg/runtime/type.h and $GOROOT/src/pkg/runtime/type.go + */ + +type rt1UncommonType struct { + name *string + pkgPath *string + //methods []method; +} + +type rt1CommonType struct { + size uintptr + hash uint32 + alg, align, fieldAlign uint8 + string *string + uncommonType *rt1UncommonType +} + +type rt1Type struct { + // While Type is technically an Eface, treating the + // discriminator as an opaque pointer and taking advantage of + // the commonType prologue on all Type's makes type parsing + // much simpler. + typ uintptr + ptr *rt1CommonType +} + +type rt1StructField struct { + name *string + pkgPath *string + typ *rt1Type + tag *string + offset uintptr +} + +type rt1StructType struct { + rt1CommonType + fields []rt1StructField +} + +type rt1PtrType struct { + rt1CommonType + elem *rt1Type +} + +type rt1SliceType struct { + rt1CommonType + elem *rt1Type +} + +type rt1ArrayType struct { + rt1CommonType + elem *rt1Type + len uintptr +} + +/* + * Runtime scheduler structures + * + * 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 { + _stackguard uintptr + stackbase *rt1Stktop + _defer uintptr + sched rt1Gobuf + _stack0 uintptr + _entry uintptr + alllink *rt1G + _param uintptr + status int16 + // Incomplete +} + +var rt1GStatus = runtimeGStatus{ + Gidle: 0, + Grunnable: 1, + Grunning: 2, + Gsyscall: 3, + Gwaiting: 4, + Gmoribund: 5, + Gdead: 6, +} + +// runtimeIndexes stores the indexes of fields in the runtime +// structures. It is filled in using reflection, so the name of the +// fields must match the names of the remoteType's in runtimeValues +// exactly and the names of the index fields must be the capitalized +// version of the names of the fields in the runtime structures above. +type runtimeIndexes struct { + String struct { + Str, Len int + } + Slice struct { + Array, Len, Cap int + } + Eface struct { + Typ, Ptr int + } + + UncommonType struct { + Name, PkgPath int + } + CommonType struct { + Size, Hash, Alg, Align, FieldAlign, String, UncommonType int + } + Type struct { + Typ, Ptr int + } + StructField struct { + Name, PkgPath, Typ, Tag, Offset int + } + StructType struct { + Fields int + } + PtrType struct { + Elem int + } + SliceType struct { + Elem int + } + ArrayType struct { + Elem, Len int + } + + Stktop struct { + Stackguard, Stackbase, Gobuf int + } + Gobuf struct { + Sp, Pc, G int + } + G struct { + Stackbase, Sched, Status, Alllink int + } +} + +// Values of G status codes +type runtimeGStatus struct { + Gidle, Grunnable, Grunning, Gsyscall, Gwaiting, Gmoribund, Gdead int64 +} + +// runtimeValues stores the types and values that correspond to those +// in the remote runtime package. +type runtimeValues struct { + // Runtime data headers + String, Slice, Eface *remoteType + // Runtime type structures + Type, CommonType, UncommonType, StructField, StructType, PtrType, + ArrayType, SliceType *remoteType + // Runtime scheduler structures + Stktop, Gobuf, G *remoteType + // Addresses of *runtime.XType types. These are the + // discriminators on the runtime.Type interface. We use local + // reflection to fill these in from the remote symbol table, + // so the names must match the runtime names. + PBoolType, + PUint8Type, PUint16Type, PUint32Type, PUint64Type, PUintType, PUintptrType, + PInt8Type, PInt16Type, PInt32Type, PInt64Type, PIntType, + PFloat32Type, PFloat64Type, PFloatType, + PArrayType, PStringType, PStructType, PPtrType, PFuncType, + PInterfaceType, PSliceType, PMapType, PChanType, + PDotDotDotType, PUnsafePointerType proc.Word + // G status values + runtimeGStatus +} + +// fillRuntimeIndexes fills a runtimeIndexes structure will the field +// indexes gathered from the remoteTypes recorded in a runtimeValues +// structure. +func fillRuntimeIndexes(runtime *runtimeValues, out *runtimeIndexes) { + outv := reflect.Indirect(reflect.NewValue(out)).(*reflect.StructValue) + outt := outv.Type().(*reflect.StructType) + runtimev := reflect.Indirect(reflect.NewValue(runtime)).(*reflect.StructValue) + + // out contains fields corresponding to each runtime type + for i := 0; i < outt.NumField(); i++ { + // Find the interpreter type for this runtime type + name := outt.Field(i).Name + et := runtimev.FieldByName(name).Interface().(*remoteType).Type.(*eval.StructType) + + // Get the field indexes of the interpreter struct type + indexes := make(map[string]int, len(et.Elems)) + for j, f := range et.Elems { + if f.Anonymous { + continue + } + name := f.Name + if name[0] >= 'a' && name[0] <= 'z' { + name = string(name[0]+'A'-'a') + name[1:] + } + indexes[name] = j + } + + // Fill this field of out + outStructv := outv.Field(i).(*reflect.StructValue) + outStructt := outStructv.Type().(*reflect.StructType) + for j := 0; j < outStructt.NumField(); j++ { + f := outStructv.Field(j).(*reflect.IntValue) + name := outStructt.Field(j).Name + f.Set(int64(indexes[name])) + } + } +} diff --git a/libgo/go/exp/ogle/rtype.go b/libgo/go/exp/ogle/rtype.go new file mode 100644 index 000000000..b3c35575a --- /dev/null +++ b/libgo/go/exp/ogle/rtype.go @@ -0,0 +1,288 @@ +// 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 ( + "debug/proc" + "exp/eval" + "fmt" + "log" +) + +const debugParseRemoteType = false + +// A remoteType is the local representation of a type in a remote process. +type remoteType struct { + eval.Type + // The size of values of this type in bytes. + size int + // The field alignment of this type. Only used for + // manually-constructed types. + fieldAlign int + // The maker function to turn a remote address of a value of + // this type into an interpreter Value. + mk maker +} + +var manualTypes = make(map[Arch]map[eval.Type]*remoteType) + +// newManualType constructs a remote type from an interpreter Type +// using the size and alignment properties of the given architecture. +// Most types are parsed directly out of the remote process, but to do +// so we need to layout the structures that describe those types ourselves. +func newManualType(t eval.Type, arch Arch) *remoteType { + if nt, ok := t.(*eval.NamedType); ok { + t = nt.Def + } + + // Get the type map for this architecture + typeMap := manualTypes[arch] + if typeMap == nil { + typeMap = make(map[eval.Type]*remoteType) + manualTypes[arch] = typeMap + + // Construct basic types for this architecture + basicType := func(t eval.Type, mk maker, size int, fieldAlign int) { + t = t.(*eval.NamedType).Def + if fieldAlign == 0 { + fieldAlign = size + } + typeMap[t] = &remoteType{t, size, fieldAlign, mk} + } + 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()) + } + + if rt, ok := typeMap[t]; ok { + return rt + } + + var rt *remoteType + switch t := t.(type) { + case *eval.PtrType: + var elem *remoteType + mk := func(r remote) eval.Value { return remotePtr{r, elem} } + rt = &remoteType{t, arch.PtrSize(), arch.PtrSize(), mk} + // Construct the element type after registering the + // type to break cycles. + typeMap[eval.Type(t)] = rt + elem = newManualType(t.Elem, arch) + + case *eval.ArrayType: + elem := newManualType(t.Elem, arch) + mk := func(r remote) eval.Value { return remoteArray{r, t.Len, elem} } + rt = &remoteType{t, elem.size * int(t.Len), elem.fieldAlign, mk} + + case *eval.SliceType: + elem := newManualType(t.Elem, arch) + mk := func(r remote) eval.Value { return remoteSlice{r, elem} } + rt = &remoteType{t, arch.PtrSize() + 2*arch.IntSize(), arch.PtrSize(), mk} + + case *eval.StructType: + layout := make([]remoteStructField, len(t.Elems)) + offset := 0 + fieldAlign := 0 + for i, f := range t.Elems { + elem := newManualType(f.Type, arch) + if fieldAlign == 0 { + fieldAlign = elem.fieldAlign + } + offset = arch.Align(offset, elem.fieldAlign) + layout[i].offset = offset + layout[i].fieldType = elem + offset += elem.size + } + mk := func(r remote) eval.Value { return remoteStruct{r, layout} } + rt = &remoteType{t, offset, fieldAlign, mk} + + default: + log.Panicf("cannot manually construct type %T", t) + } + + typeMap[t] = rt + return rt +} + +var prtIndent = "" + +// parseRemoteType parses a Type structure in a remote process to +// construct the corresponding interpreter type and remote type. +func parseRemoteType(a aborter, rs remoteStruct) *remoteType { + addr := rs.addr().base + p := rs.addr().p + + // We deal with circular types by discovering cycles at + // NamedTypes. If a type cycles back to something other than + // a named type, we're guaranteed that there will be a named + // type somewhere in that cycle. Thus, we continue down, + // re-parsing types until we reach the named type in the + // cycle. In order to still create one remoteType per remote + // type, we insert an empty remoteType in the type map the + // first time we encounter the type and re-use that structure + // the second time we encounter it. + + rt, ok := p.types[addr] + if ok && rt.Type != nil { + return rt + } else if !ok { + rt = &remoteType{} + p.types[addr] = rt + } + + if debugParseRemoteType { + sym := p.syms.SymByAddr(uint64(addr)) + name := "<unknown>" + if sym != nil { + name = sym.Name + } + log.Printf("%sParsing type at %#x (%s)", prtIndent, addr, name) + prtIndent += " " + defer func() { prtIndent = prtIndent[0 : len(prtIndent)-1] }() + } + + // Get Type header + itype := proc.Word(rs.field(p.f.Type.Typ).(remoteUint).aGet(a)) + typ := rs.field(p.f.Type.Ptr).(remotePtr).aGet(a).(remoteStruct) + + // Is this a named type? + var nt *eval.NamedType + uncommon := typ.field(p.f.CommonType.UncommonType).(remotePtr).aGet(a) + if uncommon != nil { + name := uncommon.(remoteStruct).field(p.f.UncommonType.Name).(remotePtr).aGet(a) + if name != nil { + // TODO(austin) Declare type in appropriate remote package + nt = eval.NewNamedType(name.(remoteString).aGet(a)) + rt.Type = nt + } + } + + // Create type + var t eval.Type + var mk maker + switch itype { + case p.runtime.PBoolType: + t = eval.BoolType + mk = mkBool + case p.runtime.PUint8Type: + t = eval.Uint8Type + mk = mkUint8 + case p.runtime.PUint16Type: + t = eval.Uint16Type + mk = mkUint16 + case p.runtime.PUint32Type: + t = eval.Uint32Type + mk = mkUint32 + case p.runtime.PUint64Type: + t = eval.Uint64Type + mk = mkUint64 + case p.runtime.PUintType: + t = eval.UintType + mk = mkUint + case p.runtime.PUintptrType: + t = eval.UintptrType + mk = mkUintptr + case p.runtime.PInt8Type: + t = eval.Int8Type + mk = mkInt8 + case p.runtime.PInt16Type: + t = eval.Int16Type + mk = mkInt16 + case p.runtime.PInt32Type: + t = eval.Int32Type + mk = mkInt32 + case p.runtime.PInt64Type: + t = eval.Int64Type + mk = mkInt64 + case p.runtime.PIntType: + t = eval.IntType + mk = mkInt + case p.runtime.PFloat32Type: + t = eval.Float32Type + mk = mkFloat32 + case p.runtime.PFloat64Type: + t = eval.Float64Type + mk = mkFloat64 + case p.runtime.PStringType: + t = eval.StringType + mk = mkString + + case p.runtime.PArrayType: + // Cast to an ArrayType + typ := p.runtime.ArrayType.mk(typ.addr()).(remoteStruct) + len := int64(typ.field(p.f.ArrayType.Len).(remoteUint).aGet(a)) + elem := parseRemoteType(a, typ.field(p.f.ArrayType.Elem).(remotePtr).aGet(a).(remoteStruct)) + t = eval.NewArrayType(len, elem.Type) + mk = func(r remote) eval.Value { return remoteArray{r, len, elem} } + + case p.runtime.PStructType: + // Cast to a StructType + typ := p.runtime.StructType.mk(typ.addr()).(remoteStruct) + fs := typ.field(p.f.StructType.Fields).(remoteSlice).aGet(a) + + fields := make([]eval.StructField, fs.Len) + layout := make([]remoteStructField, fs.Len) + for i := range fields { + f := fs.Base.(remoteArray).elem(int64(i)).(remoteStruct) + elemrs := f.field(p.f.StructField.Typ).(remotePtr).aGet(a).(remoteStruct) + elem := parseRemoteType(a, elemrs) + fields[i].Type = elem.Type + name := f.field(p.f.StructField.Name).(remotePtr).aGet(a) + if name == nil { + fields[i].Anonymous = true + } else { + fields[i].Name = name.(remoteString).aGet(a) + } + layout[i].offset = int(f.field(p.f.StructField.Offset).(remoteUint).aGet(a)) + layout[i].fieldType = elem + } + + t = eval.NewStructType(fields) + mk = func(r remote) eval.Value { return remoteStruct{r, layout} } + + case p.runtime.PPtrType: + // Cast to a PtrType + typ := p.runtime.PtrType.mk(typ.addr()).(remoteStruct) + elem := parseRemoteType(a, typ.field(p.f.PtrType.Elem).(remotePtr).aGet(a).(remoteStruct)) + t = eval.NewPtrType(elem.Type) + mk = func(r remote) eval.Value { return remotePtr{r, elem} } + + case p.runtime.PSliceType: + // Cast to a SliceType + typ := p.runtime.SliceType.mk(typ.addr()).(remoteStruct) + elem := parseRemoteType(a, typ.field(p.f.SliceType.Elem).(remotePtr).aGet(a).(remoteStruct)) + t = eval.NewSliceType(elem.Type) + mk = func(r remote) eval.Value { return remoteSlice{r, elem} } + + case p.runtime.PMapType, p.runtime.PChanType, p.runtime.PFuncType, p.runtime.PInterfaceType, p.runtime.PUnsafePointerType, p.runtime.PDotDotDotType: + // TODO(austin) + t = eval.UintptrType + mk = mkUintptr + + default: + sym := p.syms.SymByAddr(uint64(itype)) + name := "<unknown symbol>" + if sym != nil { + name = sym.Name + } + err := fmt.Sprintf("runtime type at %#x has unexpected type %#x (%s)", addr, itype, name) + a.Abort(FormatError(err)) + } + + // Fill in the remote type + if nt != nil { + nt.Complete(t) + } else { + rt.Type = t + } + rt.size = int(typ.field(p.f.CommonType.Size).(remoteUint).aGet(a)) + rt.mk = mk + + return rt +} diff --git a/libgo/go/exp/ogle/rvalue.go b/libgo/go/exp/ogle/rvalue.go new file mode 100644 index 000000000..3d630f936 --- /dev/null +++ b/libgo/go/exp/ogle/rvalue.go @@ -0,0 +1,515 @@ +// 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 ( + "debug/proc" + "exp/eval" + "fmt" +) + +// A RemoteMismatchError occurs when an operation that requires two +// identical remote processes is given different process. For +// example, this occurs when trying to set a pointer in one process to +// point to something in another process. +type RemoteMismatchError string + +func (e RemoteMismatchError) String() string { return string(e) } + +// A ReadOnlyError occurs when attempting to set or assign to a +// read-only value. +type ReadOnlyError string + +func (e ReadOnlyError) String() string { return string(e) } + +// A maker is a function that converts a remote address into an +// interpreter Value. +type maker func(remote) eval.Value + +type remoteValue interface { + addr() remote +} + +// remote represents an address in a remote process. +type remote struct { + base proc.Word + p *Process +} + +func (v remote) Get(a aborter, size int) uint64 { + // TODO(austin) This variable might temporarily be in a + // register. We could trace the assembly back from the + // current PC, looking for the beginning of the function or a + // call (both of which guarantee that the variable is in + // memory), or an instruction that loads the variable into a + // register. + // + // TODO(austin) If this is a local variable, it might not be + // live at this PC. In fact, because the compiler reuses + // slots, there might even be a different local variable at + // this location right now. A simple solution to both + // problems is to include the range of PC's over which a local + // variable is live in the symbol table. + // + // TODO(austin) We need to prevent the remote garbage + // collector from collecting objects out from under us. + var arr [8]byte + buf := arr[0:size] + _, err := v.p.Peek(v.base, buf) + if err != nil { + a.Abort(err) + } + return uint64(v.p.ToWord(buf)) +} + +func (v remote) Set(a aborter, size int, x uint64) { + var arr [8]byte + buf := arr[0:size] + v.p.FromWord(proc.Word(x), buf) + _, err := v.p.Poke(v.base, buf) + if err != nil { + a.Abort(err) + } +} + +func (v remote) plus(x proc.Word) remote { return remote{v.base + x, v.p} } + +func tryRVString(f func(a aborter) string) string { + var s string + err := try(func(a aborter) { s = f(a) }) + if err != nil { + return fmt.Sprintf("<error: %v>", err) + } + return s +} + +/* + * Bool + */ + +type remoteBool struct { + r remote +} + +func (v remoteBool) String() string { + return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }) +} + +func (v remoteBool) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.BoolValue).Get(t)) +} + +func (v remoteBool) Get(t *eval.Thread) bool { return v.aGet(t) } + +func (v remoteBool) aGet(a aborter) bool { return v.r.Get(a, 1) != 0 } + +func (v remoteBool) Set(t *eval.Thread, x bool) { + v.aSet(t, x) +} + +func (v remoteBool) aSet(a aborter, x bool) { + if x { + v.r.Set(a, 1, 1) + } else { + v.r.Set(a, 1, 0) + } +} + +func (v remoteBool) addr() remote { return v.r } + +func mkBool(r remote) eval.Value { return remoteBool{r} } + +/* + * Uint + */ + +type remoteUint struct { + r remote + size int +} + +func (v remoteUint) String() string { + return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }) +} + +func (v remoteUint) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.UintValue).Get(t)) +} + +func (v remoteUint) Get(t *eval.Thread) uint64 { + return v.aGet(t) +} + +func (v remoteUint) aGet(a aborter) uint64 { return v.r.Get(a, v.size) } + +func (v remoteUint) Set(t *eval.Thread, x uint64) { + v.aSet(t, x) +} + +func (v remoteUint) aSet(a aborter, x uint64) { v.r.Set(a, v.size, x) } + +func (v remoteUint) addr() remote { return v.r } + +func mkUint8(r remote) eval.Value { return remoteUint{r, 1} } + +func mkUint16(r remote) eval.Value { return remoteUint{r, 2} } + +func mkUint32(r remote) eval.Value { return remoteUint{r, 4} } + +func mkUint64(r remote) eval.Value { return remoteUint{r, 8} } + +func mkUint(r remote) eval.Value { return remoteUint{r, r.p.IntSize()} } + +func mkUintptr(r remote) eval.Value { return remoteUint{r, r.p.PtrSize()} } + +/* + * Int + */ + +type remoteInt struct { + r remote + size int +} + +func (v remoteInt) String() string { + return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }) +} + +func (v remoteInt) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.IntValue).Get(t)) +} + +func (v remoteInt) Get(t *eval.Thread) int64 { return v.aGet(t) } + +func (v remoteInt) aGet(a aborter) int64 { return int64(v.r.Get(a, v.size)) } + +func (v remoteInt) Set(t *eval.Thread, x int64) { + v.aSet(t, x) +} + +func (v remoteInt) aSet(a aborter, x int64) { v.r.Set(a, v.size, uint64(x)) } + +func (v remoteInt) addr() remote { return v.r } + +func mkInt8(r remote) eval.Value { return remoteInt{r, 1} } + +func mkInt16(r remote) eval.Value { return remoteInt{r, 2} } + +func mkInt32(r remote) eval.Value { return remoteInt{r, 4} } + +func mkInt64(r remote) eval.Value { return remoteInt{r, 8} } + +func mkInt(r remote) eval.Value { return remoteInt{r, r.p.IntSize()} } + +/* + * Float + */ + +type remoteFloat struct { + r remote + size int +} + +func (v remoteFloat) String() string { + return tryRVString(func(a aborter) string { return fmt.Sprintf("%v", v.aGet(a)) }) +} + +func (v remoteFloat) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.FloatValue).Get(t)) +} + +func (v remoteFloat) Get(t *eval.Thread) float64 { + return v.aGet(t) +} + +func (v remoteFloat) aGet(a aborter) float64 { + bits := v.r.Get(a, v.size) + switch v.size { + case 4: + return float64(v.r.p.ToFloat32(uint32(bits))) + case 8: + return v.r.p.ToFloat64(bits) + } + panic("Unexpected float size") +} + +func (v remoteFloat) Set(t *eval.Thread, x float64) { + v.aSet(t, x) +} + +func (v remoteFloat) aSet(a aborter, x float64) { + var bits uint64 + switch v.size { + case 4: + bits = uint64(v.r.p.FromFloat32(float32(x))) + case 8: + bits = v.r.p.FromFloat64(x) + default: + panic("Unexpected float size") + } + v.r.Set(a, v.size, bits) +} + +func (v remoteFloat) addr() remote { return v.r } + +func mkFloat32(r remote) eval.Value { return remoteFloat{r, 4} } + +func mkFloat64(r remote) eval.Value { return remoteFloat{r, 8} } + +func mkFloat(r remote) eval.Value { return remoteFloat{r, r.p.FloatSize()} } + +/* + * String + */ + +type remoteString struct { + r remote +} + +func (v remoteString) String() string { + return tryRVString(func(a aborter) string { return v.aGet(a) }) +} + +func (v remoteString) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.StringValue).Get(t)) +} + +func (v remoteString) Get(t *eval.Thread) string { + return v.aGet(t) +} + +func (v remoteString) aGet(a aborter) string { + rs := v.r.p.runtime.String.mk(v.r).(remoteStruct) + str := proc.Word(rs.field(v.r.p.f.String.Str).(remoteUint).aGet(a)) + len := rs.field(v.r.p.f.String.Len).(remoteInt).aGet(a) + + bytes := make([]uint8, len) + _, err := v.r.p.Peek(str, bytes) + if err != nil { + a.Abort(err) + } + return string(bytes) +} + +func (v remoteString) Set(t *eval.Thread, x string) { + v.aSet(t, x) +} + +func (v remoteString) aSet(a aborter, x string) { + // TODO(austin) This isn't generally possible without the + // ability to allocate remote memory. + a.Abort(ReadOnlyError("remote strings cannot be assigned to")) +} + +func mkString(r remote) eval.Value { return remoteString{r} } + +/* + * Array + */ + +type remoteArray struct { + r remote + len int64 + elemType *remoteType +} + +func (v remoteArray) String() string { + res := "{" + for i := int64(0); i < v.len; i++ { + if i > 0 { + res += ", " + } + res += v.elem(i).String() + } + return res + "}" +} + +func (v remoteArray) Assign(t *eval.Thread, o eval.Value) { + // TODO(austin) Could do a bigger memcpy if o is a + // remoteArray in the same Process. + oa := o.(eval.ArrayValue) + for i := int64(0); i < v.len; i++ { + v.Elem(t, i).Assign(t, oa.Elem(t, i)) + } +} + +func (v remoteArray) Get(t *eval.Thread) eval.ArrayValue { + return v +} + +func (v remoteArray) Elem(t *eval.Thread, i int64) eval.Value { + return v.elem(i) +} + +func (v remoteArray) elem(i int64) eval.Value { + return v.elemType.mk(v.r.plus(proc.Word(int64(v.elemType.size) * i))) +} + +func (v remoteArray) Sub(i int64, len int64) eval.ArrayValue { + return remoteArray{v.r.plus(proc.Word(int64(v.elemType.size) * i)), len, v.elemType} +} + +/* + * Struct + */ + +type remoteStruct struct { + r remote + layout []remoteStructField +} + +type remoteStructField struct { + offset int + fieldType *remoteType +} + +func (v remoteStruct) String() string { + res := "{" + for i := range v.layout { + if i > 0 { + res += ", " + } + res += v.field(i).String() + } + return res + "}" +} + +func (v remoteStruct) Assign(t *eval.Thread, o eval.Value) { + // TODO(austin) Could do a bigger memcpy. + oa := o.(eval.StructValue) + l := len(v.layout) + for i := 0; i < l; i++ { + v.Field(t, i).Assign(t, oa.Field(t, i)) + } +} + +func (v remoteStruct) Get(t *eval.Thread) eval.StructValue { + return v +} + +func (v remoteStruct) Field(t *eval.Thread, i int) eval.Value { + return v.field(i) +} + +func (v remoteStruct) field(i int) eval.Value { + f := &v.layout[i] + return f.fieldType.mk(v.r.plus(proc.Word(f.offset))) +} + +func (v remoteStruct) addr() remote { return v.r } + +/* + * Pointer + */ + +// TODO(austin) Comparing two remote pointers for equality in the +// interpreter will crash it because the Value's returned from +// remotePtr.Get() will be structs. + +type remotePtr struct { + r remote + elemType *remoteType +} + +func (v remotePtr) String() string { + return tryRVString(func(a aborter) string { + e := v.aGet(a) + if e == nil { + return "<nil>" + } + return "&" + e.String() + }) +} + +func (v remotePtr) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.PtrValue).Get(t)) +} + +func (v remotePtr) Get(t *eval.Thread) eval.Value { + return v.aGet(t) +} + +func (v remotePtr) aGet(a aborter) eval.Value { + addr := proc.Word(v.r.Get(a, v.r.p.PtrSize())) + if addr == 0 { + return nil + } + return v.elemType.mk(remote{addr, v.r.p}) +} + +func (v remotePtr) Set(t *eval.Thread, x eval.Value) { + v.aSet(t, x) +} + +func (v remotePtr) aSet(a aborter, x eval.Value) { + if x == nil { + v.r.Set(a, v.r.p.PtrSize(), 0) + return + } + xr, ok := x.(remoteValue) + if !ok || v.r.p != xr.addr().p { + a.Abort(RemoteMismatchError("remote pointer must point within the same process")) + } + v.r.Set(a, v.r.p.PtrSize(), uint64(xr.addr().base)) +} + +func (v remotePtr) addr() remote { return v.r } + +/* + * Slice + */ + +type remoteSlice struct { + r remote + elemType *remoteType +} + +func (v remoteSlice) String() string { + return tryRVString(func(a aborter) string { + b := v.aGet(a).Base + if b == nil { + return "<nil>" + } + return b.String() + }) +} + +func (v remoteSlice) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.SliceValue).Get(t)) +} + +func (v remoteSlice) Get(t *eval.Thread) eval.Slice { + return v.aGet(t) +} + +func (v remoteSlice) aGet(a aborter) eval.Slice { + rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct) + base := proc.Word(rs.field(v.r.p.f.Slice.Array).(remoteUint).aGet(a)) + nel := rs.field(v.r.p.f.Slice.Len).(remoteInt).aGet(a) + cap := rs.field(v.r.p.f.Slice.Cap).(remoteInt).aGet(a) + if base == 0 { + return eval.Slice{nil, nel, cap} + } + return eval.Slice{remoteArray{remote{base, v.r.p}, nel, v.elemType}, nel, cap} +} + +func (v remoteSlice) Set(t *eval.Thread, x eval.Slice) { + v.aSet(t, x) +} + +func (v remoteSlice) aSet(a aborter, x eval.Slice) { + rs := v.r.p.runtime.Slice.mk(v.r).(remoteStruct) + if x.Base == nil { + rs.field(v.r.p.f.Slice.Array).(remoteUint).aSet(a, 0) + } else { + ar, ok := x.Base.(remoteArray) + if !ok || v.r.p != ar.r.p { + a.Abort(RemoteMismatchError("remote slice must point within the same process")) + } + rs.field(v.r.p.f.Slice.Array).(remoteUint).aSet(a, uint64(ar.r.base)) + } + rs.field(v.r.p.f.Slice.Len).(remoteInt).aSet(a, x.Len) + rs.field(v.r.p.f.Slice.Cap).(remoteInt).aSet(a, x.Cap) +} diff --git a/libgo/go/exp/ogle/vars.go b/libgo/go/exp/ogle/vars.go new file mode 100644 index 000000000..8a3a14791 --- /dev/null +++ b/libgo/go/exp/ogle/vars.go @@ -0,0 +1,272 @@ +// 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 ( + "debug/gosym" + "debug/proc" + "exp/eval" + "log" + "os" +) + +/* + * Remote frame pointers + */ + +// A NotOnStack error occurs when attempting to access a variable in a +// remote frame where that remote frame is not on the current stack. +type NotOnStack struct { + Fn *gosym.Func + Goroutine *Goroutine +} + +func (e NotOnStack) String() string { + return "function " + e.Fn.Name + " not on " + e.Goroutine.String() + "'s stack" +} + +// A remoteFramePtr is an implementation of eval.PtrValue that +// represents a pointer to a function frame in a remote process. When +// accessed, this locates the function on the current goroutine's +// stack and returns a structure containing the local variables of +// that function. +type remoteFramePtr struct { + p *Process + fn *gosym.Func + rt *remoteType +} + +func (v remoteFramePtr) String() string { + // TODO(austin): This could be a really awesome string method + return "<remote frame>" +} + +func (v remoteFramePtr) Assign(t *eval.Thread, o eval.Value) { + v.Set(t, o.(eval.PtrValue).Get(t)) +} + +func (v remoteFramePtr) Get(t *eval.Thread) eval.Value { + g := v.p.curGoroutine + if g == nil || g.frame == nil { + t.Abort(NoCurrentGoroutine{}) + } + + for f := g.frame; f != nil; f = f.aOuter(t) { + if f.fn != v.fn { + continue + } + + // TODO(austin): Register for shootdown with f + return v.rt.mk(remote{f.fp, v.p}) + } + + t.Abort(NotOnStack{v.fn, g}) + panic("fail") +} + +func (v remoteFramePtr) Set(t *eval.Thread, x eval.Value) { + // Theoretically this could be a static error. If remote + // packages were packages, remote frames could just be defined + // as constants. + t.Abort(ReadOnlyError("remote frames cannot be assigned to")) +} + +/* + * Remote packages + */ + +// TODO(austin): Remote packages are implemented as structs right now, +// which has some weird consequences. You can attempt to assign to a +// remote package. It also produces terrible error messages. +// Ideally, these would actually be packages, but somehow first-class +// so they could be assigned to other names. + +// A remotePackage is an implementation of eval.StructValue that +// represents a package in a remote process. It's essentially a +// regular struct, except it cannot be assigned to. +type remotePackage struct { + defs []eval.Value +} + +func (v remotePackage) String() string { return "<remote package>" } + +func (v remotePackage) Assign(t *eval.Thread, o eval.Value) { + t.Abort(ReadOnlyError("remote packages cannot be assigned to")) +} + +func (v remotePackage) Get(t *eval.Thread) eval.StructValue { + return v +} + +func (v remotePackage) Field(t *eval.Thread, i int) eval.Value { + return v.defs[i] +} + +/* + * Remote variables + */ + +// populateWorld defines constants in the given world for each package +// in this process. These packages are structs that, in turn, contain +// fields for each global and function in that package. +func (p *Process) populateWorld(w *eval.World) os.Error { + type def struct { + t eval.Type + v eval.Value + } + packages := make(map[string]map[string]def) + + for _, s := range p.syms.Syms { + if s.ReceiverName() != "" { + // TODO(austin) + continue + } + + // Package + pkgName := s.PackageName() + switch pkgName { + case "", "type", "extratype", "string", "go": + // "go" is really "go.string" + continue + } + pkg, ok := packages[pkgName] + if !ok { + pkg = make(map[string]def) + packages[pkgName] = pkg + } + + // Symbol name + name := s.BaseName() + if _, ok := pkg[name]; ok { + log.Printf("Multiple definitions of symbol %s", s.Name) + continue + } + + // Symbol type + rt, err := p.typeOfSym(&s) + if err != nil { + return err + } + + // Definition + switch s.Type { + case 'D', 'd', 'B', 'b': + // Global variable + if rt == nil { + continue + } + pkg[name] = def{rt.Type, rt.mk(remote{proc.Word(s.Value), p})} + + case 'T', 't', 'L', 'l': + // Function + s := s.Func + // TODO(austin): Ideally, this would *also* be + // callable. How does that interact with type + // conversion syntax? + rt, err := p.makeFrameType(s) + if err != nil { + return err + } + pkg[name] = def{eval.NewPtrType(rt.Type), remoteFramePtr{p, s, rt}} + } + } + + // TODO(austin): Define remote types + + // Define packages + for pkgName, defs := range packages { + fields := make([]eval.StructField, len(defs)) + vals := make([]eval.Value, len(defs)) + i := 0 + for name, def := range defs { + fields[i].Name = name + fields[i].Type = def.t + vals[i] = def.v + i++ + } + pkgType := eval.NewStructType(fields) + pkgVal := remotePackage{vals} + + err := w.DefineConst(pkgName, pkgType, pkgVal) + if err != nil { + log.Printf("while defining package %s: %v", pkgName, err) + } + } + + return nil +} + +// typeOfSym returns the type associated with a symbol. If the symbol +// has no type, returns nil. +func (p *Process) typeOfSym(s *gosym.Sym) (*remoteType, os.Error) { + if s.GoType == 0 { + return nil, nil + } + addr := proc.Word(s.GoType) + var rt *remoteType + err := try(func(a aborter) { rt = parseRemoteType(a, p.runtime.Type.mk(remote{addr, p}).(remoteStruct)) }) + if err != nil { + return nil, err + } + return rt, nil +} + +// makeFrameType constructs a struct type for the frame of a function. +// The offsets in this struct type are such that the struct can be +// instantiated at this function's frame pointer. +func (p *Process) makeFrameType(s *gosym.Func) (*remoteType, os.Error) { + n := len(s.Params) + len(s.Locals) + fields := make([]eval.StructField, n) + layout := make([]remoteStructField, n) + i := 0 + + // TODO(austin): There can be multiple locals/parameters with + // the same name. We probably need liveness information to do + // anything about this. Once we have that, perhaps we give + // such fields interface{} type? Or perhaps we disambiguate + // the names with numbers. Disambiguation is annoying for + // things like "i", where there's an obvious right answer. + + for _, param := range s.Params { + rt, err := p.typeOfSym(param) + if err != nil { + return nil, err + } + if rt == nil { + //fmt.Printf(" (no type)\n"); + continue + } + // TODO(austin): Why do local variables carry their + // package name? + fields[i].Name = param.BaseName() + fields[i].Type = rt.Type + // Parameters have positive offsets from FP + layout[i].offset = int(param.Value) + layout[i].fieldType = rt + i++ + } + + for _, local := range s.Locals { + rt, err := p.typeOfSym(local) + if err != nil { + return nil, err + } + if rt == nil { + continue + } + fields[i].Name = local.BaseName() + fields[i].Type = rt.Type + // Locals have negative offsets from FP - PtrSize + layout[i].offset = -int(local.Value) - p.PtrSize() + layout[i].fieldType = rt + i++ + } + + fields = fields[0:i] + layout = layout[0:i] + t := eval.NewStructType(fields) + mk := func(r remote) eval.Value { return remoteStruct{r, layout} } + return &remoteType{t, 0, 0, mk}, nil +} |