diff options
Diffstat (limited to 'src/pkg/exp/ogle')
-rw-r--r-- | src/pkg/exp/ogle/Makefile | 29 | ||||
-rw-r--r-- | src/pkg/exp/ogle/abort.go | 35 | ||||
-rw-r--r-- | src/pkg/exp/ogle/arch.go | 125 | ||||
-rw-r--r-- | src/pkg/exp/ogle/cmd.go | 373 | ||||
-rw-r--r-- | src/pkg/exp/ogle/event.go | 280 | ||||
-rw-r--r-- | src/pkg/exp/ogle/frame.go | 212 | ||||
-rw-r--r-- | src/pkg/exp/ogle/goroutine.go | 117 | ||||
-rw-r--r-- | src/pkg/exp/ogle/main.go | 9 | ||||
-rw-r--r-- | src/pkg/exp/ogle/process.go | 521 | ||||
-rw-r--r-- | src/pkg/exp/ogle/rruntime.go | 271 | ||||
-rw-r--r-- | src/pkg/exp/ogle/rtype.go | 288 | ||||
-rw-r--r-- | src/pkg/exp/ogle/rvalue.go | 515 | ||||
-rw-r--r-- | src/pkg/exp/ogle/vars.go | 272 |
13 files changed, 0 insertions, 3047 deletions
diff --git a/src/pkg/exp/ogle/Makefile b/src/pkg/exp/ogle/Makefile deleted file mode 100644 index ef65d36c8..000000000 --- a/src/pkg/exp/ogle/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -# 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. - -include ../../../Make.inc - -TARG=exp/ogle -GOFILES=\ - abort.go\ - arch.go\ - cmd.go\ - event.go\ - frame.go\ - goroutine.go\ - rruntime.go\ - rtype.go\ - rvalue.go\ - process.go\ - vars.go\ - -CLEANFILES+=ogle - -include ../../../Make.pkg - -main.$O: main.go package - $(GC) -I_obj $< - -ogle: main.$O - $(LD) -L_obj -o $@ $< diff --git a/src/pkg/exp/ogle/abort.go b/src/pkg/exp/ogle/abort.go deleted file mode 100644 index 311a7b38e..000000000 --- a/src/pkg/exp/ogle/abort.go +++ /dev/null @@ -1,35 +0,0 @@ -// 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/src/pkg/exp/ogle/arch.go b/src/pkg/exp/ogle/arch.go deleted file mode 100644 index 52b1c9757..000000000 --- a/src/pkg/exp/ogle/arch.go +++ /dev/null @@ -1,125 +0,0 @@ -// 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/src/pkg/exp/ogle/cmd.go b/src/pkg/exp/ogle/cmd.go deleted file mode 100644 index ff0d24c69..000000000 --- a/src/pkg/exp/ogle/cmd.go +++ /dev/null @@ -1,373 +0,0 @@ -// 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 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, " ") - if len(parts) == 0 { - fname = "" - } else { - fname = parts[0] - } - tproc, err = proc.StartProcess(fname, parts, &os.ProcAttr{Files: []*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) - 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]string - 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/src/pkg/exp/ogle/event.go b/src/pkg/exp/ogle/event.go deleted file mode 100644 index d7092ded3..000000000 --- a/src/pkg/exp/ogle/event.go +++ /dev/null @@ -1,280 +0,0 @@ -// 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/src/pkg/exp/ogle/frame.go b/src/pkg/exp/ogle/frame.go deleted file mode 100644 index 1538362ba..000000000 --- a/src/pkg/exp/ogle/frame.go +++ /dev/null @@ -1,212 +0,0 @@ -// 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/src/pkg/exp/ogle/goroutine.go b/src/pkg/exp/ogle/goroutine.go deleted file mode 100644 index 5104ec6d4..000000000 --- a/src/pkg/exp/ogle/goroutine.go +++ /dev/null @@ -1,117 +0,0 @@ -// 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/src/pkg/exp/ogle/main.go b/src/pkg/exp/ogle/main.go deleted file mode 100644 index 1999eccca..000000000 --- a/src/pkg/exp/ogle/main.go +++ /dev/null @@ -1,9 +0,0 @@ -// 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/src/pkg/exp/ogle/process.go b/src/pkg/exp/ogle/process.go deleted file mode 100644 index 7c803b3a2..000000000 --- a/src/pkg/exp/ogle/process.go +++ /dev/null @@ -1,521 +0,0 @@ -// 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.ValueOf(&p.runtime)) - rtvt := rtv.Type() - 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).SetUint(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/src/pkg/exp/ogle/rruntime.go b/src/pkg/exp/ogle/rruntime.go deleted file mode 100644 index 950418b53..000000000 --- a/src/pkg/exp/ogle/rruntime.go +++ /dev/null @@ -1,271 +0,0 @@ -// 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.ValueOf(out)) - outt := outv.Type() - runtimev := reflect.Indirect(reflect.ValueOf(runtime)) - - // 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) - outStructt := outStructv.Type() - for j := 0; j < outStructt.NumField(); j++ { - f := outStructv.Field(j) - name := outStructt.Field(j).Name - f.SetInt(int64(indexes[name])) - } - } -} diff --git a/src/pkg/exp/ogle/rtype.go b/src/pkg/exp/ogle/rtype.go deleted file mode 100644 index b3c35575a..000000000 --- a/src/pkg/exp/ogle/rtype.go +++ /dev/null @@ -1,288 +0,0 @@ -// 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/src/pkg/exp/ogle/rvalue.go b/src/pkg/exp/ogle/rvalue.go deleted file mode 100644 index 3d630f936..000000000 --- a/src/pkg/exp/ogle/rvalue.go +++ /dev/null @@ -1,515 +0,0 @@ -// 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/src/pkg/exp/ogle/vars.go b/src/pkg/exp/ogle/vars.go deleted file mode 100644 index 8a3a14791..000000000 --- a/src/pkg/exp/ogle/vars.go +++ /dev/null @@ -1,272 +0,0 @@ -// 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 -} |