summaryrefslogtreecommitdiff
path: root/libgo/go/exp/ogle/process.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/exp/ogle/process.go')
-rw-r--r--libgo/go/exp/ogle/process.go521
1 files changed, 0 insertions, 521 deletions
diff --git a/libgo/go/exp/ogle/process.go b/libgo/go/exp/ogle/process.go
deleted file mode 100644
index 7c803b3a27e..00000000000
--- a/libgo/go/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()
-}