diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go index 6cd6669b..59b2073d 100644 --- a/pkg/proc/bininfo.go +++ b/pkg/proc/bininfo.go @@ -22,13 +22,13 @@ import ( type BinaryInfo struct { lastModified time.Time // Time the executable of this process was last modified - goos string + GOOS string closer io.Closer // Maps package names to package paths, needed to lookup types inside DWARF info packageMap map[string]string - arch Arch + Arch Arch dwarf *dwarf.Data frameEntries frame.FrameDescriptionEntries lineInfo line.DebugLines @@ -46,12 +46,12 @@ var UnsupportedWindowsArchErr = errors.New("unsupported architecture of windows/ var UnsupportedDarwinArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported") func NewBinaryInfo(goos, goarch string) BinaryInfo { - r := BinaryInfo{goos: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry)} + r := BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry)} // TODO: find better way to determine proc arch (perhaps use executable file info) switch goarch { case "amd64": - r.arch = AMD64Arch(goos) + r.Arch = AMD64Arch(goos) } return r @@ -63,7 +63,7 @@ func (bininfo *BinaryInfo) LoadBinaryInfo(path string, wg *sync.WaitGroup) error bininfo.lastModified = fi.ModTime() } - switch bininfo.goos { + switch bininfo.GOOS { case "linux": return bininfo.LoadBinaryInfoElf(path, wg) case "windows": @@ -112,6 +112,11 @@ func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *gosy return bi.goSymTable.LineToPC(filename, lineno) } +// PCToFunc returns the function containing the given PC address +func (bi *BinaryInfo) PCToFunc(pc uint64) *gosym.Func { + return bi.goSymTable.PCToFunc(pc) +} + func (bi *BinaryInfo) Close() error { return bi.closer.Close() } diff --git a/pkg/proc/breakpoints.go b/pkg/proc/breakpoints.go index 3381c062..a8263c96 100644 --- a/pkg/proc/breakpoints.go +++ b/pkg/proc/breakpoints.go @@ -72,42 +72,30 @@ func (bp *Breakpoint) String() string { return fmt.Sprintf("Breakpoint %d at %#v %s:%d (%d)", bp.ID, bp.Addr, bp.File, bp.Line, bp.TotalHitCount) } -// ClearBreakpoint clears the specified breakpoint. -func (thread *Thread) ClearBreakpoint(bp *Breakpoint) (*Breakpoint, error) { - if _, err := thread.writeMemory(uintptr(bp.Addr), bp.OriginalData); err != nil { - return nil, fmt.Errorf("could not clear breakpoint %s", err) - } - return bp, nil -} - // BreakpointExistsError is returned when trying to set a breakpoint at // an address that already has a breakpoint set for it. type BreakpointExistsError struct { - file string - line int - addr uint64 + File string + Line int + Addr uint64 } func (bpe BreakpointExistsError) Error() string { - return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr) + return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.File, bpe.Line, bpe.Addr) } // InvalidAddressError represents the result of // attempting to set a breakpoint at an invalid address. type InvalidAddressError struct { - address uint64 + Address uint64 } func (iae InvalidAddressError) Error() string { - return fmt.Sprintf("Invalid address %#v\n", iae.address) + return fmt.Sprintf("Invalid address %#v\n", iae.Address) } -func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error { - _, err := thread.writeMemory(uintptr(addr), dbp.bi.arch.BreakpointInstruction()) - return err -} - -func (bp *Breakpoint) checkCondition(thread IThread) (bool, error) { +// CheckCondition evaluates bp's condition on thread. +func (bp *Breakpoint) CheckCondition(thread IThread) (bool, error) { if bp.Cond == nil { return true, nil } @@ -154,9 +142,9 @@ func (bp *Breakpoint) Internal() bool { // NoBreakpointError is returned when trying to // clear a breakpoint that does not exist. type NoBreakpointError struct { - addr uint64 + Addr uint64 } func (nbp NoBreakpointError) Error() string { - return fmt.Sprintf("no breakpoint at %#v", nbp.addr) + return fmt.Sprintf("no breakpoint at %#v", nbp.Addr) } diff --git a/pkg/proc/core.go b/pkg/proc/core/core.go similarity index 76% rename from pkg/proc/core.go rename to pkg/proc/core/core.go index f7d936da..51f24d0f 100644 --- a/pkg/proc/core.go +++ b/pkg/proc/core/core.go @@ -1,4 +1,4 @@ -package proc +package core import ( "debug/gosym" @@ -7,6 +7,8 @@ import ( "go/ast" "io" "sync" + + "github.com/derekparker/delve/pkg/proc" ) // A SplicedMemory represents a memory space formed from multiple regions, @@ -29,11 +31,11 @@ type SplicedMemory struct { type readerEntry struct { offset uintptr length uintptr - reader MemoryReader + reader proc.MemoryReader } // Add adds a new region to the SplicedMemory, which may override existing regions. -func (r *SplicedMemory) Add(reader MemoryReader, off, length uintptr) { +func (r *SplicedMemory) Add(reader proc.MemoryReader, off, length uintptr) { if length == 0 { return } @@ -141,12 +143,12 @@ func (r *OffsetReaderAt) ReadMemory(buf []byte, addr uintptr) (n int, err error) } type CoreProcess struct { - bi BinaryInfo + bi proc.BinaryInfo core *Core - breakpoints map[uint64]*Breakpoint + breakpoints map[uint64]*proc.Breakpoint currentThread *LinuxPrStatus - selectedGoroutine *G - allGCache []*G + selectedGoroutine *proc.G + allGCache []*proc.G } type CoreThread struct { @@ -165,8 +167,8 @@ func OpenCore(corePath, exePath string) (*CoreProcess, error) { } p := &CoreProcess{ core: core, - breakpoints: make(map[uint64]*Breakpoint), - bi: NewBinaryInfo("linux", "amd64"), + breakpoints: make(map[uint64]*proc.Breakpoint), + bi: proc.NewBinaryInfo("linux", "amd64"), } var wg sync.WaitGroup @@ -178,19 +180,18 @@ func OpenCore(corePath, exePath string) (*CoreProcess, error) { break } - scope := &EvalScope{0, 0, p.CurrentThread(), nil, &p.bi} - ver, isextld, err := scope.getGoInformation() + ver, isextld, err := proc.GetGoInformation(p) if err != nil { return nil, err } - p.bi.arch.SetGStructOffset(ver, isextld) - p.selectedGoroutine, _ = GetG(p.CurrentThread()) + p.bi.Arch.SetGStructOffset(ver, isextld) + p.selectedGoroutine, _ = proc.GetG(p.CurrentThread()) return p, nil } -func (p *CoreProcess) BinInfo() *BinaryInfo { +func (p *CoreProcess) BinInfo() *proc.BinaryInfo { return &p.bi } @@ -202,16 +203,16 @@ func (thread *CoreThread) ReadMemory(data []byte, addr uintptr) (n int, err erro return n, err } -func (thread *CoreThread) writeMemory(addr uintptr, data []byte) (int, error) { +func (thread *CoreThread) WriteMemory(addr uintptr, data []byte) (int, error) { return 0, ErrWriteCore } -func (t *CoreThread) Location() (*Location, error) { +func (t *CoreThread) Location() (*proc.Location, error) { f, l, fn := t.p.bi.PCToLine(t.th.Reg.Rip) - return &Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil + return &proc.Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil } -func (t *CoreThread) Breakpoint() (*Breakpoint, bool, error) { +func (t *CoreThread) Breakpoint() (*proc.Breakpoint, bool, error) { return nil, false, nil } @@ -219,16 +220,16 @@ func (t *CoreThread) ThreadID() int { return int(t.th.Pid) } -func (t *CoreThread) Registers(floatingPoint bool) (Registers, error) { +func (t *CoreThread) Registers(floatingPoint bool) (proc.Registers, error) { //TODO(aarzilli): handle floating point registers return &t.th.Reg, nil } -func (t *CoreThread) Arch() Arch { - return t.p.bi.arch +func (t *CoreThread) Arch() proc.Arch { + return t.p.bi.Arch } -func (t *CoreThread) BinInfo() *BinaryInfo { +func (t *CoreThread) BinInfo() *proc.BinaryInfo { return &t.p.bi } @@ -236,19 +237,23 @@ func (t *CoreThread) StepInstruction() error { return ErrContinueCore } -func (p *CoreProcess) Breakpoints() map[uint64]*Breakpoint { +func (t *CoreThread) Blocked() bool { + return false +} + +func (p *CoreProcess) Breakpoints() map[uint64]*proc.Breakpoint { return p.breakpoints } -func (p *CoreProcess) ClearBreakpoint(addr uint64) (*Breakpoint, error) { - return nil, NoBreakpointError{addr: addr} +func (p *CoreProcess) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) { + return nil, proc.NoBreakpointError{Addr: addr} } func (p *CoreProcess) ClearInternalBreakpoints() error { return nil } -func (p *CoreProcess) ContinueOnce() (IThread, error) { +func (p *CoreProcess) ContinueOnce() (proc.IThread, error) { return nil, ErrContinueCore } @@ -260,7 +265,7 @@ func (p *CoreProcess) RequestManualStop() error { return nil } -func (p *CoreProcess) CurrentThread() IThread { +func (p *CoreProcess) CurrentThread() proc.IThread { return &CoreThread{p.currentThread, p} } @@ -273,18 +278,18 @@ func (p *CoreProcess) Exited() bool { } func (p *CoreProcess) FindFileLocation(fileName string, lineNumber int) (uint64, error) { - return FindFileLocation(p.CurrentThread(), p.breakpoints, &p.bi, fileName, lineNumber) + return proc.FindFileLocation(p.CurrentThread(), p.breakpoints, &p.bi, fileName, lineNumber) } func (p *CoreProcess) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) { - return FirstPCAfterPrologue(p.CurrentThread(), p.breakpoints, &p.bi, fn, sameline) + return proc.FirstPCAfterPrologue(p.CurrentThread(), p.breakpoints, &p.bi, fn, sameline) } func (p *CoreProcess) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) { - return FindFunctionLocation(p.CurrentThread(), p.breakpoints, &p.bi, funcName, firstLine, lineOffset) + return proc.FindFunctionLocation(p.CurrentThread(), p.breakpoints, &p.bi, funcName, firstLine, lineOffset) } -func (p *CoreProcess) AllGCache() *[]*G { +func (p *CoreProcess) AllGCache() *[]*proc.G { return &p.allGCache } @@ -304,16 +309,16 @@ func (p *CoreProcess) Running() bool { return false } -func (p *CoreProcess) SelectedGoroutine() *G { +func (p *CoreProcess) SelectedGoroutine() *proc.G { return p.selectedGoroutine } -func (p *CoreProcess) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) { +func (p *CoreProcess) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) { return nil, ErrWriteCore } func (p *CoreProcess) SwitchGoroutine(gid int) error { - g, err := FindGoroutine(p, gid) + g, err := proc.FindGoroutine(p, gid) if err != nil { return err } @@ -321,8 +326,8 @@ func (p *CoreProcess) SwitchGoroutine(gid int) error { // user specified -1 and selectedGoroutine is nil return nil } - if g.thread != nil { - return p.SwitchThread(g.thread.ThreadID()) + if g.Thread != nil { + return p.SwitchThread(g.Thread.ThreadID()) } p.selectedGoroutine = g return nil @@ -331,21 +336,21 @@ func (p *CoreProcess) SwitchGoroutine(gid int) error { func (p *CoreProcess) SwitchThread(tid int) error { if th, ok := p.core.Threads[tid]; ok { p.currentThread = th - p.selectedGoroutine, _ = GetG(p.CurrentThread()) + p.selectedGoroutine, _ = proc.GetG(p.CurrentThread()) return nil } return fmt.Errorf("thread %d does not exist", tid) } -func (p *CoreProcess) ThreadList() []IThread { - r := make([]IThread, 0, len(p.core.Threads)) +func (p *CoreProcess) ThreadList() []proc.IThread { + r := make([]proc.IThread, 0, len(p.core.Threads)) for _, v := range p.core.Threads { r = append(r, &CoreThread{v, p}) } return r } -func (p *CoreProcess) FindThread(threadID int) (IThread, bool) { +func (p *CoreProcess) FindThread(threadID int) (proc.IThread, bool) { t, ok := p.core.Threads[threadID] return &CoreThread{t, p}, ok } diff --git a/pkg/proc/core_test.go b/pkg/proc/core/core_test.go similarity index 93% rename from pkg/proc/core_test.go rename to pkg/proc/core/core_test.go index 882ce7f8..7918ac0c 100644 --- a/pkg/proc/core_test.go +++ b/pkg/proc/core/core_test.go @@ -1,4 +1,4 @@ -package proc +package core import ( "bytes" @@ -13,6 +13,7 @@ import ( "strings" "testing" + "github.com/derekparker/delve/pkg/proc" "github.com/derekparker/delve/pkg/proc/test" ) @@ -155,13 +156,13 @@ func TestCore(t *testing.T) { t.Errorf("read apport log: %q, %v", apport, err) t.Fatalf("ReadCore() failed: %v", err) } - gs, err := GoroutinesInfo(p) + gs, err := proc.GoroutinesInfo(p) if err != nil || len(gs) == 0 { t.Fatalf("GoroutinesInfo() = %v, %v; wanted at least one goroutine", gs, err) } - var panicking *G - var panickingStack []Stackframe + var panicking *proc.G + var panickingStack []proc.Stackframe for _, g := range gs { stack, err := g.Stacktrace(10) if err != nil { @@ -178,7 +179,7 @@ func TestCore(t *testing.T) { t.Fatalf("Didn't find a call to panic in goroutine stacks: %v", gs) } - var mainFrame *Stackframe + var mainFrame *proc.Stackframe // Walk backward, because the current function seems to be main.main // in the actual call to panic(). for i := len(panickingStack) - 1; i >= 0; i-- { @@ -189,7 +190,7 @@ func TestCore(t *testing.T) { if mainFrame == nil { t.Fatalf("Couldn't find main in stack %v", panickingStack) } - msg, err := FrameToScope(p, *mainFrame).EvalVariable("msg", LoadConfig{MaxStringLen: 64}) + msg, err := proc.FrameToScope(p, *mainFrame).EvalVariable("msg", proc.LoadConfig{MaxStringLen: 64}) if err != nil { t.Fatalf("Couldn't EvalVariable(msg, ...): %v", err) } diff --git a/pkg/proc/linux_amd64_core.go b/pkg/proc/core/linux_amd64_core.go similarity index 96% rename from pkg/proc/linux_amd64_core.go rename to pkg/proc/core/linux_amd64_core.go index a06783db..ba052687 100644 --- a/pkg/proc/linux_amd64_core.go +++ b/pkg/proc/core/linux_amd64_core.go @@ -1,4 +1,4 @@ -package proc +package core import ( "bytes" @@ -11,6 +11,8 @@ import ( "golang.org/x/debug/elf" "golang.org/x/arch/x86/x86asm" + + "github.com/derekparker/delve/pkg/proc" ) // Copied from golang.org/x/sys/unix.PtraceRegs since it's not available on @@ -232,14 +234,14 @@ func (r *LinuxCoreRegisters) Get(n int) (uint64, error) { return r.R15, nil } - return 0, UnknownRegisterError + return 0, proc.UnknownRegisterError } -func (r *LinuxCoreRegisters) SetPC(IThread, uint64) error { +func (r *LinuxCoreRegisters) SetPC(proc.IThread, uint64) error { return errors.New("not supported") } -func (r *LinuxCoreRegisters) Slice() []Register { +func (r *LinuxCoreRegisters) Slice() []proc.Register { var regs = []struct { k string v uint64 @@ -272,12 +274,12 @@ func (r *LinuxCoreRegisters) Slice() []Register { {"Fs", r.Fs}, {"Gs", r.Gs}, } - out := make([]Register, 0, len(regs)) + out := make([]proc.Register, 0, len(regs)) for _, reg := range regs { if reg.k == "Eflags" { - out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64) + out = proc.AppendEflagReg(out, reg.k, reg.v) } else { - out = appendQwordReg(out, reg.k, reg.v) + out = proc.AppendQwordReg(out, reg.k, reg.v) } } return out @@ -328,7 +330,7 @@ func readCore(corePath, exePath string) (*Core, error) { } type Core struct { - MemoryReader + proc.MemoryReader Threads map[int]*LinuxPrStatus Pid int } @@ -448,7 +450,7 @@ func skipPadding(r io.ReadSeeker, pad int64) error { return nil } -func buildMemory(core *elf.File, exe io.ReaderAt, notes []*Note) MemoryReader { +func buildMemory(core *elf.File, exe io.ReaderAt, notes []*Note) proc.MemoryReader { memory := &SplicedMemory{} // For now, assume all file mappings are to the exe. diff --git a/pkg/proc/disasm.go b/pkg/proc/disasm.go index 330b0d50..95c7ee18 100644 --- a/pkg/proc/disasm.go +++ b/pkg/proc/disasm.go @@ -35,16 +35,16 @@ func Disassemble(dbp DisassembleInfo, g *G, startPC, endPC uint64) ([]AsmInstruc } var regs Registers - var mem memoryReadWriter = dbp.CurrentThread() - if g.thread != nil { - mem = g.thread - regs, _ = g.thread.Registers(false) + var mem MemoryReadWriter = dbp.CurrentThread() + if g.Thread != nil { + mem = g.Thread + regs, _ = g.Thread.Registers(false) } return disassemble(mem, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC) } -func disassemble(memrw memoryReadWriter, regs Registers, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, startPC, endPC uint64) ([]AsmInstruction, error) { +func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, startPC, endPC uint64) ([]AsmInstruction, error) { mem := make([]byte, int(endPC-startPC)) _, err := memrw.ReadMemory(mem, uintptr(startPC)) if err != nil { diff --git a/pkg/proc/disasm_amd64.go b/pkg/proc/disasm_amd64.go index dc7a135d..ca45cc29 100644 --- a/pkg/proc/disasm_amd64.go +++ b/pkg/proc/disasm_amd64.go @@ -63,7 +63,7 @@ func (inst *AsmInstruction) IsCall() bool { return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL } -func resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers, mem memoryReadWriter, bininfo *BinaryInfo) *Location { +func resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location { if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL { return nil } @@ -140,13 +140,9 @@ func init() { } } -func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) { - return FirstPCAfterPrologue(dbp.currentThread, dbp.breakpoints, &dbp.bi, fn, sameline) -} - // FirstPCAfterPrologue returns the address of the first instruction after the prologue for function fn // If sameline is set FirstPCAfterPrologue will always return an address associated with the same line as fn.Entry -func FirstPCAfterPrologue(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fn *gosym.Func, sameline bool) (uint64, error) { +func FirstPCAfterPrologue(mem MemoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fn *gosym.Func, sameline bool) (uint64, error) { text, err := disassemble(mem, nil, breakpoints, bi, fn.Entry, fn.End) if err != nil { return fn.Entry, err diff --git a/pkg/proc/eval.go b/pkg/proc/eval.go index 3a39230e..1d87c5f1 100644 --- a/pkg/proc/eval.go +++ b/pkg/proc/eval.go @@ -67,10 +67,10 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) { // try to interpret the selector as a package variable if maybePkg, ok := node.X.(*ast.Ident); ok { if maybePkg.Name == "runtime" && node.Sel.Name == "curg" { - if scope.gvar == nil { + if scope.Gvar == nil { return nilVariable, nil } - return scope.gvar.clone(), nil + return scope.Gvar.clone(), nil } else if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil { return v, nil } @@ -144,7 +144,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) { fnnode = p.X } - styp, err := scope.bi.findTypeExpr(fnnode) + styp, err := scope.BinInfo.findTypeExpr(fnnode) if err != nil { return nil, err } @@ -152,7 +152,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) { converr := fmt.Errorf("can not convert %q to %s", exprToString(node.Args[0]), typ.String()) - v := newVariable("", 0, styp, scope.bi, scope.Mem) + v := newVariable("", 0, styp, scope.BinInfo, scope.Mem) v.loaded = true switch ttyp := typ.(type) { @@ -457,7 +457,7 @@ func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) { return v, nil } // if it's not a local variable then it could be a package variable w/o explicit package name - _, _, fn := scope.bi.PCToLine(scope.PC) + _, _, fn := scope.BinInfo.PCToLine(scope.PC) if fn != nil { if v, err = scope.packageVarAddr(fn.PackageName() + "." + node.Name); err == nil { v.Name = node.Name @@ -495,7 +495,7 @@ func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, err if xv.Children[0].Addr == 0 { return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type)) } - typ, err := scope.bi.findTypeExpr(node.Type) + typ, err := scope.BinInfo.findTypeExpr(node.Type) if err != nil { return nil, err } @@ -640,7 +640,7 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) { xev.OnlyAddr = true typename := "*" + xev.DwarfType.Common().Name - rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.bi.arch.PtrSize()), Name: typename}, Type: xev.DwarfType}) + rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.BinInfo.Arch.PtrSize()), Name: typename}, Type: xev.DwarfType}) rv.Children = []Variable{*xev} rv.loaded = true diff --git a/pkg/proc/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go similarity index 87% rename from pkg/proc/gdbserver.go rename to pkg/proc/gdbserial/gdbserver.go index c0fc58d8..14cb451b 100644 --- a/pkg/proc/gdbserver.go +++ b/pkg/proc/gdbserial/gdbserver.go @@ -59,7 +59,7 @@ // // Therefore the following code will assume lldb-server-like behavior. -package proc +package gdbserial import ( "debug/gosym" @@ -77,6 +77,8 @@ import ( "time" "golang.org/x/arch/x86/x86asm" + + "github.com/derekparker/delve/pkg/proc" ) const ( @@ -94,17 +96,17 @@ const heartbeatInterval = 10 * time.Second // GdbserverProcess implements target.Interface using a connection to a // debugger stub that understands Gdb Remote Serial Protocol. type GdbserverProcess struct { - bi BinaryInfo + bi proc.BinaryInfo conn gdbConn threads map[int]*GdbserverThread currentThread *GdbserverThread - selectedGoroutine *G + selectedGoroutine *proc.G exited bool ctrlC bool // ctrl-c was sent to stop inferior - breakpoints map[uint64]*Breakpoint + breakpoints map[uint64]*proc.Breakpoint breakpointIDCounter int internalBreakpointIDCounter int @@ -115,7 +117,7 @@ type GdbserverProcess struct { process *exec.Cmd - allGCache []*G + allGCache []*proc.G } // GdbserverThread is a thread of GdbserverProcess. @@ -123,7 +125,7 @@ type GdbserverThread struct { ID int strID string regs gdbRegisters - CurrentBreakpoint *Breakpoint + CurrentBreakpoint *proc.Breakpoint BreakpointConditionMet bool BreakpointConditionError error p *GdbserverProcess @@ -175,8 +177,8 @@ func GdbserverConnect(addr string, path string, pid int, attempts int) (*Gdbserv }, threads: make(map[int]*GdbserverThread), - bi: NewBinaryInfo(runtime.GOOS, runtime.GOARCH), - breakpoints: make(map[uint64]*Breakpoint), + bi: proc.NewBinaryInfo(runtime.GOOS, runtime.GOARCH), + breakpoints: make(map[uint64]*proc.Breakpoint), gcmdok: true, threadStopInfo: true, } @@ -247,20 +249,19 @@ func GdbserverConnect(addr string, path string, pid int, attempts int) (*Gdbserv } } - scope := &EvalScope{0, 0, p.CurrentThread(), nil, &p.bi} - ver, isextld, err := scope.getGoInformation() + ver, isextld, err := proc.GetGoInformation(p) if err != nil { conn.Close() p.bi.Close() return nil, err } - p.bi.arch.SetGStructOffset(ver, isextld) - p.selectedGoroutine, _ = GetG(p.CurrentThread()) + p.bi.Arch.SetGStructOffset(ver, isextld) + p.selectedGoroutine, _ = proc.GetG(p.CurrentThread()) panicpc, err := p.FindFunctionLocation("runtime.startpanic", true, 0) if err == nil { - bp, err := p.SetBreakpoint(panicpc, UserBreakpoint, nil) + bp, err := p.SetBreakpoint(panicpc, proc.UserBreakpoint, nil) if err == nil { bp.Name = "unrecovered-panic" bp.ID = -1 @@ -298,7 +299,7 @@ const debugserverExecutable = "/Library/Developer/CommandLineTools/Library/Priva func LLDBLaunch(cmd []string, wd string) (*GdbserverProcess, error) { // check that the argument to Launch is an executable file if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 { - return nil, NotExecutableErr + return nil, proc.NotExecutableErr } port := unusedPort() @@ -400,7 +401,7 @@ func (p *GdbserverProcess) loadProcessInfo(pid int) (int, string, error) { return pid, pi["name"], nil } -func (p *GdbserverProcess) BinInfo() *BinaryInfo { +func (p *GdbserverProcess) BinInfo() *proc.BinaryInfo { return &p.bi } @@ -417,39 +418,39 @@ func (p *GdbserverProcess) Running() bool { } func (p *GdbserverProcess) FindFileLocation(fileName string, lineNumber int) (uint64, error) { - return FindFileLocation(p.CurrentThread(), p.breakpoints, &p.bi, fileName, lineNumber) + return proc.FindFileLocation(p.CurrentThread(), p.breakpoints, &p.bi, fileName, lineNumber) } func (p *GdbserverProcess) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) { - return FirstPCAfterPrologue(p.CurrentThread(), p.breakpoints, &p.bi, fn, sameline) + return proc.FirstPCAfterPrologue(p.CurrentThread(), p.breakpoints, &p.bi, fn, sameline) } func (p *GdbserverProcess) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) { - return FindFunctionLocation(p.CurrentThread(), p.breakpoints, &p.bi, funcName, firstLine, lineOffset) + return proc.FindFunctionLocation(p.CurrentThread(), p.breakpoints, &p.bi, funcName, firstLine, lineOffset) } -func (p *GdbserverProcess) FindThread(threadID int) (IThread, bool) { +func (p *GdbserverProcess) FindThread(threadID int) (proc.IThread, bool) { thread, ok := p.threads[threadID] return thread, ok } -func (p *GdbserverProcess) ThreadList() []IThread { - r := make([]IThread, 0, len(p.threads)) +func (p *GdbserverProcess) ThreadList() []proc.IThread { + r := make([]proc.IThread, 0, len(p.threads)) for _, thread := range p.threads { r = append(r, thread) } return r } -func (p *GdbserverProcess) CurrentThread() IThread { +func (p *GdbserverProcess) CurrentThread() proc.IThread { return p.currentThread } -func (p *GdbserverProcess) AllGCache() *[]*G { +func (p *GdbserverProcess) AllGCache() *[]*proc.G { return &p.allGCache } -func (p *GdbserverProcess) SelectedGoroutine() *G { +func (p *GdbserverProcess) SelectedGoroutine() *proc.G { return p.selectedGoroutine } @@ -460,9 +461,9 @@ const ( stopSignal = 0x13 ) -func (p *GdbserverProcess) ContinueOnce() (IThread, error) { +func (p *GdbserverProcess) ContinueOnce() (proc.IThread, error) { if p.exited { - return nil, &ProcessExitedError{Pid: p.conn.pid} + return nil, &proc.ProcessExitedError{Pid: p.conn.pid} } // step threads stopped at any breakpoint over their breakpoint @@ -491,7 +492,7 @@ continueLoop: tu.done = false threadID, sig, err = p.conn.resume(sig, &tu) if err != nil { - if _, exited := err.(ProcessExitedError); exited { + if _, exited := err.(proc.ProcessExitedError); exited { p.exited = true } return nil, err @@ -547,35 +548,35 @@ func (p *GdbserverProcess) StepInstruction() error { if p.selectedGoroutine == nil { return errors.New("cannot single step: no selected goroutine") } - if p.selectedGoroutine.thread == nil { - if _, err := p.SetBreakpoint(p.selectedGoroutine.PC, NextBreakpoint, sameGoroutineCondition(p.selectedGoroutine)); err != nil { + if p.selectedGoroutine.Thread == nil { + if _, err := p.SetBreakpoint(p.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(p.selectedGoroutine)); err != nil { return err } - return Continue(p) + return proc.Continue(p) } p.allGCache = nil if p.exited { - return &ProcessExitedError{Pid: p.conn.pid} + return &proc.ProcessExitedError{Pid: p.conn.pid} } - p.selectedGoroutine.thread.(*GdbserverThread).clearBreakpointState() - err := p.selectedGoroutine.thread.(*GdbserverThread).StepInstruction() + p.selectedGoroutine.Thread.(*GdbserverThread).clearBreakpointState() + err := p.selectedGoroutine.Thread.(*GdbserverThread).StepInstruction() if err != nil { return err } - return p.selectedGoroutine.thread.(*GdbserverThread).SetCurrentBreakpoint() + return p.selectedGoroutine.Thread.(*GdbserverThread).SetCurrentBreakpoint() } func (p *GdbserverProcess) SwitchThread(tid int) error { if th, ok := p.threads[tid]; ok { p.currentThread = th - p.selectedGoroutine, _ = GetG(p.CurrentThread()) + p.selectedGoroutine, _ = proc.GetG(p.CurrentThread()) return nil } return fmt.Errorf("thread %d does not exist", tid) } func (p *GdbserverProcess) SwitchGoroutine(gid int) error { - g, err := FindGoroutine(p, gid) + g, err := proc.FindGoroutine(p, gid) if err != nil { return err } @@ -583,8 +584,8 @@ func (p *GdbserverProcess) SwitchGoroutine(gid int) error { // user specified -1 and selectedGoroutine is nil return nil } - if g.thread != nil { - return p.SwitchThread(g.thread.ThreadID()) + if g.Thread != nil { + return p.SwitchThread(g.Thread.ThreadID()) } p.selectedGoroutine = g return nil @@ -605,7 +606,7 @@ func (p *GdbserverProcess) Kill() error { return nil } err := p.conn.kill() - if _, exited := err.(ProcessExitedError); exited { + if _, exited := err.(proc.ProcessExitedError); exited { p.exited = true return nil } @@ -615,7 +616,7 @@ func (p *GdbserverProcess) Kill() error { func (p *GdbserverProcess) Detach(kill bool) error { if kill { if err := p.Kill(); err != nil { - if _, exited := err.(ProcessExitedError); !exited { + if _, exited := err.(proc.ProcessExitedError); !exited { return err } } @@ -632,13 +633,13 @@ func (p *GdbserverProcess) Detach(kill bool) error { return p.bi.Close() } -func (p *GdbserverProcess) Breakpoints() map[uint64]*Breakpoint { +func (p *GdbserverProcess) Breakpoints() map[uint64]*proc.Breakpoint { return p.breakpoints } -func (p *GdbserverProcess) FindBreakpoint(pc uint64) (*Breakpoint, bool) { +func (p *GdbserverProcess) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) { // Check to see if address is past the breakpoint, (i.e. breakpoint was hit). - if bp, ok := p.breakpoints[pc-uint64(p.bi.arch.BreakpointSize())]; ok { + if bp, ok := p.breakpoints[pc-uint64(p.bi.Arch.BreakpointSize())]; ok { return bp, true } // Directly use addr to lookup breakpoint. @@ -648,16 +649,16 @@ func (p *GdbserverProcess) FindBreakpoint(pc uint64) (*Breakpoint, bool) { return nil, false } -func (p *GdbserverProcess) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) { +func (p *GdbserverProcess) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) { if bp, ok := p.breakpoints[addr]; ok { - return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr} + return nil, proc.BreakpointExistsError{bp.File, bp.Line, bp.Addr} } f, l, fn := p.bi.PCToLine(uint64(addr)) if fn == nil { - return nil, InvalidAddressError{address: addr} + return nil, proc.InvalidAddressError{Address: addr} } - newBreakpoint := &Breakpoint{ + newBreakpoint := &proc.Breakpoint{ FunctionName: fn.Name, File: f, Line: l, @@ -667,7 +668,7 @@ func (p *GdbserverProcess) SetBreakpoint(addr uint64, kind BreakpointKind, cond HitCount: map[int]uint64{}, } - if kind != UserBreakpoint { + if kind != proc.UserBreakpoint { p.internalBreakpointIDCounter++ newBreakpoint.ID = p.internalBreakpointIDCounter } else { @@ -682,13 +683,13 @@ func (p *GdbserverProcess) SetBreakpoint(addr uint64, kind BreakpointKind, cond return newBreakpoint, nil } -func (p *GdbserverProcess) ClearBreakpoint(addr uint64) (*Breakpoint, error) { +func (p *GdbserverProcess) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) { if p.exited { - return nil, &ProcessExitedError{Pid: p.conn.pid} + return nil, &proc.ProcessExitedError{Pid: p.conn.pid} } bp := p.breakpoints[addr] if bp == nil { - return nil, NoBreakpointError{addr: addr} + return nil, proc.NoBreakpointError{Addr: addr} } if err := p.conn.clearBreakpoint(addr); err != nil { @@ -849,21 +850,21 @@ func (t *GdbserverThread) ReadMemory(data []byte, addr uintptr) (n int, err erro return len(data), nil } -func (t *GdbserverThread) writeMemory(addr uintptr, data []byte) (written int, err error) { +func (t *GdbserverThread) WriteMemory(addr uintptr, data []byte) (written int, err error) { return t.p.conn.writeMemory(addr, data) } -func (t *GdbserverThread) Location() (*Location, error) { +func (t *GdbserverThread) Location() (*proc.Location, error) { regs, err := t.Registers(false) if err != nil { return nil, err } pc := regs.PC() f, l, fn := t.p.bi.PCToLine(pc) - return &Location{PC: pc, File: f, Line: l, Fn: fn}, nil + return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil } -func (t *GdbserverThread) Breakpoint() (breakpoint *Breakpoint, active bool, condErr error) { +func (t *GdbserverThread) Breakpoint() (breakpoint *proc.Breakpoint, active bool, condErr error) { return t.CurrentBreakpoint, (t.CurrentBreakpoint != nil && t.BreakpointConditionMet), t.BreakpointConditionError } @@ -871,15 +872,15 @@ func (t *GdbserverThread) ThreadID() int { return t.ID } -func (t *GdbserverThread) Registers(floatingPoint bool) (Registers, error) { +func (t *GdbserverThread) Registers(floatingPoint bool) (proc.Registers, error) { return &t.regs, nil } -func (t *GdbserverThread) Arch() Arch { - return t.p.bi.arch +func (t *GdbserverThread) Arch() proc.Arch { + return t.p.bi.Arch } -func (t *GdbserverThread) BinInfo() *BinaryInfo { +func (t *GdbserverThread) BinInfo() *proc.BinaryInfo { return &t.p.bi } @@ -903,16 +904,37 @@ func (t *GdbserverThread) StepInstruction() error { return t.reloadRegisters() } +func (t *GdbserverThread) Blocked() bool { + regs, err := t.Registers(false) + if err != nil { + return false + } + pc := regs.PC() + fn := t.BinInfo().PCToFunc(pc) + if fn == nil { + return false + } + switch fn.Name { + case "runtime.futex", "runtime.usleep", "runtime.clone": + return true + case "runtime.kevent": + return true + case "runtime.mach_semaphore_wait", "runtime.mach_semaphore_timedwait": + return true + } + return false +} + // loadGInstr returns the correct MOV instruction for the current // OS/architecture that can be executed to load the address of G from an // inferior's thread. func (p *GdbserverProcess) loadGInstr() []byte { - switch p.bi.goos { + switch p.bi.GOOS { case "windows": //TODO(aarzilli): implement panic("not implemented") case "linux": - switch p.bi.arch.GStructOffset() { + switch p.bi.Arch.GStructOffset() { case 0xfffffffffffffff8, 0x0: // mov rcx,QWORD PTR fs:0xfffffffffffffff8 return []byte{0x64, 0x48, 0x8B, 0x0C, 0x25, 0xF8, 0xFF, 0xFF, 0xFF} @@ -1005,7 +1027,7 @@ func (t *GdbserverThread) readSomeRegisters(regNames ...string) error { func (t *GdbserverThread) reloadGAtPC() error { movinstr := t.p.loadGInstr() - if gdbserverThreadBlocked(t) { + if t.Blocked() { t.regs.tls = 0 t.regs.gaddr = 0 t.regs.hasgaddr = true @@ -1038,13 +1060,13 @@ func (t *GdbserverThread) reloadGAtPC() error { return err } - _, err = t.writeMemory(uintptr(pc), movinstr) + _, err = t.WriteMemory(uintptr(pc), movinstr) if err != nil { return err } defer func() { - _, err0 := t.writeMemory(uintptr(pc), savedcode) + _, err0 := t.WriteMemory(uintptr(pc), savedcode) if err == nil { err = err0 } @@ -1077,7 +1099,7 @@ func (t *GdbserverThread) reloadGAtPC() error { // a MOV instruction that loads the address of the current G in the RCX // register. func (t *GdbserverThread) reloadGAlloc() error { - if gdbserverThreadBlocked(t) { + if t.Blocked() { t.regs.tls = 0 t.regs.gaddr = 0 t.regs.hasgaddr = true @@ -1118,27 +1140,6 @@ func (t *GdbserverThread) reloadGAlloc() error { return err } -func gdbserverThreadBlocked(t *GdbserverThread) bool { - regs, err := t.Registers(false) - if err != nil { - return false - } - pc := regs.PC() - fn := t.BinInfo().goSymTable.PCToFunc(pc) - if fn == nil { - return false - } - switch fn.Name { - case "runtime.futex", "runtime.usleep", "runtime.clone": - return true - case "runtime.kevent": - return true - case "runtime.mach_semaphore_wait", "runtime.mach_semaphore_timedwait": - return true - } - return false -} - func (t *GdbserverThread) clearBreakpointState() { t.setbp = false t.CurrentBreakpoint = nil @@ -1160,9 +1161,9 @@ func (thread *GdbserverThread) SetCurrentBreakpoint() error { } } thread.CurrentBreakpoint = bp - thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.checkCondition(thread) + thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.CheckCondition(thread) if thread.CurrentBreakpoint != nil && thread.BreakpointConditionMet { - if g, err := GetG(thread); err == nil { + if g, err := proc.GetG(thread); err == nil { thread.CurrentBreakpoint.HitCount[g.ID]++ } thread.CurrentBreakpoint.TotalHitCount++ @@ -1365,10 +1366,10 @@ func (regs *gdbRegisters) Get(n int) (uint64, error) { return regs.byName("r15"), nil } - return 0, UnknownRegisterError + return 0, proc.UnknownRegisterError } -func (regs *gdbRegisters) SetPC(thread IThread, pc uint64) error { +func (regs *gdbRegisters) SetPC(thread proc.IThread, pc uint64) error { regs.setPC(pc) t := thread.(*GdbserverThread) if t.p.gcmdok { @@ -1378,20 +1379,20 @@ func (regs *gdbRegisters) SetPC(thread IThread, pc uint64) error { return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value) } -func (regs *gdbRegisters) Slice() []Register { - r := make([]Register, 0, len(regs.regsInfo)) +func (regs *gdbRegisters) Slice() []proc.Register { + r := make([]proc.Register, 0, len(regs.regsInfo)) for _, reginfo := range regs.regsInfo { switch { case reginfo.Name == "eflags": - r = appendFlagReg(r, reginfo.Name, uint64(binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value)), eflagsDescription, 32) + r = proc.AppendEflagReg(r, reginfo.Name, uint64(binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value))) case reginfo.Name == "mxcsr": - r = appendFlagReg(r, reginfo.Name, uint64(binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value)), mxcsrDescription, 32) + r = proc.AppendMxcsrReg(r, reginfo.Name, uint64(binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value))) case reginfo.Bitsize == 16: - r = appendWordReg(r, reginfo.Name, binary.LittleEndian.Uint16(regs.regs[reginfo.Name].value)) + r = proc.AppendWordReg(r, reginfo.Name, binary.LittleEndian.Uint16(regs.regs[reginfo.Name].value)) case reginfo.Bitsize == 32: - r = appendDwordReg(r, reginfo.Name, binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value)) + r = proc.AppendDwordReg(r, reginfo.Name, binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value)) case reginfo.Bitsize == 64: - r = appendQwordReg(r, reginfo.Name, binary.LittleEndian.Uint64(regs.regs[reginfo.Name].value)) + r = proc.AppendQwordReg(r, reginfo.Name, binary.LittleEndian.Uint64(regs.regs[reginfo.Name].value)) case reginfo.Bitsize == 80: idx := 0 for _, stprefix := range []string{"stmm", "st"} { @@ -1401,10 +1402,10 @@ func (regs *gdbRegisters) Slice() []Register { } } value := regs.regs[reginfo.Name].value - r = appendX87Reg(r, idx, binary.LittleEndian.Uint16(value[8:]), binary.LittleEndian.Uint64(value[:8])) + r = proc.AppendX87Reg(r, idx, binary.LittleEndian.Uint16(value[8:]), binary.LittleEndian.Uint64(value[:8])) case reginfo.Bitsize == 128: - r = appendSSEReg(r, strings.ToUpper(reginfo.Name), regs.regs[reginfo.Name].value) + r = proc.AppendSSEReg(r, strings.ToUpper(reginfo.Name), regs.regs[reginfo.Name].value) case reginfo.Bitsize == 256: if !strings.HasPrefix(strings.ToLower(reginfo.Name), "ymm") { @@ -1413,8 +1414,8 @@ func (regs *gdbRegisters) Slice() []Register { value := regs.regs[reginfo.Name].value xmmName := "x" + reginfo.Name[1:] - r = appendSSEReg(r, strings.ToUpper(xmmName), value[:16]) - r = appendSSEReg(r, strings.ToUpper(reginfo.Name), value[16:]) + r = proc.AppendSSEReg(r, strings.ToUpper(xmmName), value[:16]) + r = proc.AppendSSEReg(r, strings.ToUpper(reginfo.Name), value[16:]) } } return r diff --git a/pkg/proc/gdbserver_conn.go b/pkg/proc/gdbserial/gdbserver_conn.go similarity index 99% rename from pkg/proc/gdbserver_conn.go rename to pkg/proc/gdbserial/gdbserver_conn.go index c1c2a8fa..a7720332 100644 --- a/pkg/proc/gdbserver_conn.go +++ b/pkg/proc/gdbserial/gdbserver_conn.go @@ -1,4 +1,4 @@ -package proc +package gdbserial import ( "bufio" @@ -12,6 +12,8 @@ import ( "strconv" "strings" "time" + + "github.com/derekparker/delve/pkg/proc" ) type gdbConn struct { @@ -630,7 +632,7 @@ func (conn *gdbConn) parseStopPacket(resp []byte, threadID string, tu *threadUpd semicolon = len(resp) } status, _ := strconv.ParseUint(string(resp[1:semicolon]), 16, 8) - return false, stopPacket{}, ProcessExitedError{Pid: conn.pid, Status: int(status)} + return false, stopPacket{}, proc.ProcessExitedError{Pid: conn.pid, Status: int(status)} case 'N': // we were singlestepping the thread and the thread exited diff --git a/pkg/proc/gdbserver_unix.go b/pkg/proc/gdbserial/gdbserver_unix.go similarity index 90% rename from pkg/proc/gdbserver_unix.go rename to pkg/proc/gdbserial/gdbserver_unix.go index b77f7e03..067c2932 100644 --- a/pkg/proc/gdbserver_unix.go +++ b/pkg/proc/gdbserial/gdbserver_unix.go @@ -1,6 +1,6 @@ // +build linux darwin -package proc +package gdbserial import "syscall" diff --git a/pkg/proc/gdbserver_windows.go b/pkg/proc/gdbserial/gdbserver_windows.go similarity index 82% rename from pkg/proc/gdbserver_windows.go rename to pkg/proc/gdbserial/gdbserver_windows.go index 089f4db0..e74a4ed0 100644 --- a/pkg/proc/gdbserver_windows.go +++ b/pkg/proc/gdbserial/gdbserver_windows.go @@ -1,4 +1,4 @@ -package proc +package gdbserial import "syscall" diff --git a/pkg/proc/mem.go b/pkg/proc/mem.go index 7e11c01d..472bb0a7 100644 --- a/pkg/proc/mem.go +++ b/pkg/proc/mem.go @@ -11,15 +11,15 @@ type MemoryReader interface { ReadMemory(buf []byte, addr uintptr) (n int, err error) } -type memoryReadWriter interface { +type MemoryReadWriter interface { MemoryReader - writeMemory(addr uintptr, data []byte) (written int, err error) + WriteMemory(addr uintptr, data []byte) (written int, err error) } type memCache struct { cacheAddr uintptr cache []byte - mem memoryReadWriter + mem MemoryReadWriter } func (m *memCache) contains(addr uintptr, size int) bool { @@ -35,11 +35,11 @@ func (m *memCache) ReadMemory(data []byte, addr uintptr) (n int, err error) { return m.mem.ReadMemory(data, addr) } -func (m *memCache) writeMemory(addr uintptr, data []byte) (written int, err error) { - return m.mem.writeMemory(addr, data) +func (m *memCache) WriteMemory(addr uintptr, data []byte) (written int, err error) { + return m.mem.WriteMemory(addr, data) } -func cacheMemory(mem memoryReadWriter, addr uintptr, size int) memoryReadWriter { +func cacheMemory(mem MemoryReadWriter, addr uintptr, size int) MemoryReadWriter { if !cacheEnabled { return mem } diff --git a/pkg/proc/moduledata.go b/pkg/proc/moduledata.go index 77390336..b7b043e5 100644 --- a/pkg/proc/moduledata.go +++ b/pkg/proc/moduledata.go @@ -11,7 +11,7 @@ type moduleData struct { typemapVar *Variable } -func loadModuleData(bi *BinaryInfo, mem memoryReadWriter) (err error) { +func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) (err error) { bi.loadModuleDataOnce.Do(func() { scope := &EvalScope{0, 0, mem, nil, bi} var md *Variable @@ -56,7 +56,7 @@ func loadModuleData(bi *BinaryInfo, mem memoryReadWriter) (err error) { return } -func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryReadWriter) (*Variable, error) { +func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryReadWriter) (*Variable, error) { // See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go if err := loadModuleData(bi, mem); err != nil { return nil, err @@ -93,7 +93,7 @@ func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryRea return newVariable("", res, rtyp, bi, mem), nil } -func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryReadWriter) (name, tag string, pkgpathoff int32, err error) { +func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) { // See runtime.resolveNameOff in $GOROOT/src/runtime/type.go if err = loadModuleData(bi, mem); err != nil { return "", "", 0, err @@ -118,7 +118,7 @@ func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryRea return loadName(bi, resv.Addr, mem) } -func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem memoryReadWriter) (*Variable, error) { +func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem MemoryReadWriter) (*Variable, error) { scope := &EvalScope{0, 0, mem, nil, bi} reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs") if err != nil { @@ -140,7 +140,7 @@ const ( nameflagHasPkg = 1 << 2 ) -func loadName(bi *BinaryInfo, addr uintptr, mem memoryReadWriter) (name, tag string, pkgpathoff int32, err error) { +func loadName(bi *BinaryInfo, addr uintptr, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) { off := addr namedata := make([]byte, 3) _, err = mem.ReadMemory(namedata, off) diff --git a/pkg/proc/exc.h b/pkg/proc/native/exc.h similarity index 100% rename from pkg/proc/exc.h rename to pkg/proc/native/exc.h diff --git a/pkg/proc/exc_user_darwin.c b/pkg/proc/native/exc_user_darwin.c similarity index 100% rename from pkg/proc/exc_user_darwin.c rename to pkg/proc/native/exc_user_darwin.c diff --git a/pkg/proc/exec_darwin.c b/pkg/proc/native/exec_darwin.c similarity index 100% rename from pkg/proc/exec_darwin.c rename to pkg/proc/native/exec_darwin.c diff --git a/pkg/proc/exec_darwin.h b/pkg/proc/native/exec_darwin.h similarity index 100% rename from pkg/proc/exec_darwin.h rename to pkg/proc/native/exec_darwin.h diff --git a/pkg/proc/mach_exc.defs b/pkg/proc/native/mach_exc.defs similarity index 100% rename from pkg/proc/mach_exc.defs rename to pkg/proc/native/mach_exc.defs diff --git a/pkg/proc/mach_exc.h b/pkg/proc/native/mach_exc.h similarity index 100% rename from pkg/proc/mach_exc.h rename to pkg/proc/native/mach_exc.h diff --git a/pkg/proc/mach_exc_user_darwin.c b/pkg/proc/native/mach_exc_user_darwin.c similarity index 100% rename from pkg/proc/mach_exc_user_darwin.c rename to pkg/proc/native/mach_exc_user_darwin.c diff --git a/pkg/proc/native/proc.go b/pkg/proc/native/proc.go new file mode 100644 index 00000000..5c6b6f32 --- /dev/null +++ b/pkg/proc/native/proc.go @@ -0,0 +1,525 @@ +package native + +import ( + "debug/gosym" + "errors" + "fmt" + "go/ast" + "os" + "runtime" + "sync" + + "github.com/derekparker/delve/pkg/proc" +) + +// Process represents all of the information the debugger +// is holding onto regarding the process we are debugging. +type Process struct { + bi proc.BinaryInfo + pid int // Process Pid + Process *os.Process // Pointer to process struct for the actual process we are debugging + + // Breakpoint table, holds information on breakpoints. + // Maps instruction address to Breakpoint struct. + breakpoints map[uint64]*proc.Breakpoint + + // List of threads mapped as such: pid -> *Thread + threads map[int]*Thread + + // Active thread + currentThread *Thread + + // Goroutine that will be used by default to set breakpoint, eval variables, etc... + // Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread + selectedGoroutine *proc.G + + allGCache []*proc.G + os *OSProcessDetails + breakpointIDCounter int + internalBreakpointIDCounter int + firstStart bool + halt bool + exited bool + ptraceChan chan func() + ptraceDoneChan chan interface{} +} + +// New returns an initialized Process struct. Before returning, +// it will also launch a goroutine in order to handle ptrace(2) +// functions. For more information, see the documentation on +// `handlePtraceFuncs`. +func New(pid int) *Process { + dbp := &Process{ + pid: pid, + threads: make(map[int]*Thread), + breakpoints: make(map[uint64]*proc.Breakpoint), + firstStart: true, + os: new(OSProcessDetails), + ptraceChan: make(chan func()), + ptraceDoneChan: make(chan interface{}), + bi: proc.NewBinaryInfo(runtime.GOOS, runtime.GOARCH), + } + go dbp.handlePtraceFuncs() + return dbp +} + +func (dbp *Process) BinInfo() *proc.BinaryInfo { + return &dbp.bi +} + +// Detach from the process being debugged, optionally killing it. +func (dbp *Process) Detach(kill bool) (err error) { + if dbp.exited { + return nil + } + if dbp.Running() { + if err = dbp.Halt(); err != nil { + return + } + } + if !kill { + // Clean up any breakpoints we've set. + for _, bp := range dbp.breakpoints { + if bp != nil { + _, err := dbp.ClearBreakpoint(bp.Addr) + if err != nil { + return err + } + } + } + } + dbp.execPtraceFunc(func() { + err = dbp.detach(kill) + if err != nil { + return + } + if kill { + err = killProcess(dbp.pid) + } + }) + dbp.bi.Close() + return +} + +// Exited returns whether the debugged +// process has exited. +func (dbp *Process) Exited() bool { + return dbp.exited +} + +// Running returns whether the debugged +// process is currently executing. +func (dbp *Process) Running() bool { + for _, th := range dbp.threads { + if th.running { + return true + } + } + return false +} + +func (dbp *Process) Pid() int { + return dbp.pid +} + +func (dbp *Process) SelectedGoroutine() *proc.G { + return dbp.selectedGoroutine +} + +func (dbp *Process) ThreadList() []proc.IThread { + r := make([]proc.IThread, 0, len(dbp.threads)) + for _, v := range dbp.threads { + r = append(r, v) + } + return r +} + +func (dbp *Process) FindThread(threadID int) (proc.IThread, bool) { + th, ok := dbp.threads[threadID] + return th, ok +} + +func (dbp *Process) CurrentThread() proc.IThread { + return dbp.currentThread +} + +func (dbp *Process) Breakpoints() map[uint64]*proc.Breakpoint { + return dbp.breakpoints +} + +// LoadInformation finds the executable and then uses it +// to parse the following information: +// * Dwarf .debug_frame section +// * Dwarf .debug_line section +// * Go symbol table. +func (dbp *Process) LoadInformation(path string) error { + var wg sync.WaitGroup + + path = findExecutable(path, dbp.pid) + + wg.Add(1) + go dbp.loadProcessInformation(&wg) + dbp.bi.LoadBinaryInfo(path, &wg) + wg.Wait() + + return nil +} + +func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) { + return proc.FindFunctionLocation(dbp.currentThread, dbp.breakpoints, &dbp.bi, funcName, firstLine, lineOffset) +} + +func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) { + return proc.FindFileLocation(dbp.currentThread, dbp.breakpoints, &dbp.bi, fileName, lineno) +} + +func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) { + return proc.FirstPCAfterPrologue(dbp.currentThread, dbp.breakpoints, &dbp.bi, fn, sameline) +} + +// CurrentLocation returns the location of the current thread. +func (dbp *Process) CurrentLocation() (*proc.Location, error) { + return dbp.currentThread.Location() +} + +// RequestManualStop sets the `halt` flag and +// sends SIGSTOP to all threads. +func (dbp *Process) RequestManualStop() error { + if dbp.exited { + return &proc.ProcessExitedError{} + } + dbp.halt = true + return dbp.requestManualStop() +} + +// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide +// break point table. Setting a break point must be thread specific due to +// ptrace actions needing the thread to be in a signal-delivery-stop. +func (dbp *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) { + tid := dbp.currentThread.ID + + if bp, ok := dbp.FindBreakpoint(addr); ok { + return nil, proc.BreakpointExistsError{bp.File, bp.Line, bp.Addr} + } + + f, l, fn := dbp.bi.PCToLine(uint64(addr)) + if fn == nil { + return nil, proc.InvalidAddressError{Address: addr} + } + + newBreakpoint := &proc.Breakpoint{ + FunctionName: fn.Name, + File: f, + Line: l, + Addr: addr, + Kind: kind, + Cond: cond, + HitCount: map[int]uint64{}, + } + + if kind != proc.UserBreakpoint { + dbp.internalBreakpointIDCounter++ + newBreakpoint.ID = dbp.internalBreakpointIDCounter + } else { + dbp.breakpointIDCounter++ + newBreakpoint.ID = dbp.breakpointIDCounter + } + + thread := dbp.threads[tid] + originalData := make([]byte, dbp.bi.Arch.BreakpointSize()) + _, err := thread.ReadMemory(originalData, uintptr(addr)) + if err != nil { + return nil, err + } + if err := dbp.writeSoftwareBreakpoint(thread, addr); err != nil { + return nil, err + } + newBreakpoint.OriginalData = originalData + dbp.breakpoints[addr] = newBreakpoint + + return newBreakpoint, nil +} + +// ClearBreakpoint clears the breakpoint at addr. +func (dbp *Process) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) { + if dbp.exited { + return nil, &proc.ProcessExitedError{} + } + bp, ok := dbp.FindBreakpoint(addr) + if !ok { + return nil, proc.NoBreakpointError{Addr: addr} + } + + if _, err := dbp.currentThread.ClearBreakpoint(bp); err != nil { + return nil, err + } + + delete(dbp.breakpoints, addr) + + return bp, nil +} + +// Status returns the status of the current main thread context. +func (dbp *Process) Status() *WaitStatus { + return dbp.currentThread.Status +} + +func (dbp *Process) ContinueOnce() (proc.IThread, error) { + if dbp.exited { + return nil, &proc.ProcessExitedError{} + } + + if err := dbp.resume(); err != nil { + return nil, err + } + + dbp.allGCache = nil + for _, th := range dbp.threads { + th.clearBreakpointState() + } + + trapthread, err := dbp.trapWait(-1) + if err != nil { + return nil, err + } + if err := dbp.Halt(); err != nil { + return nil, dbp.exitGuard(err) + } + if err := dbp.setCurrentBreakpoints(trapthread); err != nil { + return nil, err + } + return trapthread, err +} + +// StepInstruction will continue the current thread for exactly +// one instruction. This method affects only the thread +// asssociated with the selected goroutine. All other +// threads will remain stopped. +func (dbp *Process) StepInstruction() (err error) { + if dbp.selectedGoroutine == nil { + return errors.New("cannot single step: no selected goroutine") + } + if dbp.selectedGoroutine.Thread == nil { + // Step called on parked goroutine + if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(dbp.selectedGoroutine)); err != nil { + return err + } + return proc.Continue(dbp) + } + dbp.allGCache = nil + if dbp.exited { + return &proc.ProcessExitedError{} + } + dbp.selectedGoroutine.Thread.(*Thread).clearBreakpointState() + err = dbp.selectedGoroutine.Thread.(*Thread).StepInstruction() + if err != nil { + return err + } + return dbp.selectedGoroutine.Thread.(*Thread).SetCurrentBreakpoint() +} + +// SwitchThread changes from current thread to the thread specified by `tid`. +func (dbp *Process) SwitchThread(tid int) error { + if dbp.exited { + return &proc.ProcessExitedError{} + } + if th, ok := dbp.threads[tid]; ok { + dbp.currentThread = th + dbp.selectedGoroutine, _ = proc.GetG(dbp.currentThread) + return nil + } + return fmt.Errorf("thread %d does not exist", tid) +} + +// SwitchGoroutine changes from current thread to the thread +// running the specified goroutine. +func (dbp *Process) SwitchGoroutine(gid int) error { + if dbp.exited { + return &proc.ProcessExitedError{} + } + g, err := proc.FindGoroutine(dbp, gid) + if err != nil { + return err + } + if g == nil { + // user specified -1 and selectedGoroutine is nil + return nil + } + if g.Thread != nil { + return dbp.SwitchThread(g.Thread.ThreadID()) + } + dbp.selectedGoroutine = g + return nil +} + +// Halt stops all threads. +func (dbp *Process) Halt() (err error) { + if dbp.exited { + return &proc.ProcessExitedError{} + } + for _, th := range dbp.threads { + if err := th.Halt(); err != nil { + return err + } + } + return nil +} + +// Registers obtains register values from the +// "current" thread of the traced process. +func (dbp *Process) Registers() (proc.Registers, error) { + return dbp.currentThread.Registers(false) +} + +// PC returns the PC of the current thread. +func (dbp *Process) PC() (uint64, error) { + return dbp.currentThread.PC() +} + +// CurrentBreakpoint returns the breakpoint the current thread +// is stopped at. +func (dbp *Process) CurrentBreakpoint() *proc.Breakpoint { + return dbp.currentThread.CurrentBreakpoint +} + +// FindBreakpointByID finds the breakpoint for the given ID. +func (dbp *Process) FindBreakpointByID(id int) (*proc.Breakpoint, bool) { + for _, bp := range dbp.breakpoints { + if bp.ID == id { + return bp, true + } + } + return nil, false +} + +// FindBreakpoint finds the breakpoint for the given pc. +func (dbp *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) { + // Check to see if address is past the breakpoint, (i.e. breakpoint was hit). + if bp, ok := dbp.breakpoints[pc-uint64(dbp.bi.Arch.BreakpointSize())]; ok { + return bp, true + } + // Directly use addr to lookup breakpoint. + if bp, ok := dbp.breakpoints[pc]; ok { + return bp, true + } + return nil, false +} + +// Returns a new Process struct. +func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, error) { + if attach { + var err error + dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) }) + if err != nil { + return nil, err + } + _, _, err = dbp.wait(dbp.pid, 0) + if err != nil { + return nil, err + } + } + + process, err := os.FindProcess(dbp.pid) + if err != nil { + return nil, err + } + + dbp.Process = process + err = dbp.LoadInformation(path) + if err != nil { + return nil, err + } + + if err := dbp.updateThreadList(); err != nil { + return nil, err + } + + ver, isextld, err := proc.GetGoInformation(dbp) + if err != nil { + return nil, err + } + + dbp.bi.Arch.SetGStructOffset(ver, isextld) + // selectedGoroutine can not be set correctly by the call to updateThreadList + // because without calling SetGStructOffset we can not read the G struct of currentThread + // but without calling updateThreadList we can not examine memory to determine + // the offset of g struct inside TLS + dbp.selectedGoroutine, _ = proc.GetG(dbp.currentThread) + + panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0) + if err == nil { + bp, err := dbp.SetBreakpoint(panicpc, proc.UserBreakpoint, nil) + if err == nil { + bp.Name = "unrecovered-panic" + bp.ID = -1 + dbp.breakpointIDCounter-- + } + } + + return dbp, nil +} + +func (dbp *Process) ClearInternalBreakpoints() error { + for _, bp := range dbp.breakpoints { + if !bp.Internal() { + continue + } + if _, err := dbp.ClearBreakpoint(bp.Addr); err != nil { + return err + } + } + for i := range dbp.threads { + if dbp.threads[i].CurrentBreakpoint != nil && dbp.threads[i].CurrentBreakpoint.Internal() { + dbp.threads[i].CurrentBreakpoint = nil + } + } + return nil +} + +func (dbp *Process) handlePtraceFuncs() { + // We must ensure here that we are running on the same thread during + // while invoking the ptrace(2) syscall. This is due to the fact that ptrace(2) expects + // all commands after PTRACE_ATTACH to come from the same thread. + runtime.LockOSThread() + + for fn := range dbp.ptraceChan { + fn() + dbp.ptraceDoneChan <- nil + } +} + +func (dbp *Process) execPtraceFunc(fn func()) { + dbp.ptraceChan <- fn + <-dbp.ptraceDoneChan +} + +func (dbp *Process) postExit() { + dbp.exited = true + close(dbp.ptraceChan) + close(dbp.ptraceDoneChan) +} + +func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error { + _, err := thread.WriteMemory(uintptr(addr), dbp.bi.Arch.BreakpointInstruction()) + return err +} + +func (dbp *Process) AllGCache() *[]*proc.G { + return &dbp.allGCache +} + +/* + +// EvalPackageVariable will evaluate the package level variable +// specified by 'name'. +func (dbp *Process) EvalPackageVariable(name string, cfg proc.LoadConfig) (*proc.Variable, error) { + scope := &proc.EvalScope{0, 0, dbp.currentThread, nil, dbp.BinInfo()} + + v, err := scope.packageVarAddr(name) + if err != nil { + return nil, err + } + v.loadValue(cfg) + return v, nil +} +*/ diff --git a/pkg/proc/proc_darwin.c b/pkg/proc/native/proc_darwin.c similarity index 100% rename from pkg/proc/proc_darwin.c rename to pkg/proc/native/proc_darwin.c diff --git a/pkg/proc/proc_darwin.go b/pkg/proc/native/proc_darwin.go similarity index 97% rename from pkg/proc/proc_darwin.go rename to pkg/proc/native/proc_darwin.go index 0d0474b3..ef870389 100644 --- a/pkg/proc/proc_darwin.go +++ b/pkg/proc/native/proc_darwin.go @@ -1,4 +1,4 @@ -package proc +package native // #include "proc_darwin.h" // #include "threads_darwin.h" @@ -15,6 +15,8 @@ import ( "unsafe" sys "golang.org/x/sys/unix" + + "github.com/derekparker/delve/pkg/proc" ) // OSProcessDetails holds Darwin specific information. @@ -36,7 +38,7 @@ type OSProcessDetails struct { func Launch(cmd []string, wd string) (*Process, error) { // check that the argument to Launch is an executable file if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 { - return nil, NotExecutableErr + return nil, proc.NotExecutableErr } argv0Go, err := filepath.Abs(cmd[0]) if err != nil { @@ -285,7 +287,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) { return nil, err } dbp.postExit() - return nil, ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()} + return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()} case C.MACH_RCV_INTERRUPTED: if !dbp.halt { @@ -393,7 +395,7 @@ func (dbp *Process) exitGuard(err error) error { _, status, werr := dbp.wait(dbp.pid, sys.WNOHANG) if werr == nil && status.Exited() { dbp.postExit() - return ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()} + return proc.ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()} } return err } diff --git a/pkg/proc/proc_darwin.h b/pkg/proc/native/proc_darwin.h similarity index 100% rename from pkg/proc/proc_darwin.h rename to pkg/proc/native/proc_darwin.h diff --git a/pkg/proc/proc_linux.go b/pkg/proc/native/proc_linux.go similarity index 94% rename from pkg/proc/proc_linux.go rename to pkg/proc/native/proc_linux.go index 574e44d2..eabc6b88 100644 --- a/pkg/proc/proc_linux.go +++ b/pkg/proc/native/proc_linux.go @@ -1,4 +1,4 @@ -package proc +package native import ( "bytes" @@ -16,6 +16,8 @@ import ( "time" sys "golang.org/x/sys/unix" + + "github.com/derekparker/delve/pkg/proc" ) // Process statuses @@ -43,34 +45,34 @@ type OSProcessDetails struct { // to be supplied to that process. `wd` is working directory of the program. func Launch(cmd []string, wd string) (*Process, error) { var ( - proc *exec.Cmd - err error + process *exec.Cmd + err error ) // check that the argument to Launch is an executable file if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 { - return nil, NotExecutableErr + return nil, proc.NotExecutableErr } dbp := New(0) dbp.execPtraceFunc(func() { - proc = exec.Command(cmd[0]) - proc.Args = cmd - proc.Stdout = os.Stdout - proc.Stderr = os.Stderr - proc.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true} + process = exec.Command(cmd[0]) + process.Args = cmd + process.Stdout = os.Stdout + process.Stderr = os.Stderr + process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true} if wd != "" { - proc.Dir = wd + process.Dir = wd } - err = proc.Start() + err = process.Start() }) if err != nil { return nil, err } - dbp.pid = proc.Process.Pid - _, _, err = dbp.wait(proc.Process.Pid, 0) + dbp.pid = process.Process.Pid + _, _, err = dbp.wait(process.Process.Pid, 0) if err != nil { return nil, fmt.Errorf("waiting for target execve failed: %s", err) } - return initializeDebugProcess(dbp, proc.Path, false) + return initializeDebugProcess(dbp, process.Path, false) } // Attach to an existing process with the given PID. @@ -189,7 +191,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) { if status.Exited() { if wpid == dbp.pid { dbp.postExit() - return nil, ProcessExitedError{Pid: wpid, Status: status.ExitStatus()} + return nil, proc.ProcessExitedError{Pid: wpid, Status: status.ExitStatus()} } delete(dbp.threads, wpid) continue @@ -246,7 +248,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) { // TODO(dp) alert user about unexpected signals here. if err := th.resumeWithSig(int(status.StopSignal())); err != nil { if err == sys.ESRCH { - return nil, ProcessExitedError{Pid: dbp.pid} + return nil, proc.ProcessExitedError{Pid: dbp.pid} } return nil, err } diff --git a/pkg/proc/proc_windows.go b/pkg/proc/native/proc_windows.go similarity index 95% rename from pkg/proc/proc_windows.go rename to pkg/proc/native/proc_windows.go index d3b43c7a..11461e96 100644 --- a/pkg/proc/proc_windows.go +++ b/pkg/proc/native/proc_windows.go @@ -1,8 +1,10 @@ -package proc +package native import ( + "debug/pe" "errors" "fmt" + "io" "os" "os/exec" "path/filepath" @@ -11,6 +13,8 @@ import ( "unsafe" sys "golang.org/x/sys/windows" + + "github.com/derekparker/delve/pkg/proc" ) // OSProcessDetails holds Windows specific information. @@ -19,6 +23,19 @@ type OSProcessDetails struct { breakThread int } +func openExecutablePathPE(path string) (*pe.File, io.Closer, error) { + f, err := os.OpenFile(path, 0, os.ModePerm) + if err != nil { + return nil, nil, err + } + peFile, err := pe.NewFile(f) + if err != nil { + f.Close() + return nil, nil, err + } + return peFile, f, nil +} + // Launch creates and begins debugging a new process. func Launch(cmd []string, wd string) (*Process, error) { argv0Go, err := filepath.Abs(cmd[0]) @@ -35,7 +52,7 @@ func Launch(cmd []string, wd string) (*Process, error) { _, closer, err := openExecutablePathPE(argv0Go) if err != nil { - return nil, NotExecutableErr + return nil, proc.NotExecutableErr } closer.Close() @@ -129,7 +146,7 @@ func newDebugProcess(dbp *Process, exepath string) (*Process, error) { } if tid == 0 { dbp.postExit() - return nil, ProcessExitedError{Pid: dbp.pid, Status: exitCode} + return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: exitCode} } // Suspend all threads so that the call to _ContinueDebugEvent will // not resume the target. @@ -330,9 +347,9 @@ func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, e // this exception anymore. atbp := true if thread, found := dbp.threads[tid]; found { - data := make([]byte, dbp.bi.arch.BreakpointSize()) + data := make([]byte, dbp.bi.Arch.BreakpointSize()) if _, err := thread.ReadMemory(data, exception.ExceptionRecord.ExceptionAddress); err == nil { - instr := dbp.bi.arch.BreakpointInstruction() + instr := dbp.bi.Arch.BreakpointInstruction() for i := range instr { if data[i] != instr[i] { atbp = false @@ -388,7 +405,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) { } if tid == 0 { dbp.postExit() - return nil, ProcessExitedError{Pid: dbp.pid, Status: exitCode} + return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: exitCode} } th := dbp.threads[tid] return th, nil diff --git a/pkg/proc/ptrace_darwin.go b/pkg/proc/native/ptrace_darwin.go similarity index 98% rename from pkg/proc/ptrace_darwin.go rename to pkg/proc/native/ptrace_darwin.go index c9ed435c..fa19be83 100644 --- a/pkg/proc/ptrace_darwin.go +++ b/pkg/proc/native/ptrace_darwin.go @@ -1,4 +1,4 @@ -package proc +package native import sys "golang.org/x/sys/unix" diff --git a/pkg/proc/ptrace_linux.go b/pkg/proc/native/ptrace_linux.go similarity index 99% rename from pkg/proc/ptrace_linux.go rename to pkg/proc/native/ptrace_linux.go index 402dbb93..fb7687a4 100644 --- a/pkg/proc/ptrace_linux.go +++ b/pkg/proc/native/ptrace_linux.go @@ -1,4 +1,4 @@ -package proc +package native import ( "encoding/binary" diff --git a/pkg/proc/ptrace_windows.go b/pkg/proc/native/ptrace_windows.go similarity index 92% rename from pkg/proc/ptrace_windows.go rename to pkg/proc/native/ptrace_windows.go index 96e347ba..4d1a7439 100644 --- a/pkg/proc/ptrace_windows.go +++ b/pkg/proc/native/ptrace_windows.go @@ -1,4 +1,4 @@ -package proc +package native import ( "fmt" diff --git a/pkg/proc/registers_darwin_amd64.go b/pkg/proc/native/registers_darwin_amd64.go similarity index 85% rename from pkg/proc/registers_darwin_amd64.go rename to pkg/proc/native/registers_darwin_amd64.go index 91610606..4981b711 100644 --- a/pkg/proc/registers_darwin_amd64.go +++ b/pkg/proc/native/registers_darwin_amd64.go @@ -1,4 +1,4 @@ -package proc +package native // #include "threads_darwin.h" import "C" @@ -8,6 +8,8 @@ import ( "unsafe" "golang.org/x/arch/x86/x86asm" + + "github.com/derekparker/delve/pkg/proc" ) // Regs represents CPU registers on an AMD64 processor. @@ -34,10 +36,10 @@ type Regs struct { fs uint64 gs uint64 gsBase uint64 - fpregs []Register + fpregs []proc.Register } -func (r *Regs) Slice() []Register { +func (r *Regs) Slice() []proc.Register { var regs = []struct { k string v uint64 @@ -65,12 +67,12 @@ func (r *Regs) Slice() []Register { {"Gs", r.gs}, {"Gs_base", r.gsBase}, } - out := make([]Register, 0, len(regs)+len(r.fpregs)) + out := make([]proc.Register, 0, len(regs)+len(r.fpregs)) for _, reg := range regs { if reg.k == "Rflags" { - out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64) + out = proc.AppendEflagReg(out, reg.k, reg.v) } else { - out = appendQwordReg(out, reg.k, reg.v) + out = proc.AppendQwordReg(out, reg.k, reg.v) } } out = append(out, r.fpregs...) @@ -110,7 +112,7 @@ func (r *Regs) GAddr() (uint64, bool) { } // SetPC sets the RIP register to the value specified by `pc`. -func (r *Regs) SetPC(t IThread, pc uint64) error { +func (r *Regs) SetPC(t proc.IThread, pc uint64) error { thread := t.(*Thread) kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc)) if kret != C.KERN_SUCCESS { @@ -273,10 +275,10 @@ func (r *Regs) Get(n int) (uint64, error) { return r.r15, nil } - return 0, UnknownRegisterError + return 0, proc.UnknownRegisterError } -func registers(thread *Thread, floatingPoint bool) (Registers, error) { +func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) { var state C.x86_thread_state64_t var identity C.thread_identifier_info_data_t kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &state) @@ -332,31 +334,31 @@ func registers(thread *Thread, floatingPoint bool) (Registers, error) { return nil, fmt.Errorf("could not get floating point registers") } - regs.fpregs = appendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw)))) - regs.fpregs = appendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw)))) - regs.fpregs = appendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw)) - regs.fpregs = appendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop)) - regs.fpregs = appendQwordReg(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip)) - regs.fpregs = appendQwordReg(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp)) + regs.fpregs = proc.AppendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw)))) + regs.fpregs = proc.AppendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw)))) + regs.fpregs = proc.AppendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw)) + regs.fpregs = proc.AppendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop)) + regs.fpregs = proc.AppendQwordReg(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip)) + regs.fpregs = proc.AppendQwordReg(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp)) for i, st := range []*C.char{&fpstate.__fpu_stmm0.__mmst_reg[0], &fpstate.__fpu_stmm1.__mmst_reg[0], &fpstate.__fpu_stmm2.__mmst_reg[0], &fpstate.__fpu_stmm3.__mmst_reg[0], &fpstate.__fpu_stmm4.__mmst_reg[0], &fpstate.__fpu_stmm5.__mmst_reg[0], &fpstate.__fpu_stmm6.__mmst_reg[0], &fpstate.__fpu_stmm7.__mmst_reg[0]} { stb := C.GoBytes(unsafe.Pointer(st), 10) mantissa := binary.LittleEndian.Uint64(stb[:8]) exponent := binary.LittleEndian.Uint16(stb[8:]) - regs.fpregs = appendX87Reg(regs.fpregs, i, exponent, mantissa) + regs.fpregs = proc.AppendX87Reg(regs.fpregs, i, exponent, mantissa) } - regs.fpregs = appendFlagReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr), mxcsrDescription, 32) - regs.fpregs = appendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask)) + regs.fpregs = proc.AppendMxcsrReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr)) + regs.fpregs = proc.AppendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask)) for i, xmm := range []*C.char{&fpstate.__fpu_xmm0.__xmm_reg[0], &fpstate.__fpu_xmm1.__xmm_reg[0], &fpstate.__fpu_xmm2.__xmm_reg[0], &fpstate.__fpu_xmm3.__xmm_reg[0], &fpstate.__fpu_xmm4.__xmm_reg[0], &fpstate.__fpu_xmm5.__xmm_reg[0], &fpstate.__fpu_xmm6.__xmm_reg[0], &fpstate.__fpu_xmm7.__xmm_reg[0], &fpstate.__fpu_xmm8.__xmm_reg[0], &fpstate.__fpu_xmm9.__xmm_reg[0], &fpstate.__fpu_xmm10.__xmm_reg[0], &fpstate.__fpu_xmm11.__xmm_reg[0], &fpstate.__fpu_xmm12.__xmm_reg[0], &fpstate.__fpu_xmm13.__xmm_reg[0], &fpstate.__fpu_xmm14.__xmm_reg[0], &fpstate.__fpu_xmm15.__xmm_reg[0]} { - regs.fpregs = appendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16)) + regs.fpregs = proc.AppendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16)) } } return regs, nil } -func (thread *Thread) saveRegisters() (Registers, error) { +func (thread *Thread) saveRegisters() (proc.Registers, error) { kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &thread.os.registers) if kret != C.KERN_SUCCESS { return nil, fmt.Errorf("could not save register contents") diff --git a/pkg/proc/registers_linux_amd64.go b/pkg/proc/native/registers_linux_amd64.go similarity index 83% rename from pkg/proc/registers_linux_amd64.go rename to pkg/proc/native/registers_linux_amd64.go index b1081d17..4a3cc48e 100644 --- a/pkg/proc/registers_linux_amd64.go +++ b/pkg/proc/native/registers_linux_amd64.go @@ -1,19 +1,21 @@ -package proc +package native import ( "fmt" "golang.org/x/arch/x86/x86asm" sys "golang.org/x/sys/unix" + + "github.com/derekparker/delve/pkg/proc" ) // Regs is a wrapper for sys.PtraceRegs. type Regs struct { regs *sys.PtraceRegs - fpregs []Register + fpregs []proc.Register } -func (r *Regs) Slice() []Register { +func (r *Regs) Slice() []proc.Register { var regs = []struct { k string v uint64 @@ -46,12 +48,12 @@ func (r *Regs) Slice() []Register { {"Fs", r.regs.Fs}, {"Gs", r.regs.Gs}, } - out := make([]Register, 0, len(regs)+len(r.fpregs)) + out := make([]proc.Register, 0, len(regs)+len(r.fpregs)) for _, reg := range regs { if reg.k == "Eflags" { - out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64) + out = proc.AppendEflagReg(out, reg.k, reg.v) } else { - out = appendQwordReg(out, reg.k, reg.v) + out = proc.AppendQwordReg(out, reg.k, reg.v) } } out = append(out, r.fpregs...) @@ -88,7 +90,7 @@ func (r *Regs) GAddr() (uint64, bool) { } // SetPC sets RIP to the value specified by 'pc'. -func (r *Regs) SetPC(t IThread, pc uint64) (err error) { +func (r *Regs) SetPC(t proc.IThread, pc uint64) (err error) { thread := t.(*Thread) r.regs.SetPC(pc) thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) }) @@ -249,10 +251,10 @@ func (r *Regs) Get(n int) (uint64, error) { return r.regs.R15, nil } - return 0, UnknownRegisterError + return 0, proc.UnknownRegisterError } -func registers(thread *Thread, floatingPoint bool) (Registers, error) { +func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) { var ( regs sys.PtraceRegs err error @@ -302,30 +304,30 @@ const ( _XSAVE_SSE_REGION_LEN = 416 ) -func (thread *Thread) fpRegisters() (regs []Register, err error) { +func (thread *Thread) fpRegisters() (regs []proc.Register, err error) { var fpregs PtraceXsave thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) }) // x87 registers - regs = appendWordReg(regs, "CW", fpregs.Cwd) - regs = appendWordReg(regs, "SW", fpregs.Swd) - regs = appendWordReg(regs, "TW", fpregs.Ftw) - regs = appendWordReg(regs, "FOP", fpregs.Fop) - regs = appendQwordReg(regs, "FIP", fpregs.Rip) - regs = appendQwordReg(regs, "FDP", fpregs.Rdp) + regs = proc.AppendWordReg(regs, "CW", fpregs.Cwd) + regs = proc.AppendWordReg(regs, "SW", fpregs.Swd) + regs = proc.AppendWordReg(regs, "TW", fpregs.Ftw) + regs = proc.AppendWordReg(regs, "FOP", fpregs.Fop) + regs = proc.AppendQwordReg(regs, "FIP", fpregs.Rip) + regs = proc.AppendQwordReg(regs, "FDP", fpregs.Rdp) for i := 0; i < len(fpregs.StSpace); i += 4 { - regs = appendX87Reg(regs, i/4, uint16(fpregs.StSpace[i+2]), uint64(fpregs.StSpace[i+1])<<32|uint64(fpregs.StSpace[i])) + regs = proc.AppendX87Reg(regs, i/4, uint16(fpregs.StSpace[i+2]), uint64(fpregs.StSpace[i+1])<<32|uint64(fpregs.StSpace[i])) } // SSE registers - regs = appendFlagReg(regs, "MXCSR", uint64(fpregs.Mxcsr), mxcsrDescription, 32) - regs = appendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask) + regs = proc.AppendMxcsrReg(regs, "MXCSR", uint64(fpregs.Mxcsr)) + regs = proc.AppendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask) for i := 0; i < len(fpregs.XmmSpace); i += 16 { - regs = appendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), fpregs.XmmSpace[i:i+16]) + regs = proc.AppendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), fpregs.XmmSpace[i:i+16]) if fpregs.AvxState { - regs = appendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), fpregs.YmmSpace[i:i+16]) + regs = proc.AppendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), fpregs.YmmSpace[i:i+16]) } } diff --git a/pkg/proc/registers_windows_amd64.go b/pkg/proc/native/registers_windows_amd64.go similarity index 84% rename from pkg/proc/registers_windows_amd64.go rename to pkg/proc/native/registers_windows_amd64.go index 644ad921..7cb76e58 100644 --- a/pkg/proc/registers_windows_amd64.go +++ b/pkg/proc/native/registers_windows_amd64.go @@ -1,10 +1,12 @@ -package proc +package native import ( "fmt" "unsafe" "golang.org/x/arch/x86/x86asm" + + "github.com/derekparker/delve/pkg/proc" ) // Regs represents CPU registers on an AMD64 processor. @@ -34,7 +36,7 @@ type Regs struct { fltSave *_XMM_SAVE_AREA32 } -func (r *Regs) Slice() []Register { +func (r *Regs) Slice() []proc.Register { var regs = []struct { k string v uint64 @@ -66,31 +68,31 @@ func (r *Regs) Slice() []Register { if r.fltSave != nil { outlen += 6 + 8 + 2 + 16 } - out := make([]Register, 0, outlen) + out := make([]proc.Register, 0, outlen) for _, reg := range regs { if reg.k == "Eflags" { - out = append(out, Register{reg.k, eflagsDescription.Describe(reg.v, 64)}) + out = proc.AppendEflagReg(out, reg.k, reg.v) } else { - out = appendQwordReg(out, reg.k, reg.v) + out = proc.AppendQwordReg(out, reg.k, reg.v) } } if r.fltSave != nil { - out = appendWordReg(out, "CW", r.fltSave.ControlWord) - out = appendWordReg(out, "SW", r.fltSave.StatusWord) - out = appendWordReg(out, "TW", uint16(r.fltSave.TagWord)) - out = appendWordReg(out, "FOP", r.fltSave.ErrorOpcode) - out = appendQwordReg(out, "FIP", uint64(r.fltSave.ErrorSelector)<<32|uint64(r.fltSave.ErrorOffset)) - out = appendQwordReg(out, "FDP", uint64(r.fltSave.DataSelector)<<32|uint64(r.fltSave.DataOffset)) + out = proc.AppendWordReg(out, "CW", r.fltSave.ControlWord) + out = proc.AppendWordReg(out, "SW", r.fltSave.StatusWord) + out = proc.AppendWordReg(out, "TW", uint16(r.fltSave.TagWord)) + out = proc.AppendWordReg(out, "FOP", r.fltSave.ErrorOpcode) + out = proc.AppendQwordReg(out, "FIP", uint64(r.fltSave.ErrorSelector)<<32|uint64(r.fltSave.ErrorOffset)) + out = proc.AppendQwordReg(out, "FDP", uint64(r.fltSave.DataSelector)<<32|uint64(r.fltSave.DataOffset)) for i := range r.fltSave.FloatRegisters { - out = appendX87Reg(out, i, uint16(r.fltSave.FloatRegisters[i].High), r.fltSave.FloatRegisters[i].Low) + out = proc.AppendX87Reg(out, i, uint16(r.fltSave.FloatRegisters[i].High), r.fltSave.FloatRegisters[i].Low) } - out = appendFlagReg(out, "MXCSR", uint64(r.fltSave.MxCsr), mxcsrDescription, 32) - out = appendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask) + out = proc.AppendMxcsrReg(out, "MXCSR", uint64(r.fltSave.MxCsr)) + out = proc.AppendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask) for i := 0; i < len(r.fltSave.XmmRegisters); i += 16 { - out = appendSSEReg(out, fmt.Sprintf("XMM%d", i/16), r.fltSave.XmmRegisters[i:i+16]) + out = proc.AppendSSEReg(out, fmt.Sprintf("XMM%d", i/16), r.fltSave.XmmRegisters[i:i+16]) } } return out @@ -129,7 +131,7 @@ func (r *Regs) GAddr() (uint64, bool) { } // SetPC sets the RIP register to the value specified by `pc`. -func (r *Regs) SetPC(t IThread, pc uint64) error { +func (r *Regs) SetPC(t proc.IThread, pc uint64) error { thread := t.(*Thread) context := newCONTEXT() context.ContextFlags = _CONTEXT_ALL @@ -298,10 +300,10 @@ func (r *Regs) Get(n int) (uint64, error) { return r.r15, nil } - return 0, UnknownRegisterError + return 0, proc.UnknownRegisterError } -func registers(thread *Thread, floatingPoint bool) (Registers, error) { +func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) { context := newCONTEXT() context.ContextFlags = _CONTEXT_ALL @@ -348,7 +350,7 @@ func registers(thread *Thread, floatingPoint bool) (Registers, error) { return regs, nil } -func (thread *Thread) saveRegisters() (Registers, error) { +func (thread *Thread) saveRegisters() (proc.Registers, error) { return nil, fmt.Errorf("not implemented: saveRegisters") } diff --git a/pkg/proc/syscall_windows.go b/pkg/proc/native/syscall_windows.go similarity index 99% rename from pkg/proc/syscall_windows.go rename to pkg/proc/native/syscall_windows.go index 770a7890..03fdc522 100644 --- a/pkg/proc/syscall_windows.go +++ b/pkg/proc/native/syscall_windows.go @@ -1,6 +1,6 @@ //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go -package proc +package native import ( "syscall" diff --git a/pkg/proc/syscall_windows_amd64.go b/pkg/proc/native/syscall_windows_amd64.go similarity index 99% rename from pkg/proc/syscall_windows_amd64.go rename to pkg/proc/native/syscall_windows_amd64.go index 1ce592ec..9740326d 100644 --- a/pkg/proc/syscall_windows_amd64.go +++ b/pkg/proc/native/syscall_windows_amd64.go @@ -1,4 +1,4 @@ -package proc +package native import "unsafe" diff --git a/pkg/proc/native/threads.go b/pkg/proc/native/threads.go new file mode 100644 index 00000000..e4469bcd --- /dev/null +++ b/pkg/proc/native/threads.go @@ -0,0 +1,199 @@ +package native + +import ( + "fmt" + + "github.com/derekparker/delve/pkg/proc" +) + +// Thread represents a single thread in the traced process +// ID represents the thread id or port, Process holds a reference to the +// Process struct that contains info on the process as +// a whole, and Status represents the last result of a `wait` call +// on this thread. +type Thread struct { + ID int // Thread ID or mach port + Status *WaitStatus // Status returned from last wait call + CurrentBreakpoint *proc.Breakpoint // Breakpoint thread is currently stopped at + BreakpointConditionMet bool // Output of evaluating the breakpoint's condition + BreakpointConditionError error // Error evaluating the breakpoint's condition + + dbp *Process + singleStepping bool + running bool + os *OSSpecificDetails +} + +// Continue the execution of this thread. +// +// If we are currently at a breakpoint, we'll clear it +// first and then resume execution. Thread will continue until +// it hits a breakpoint or is signaled. +func (thread *Thread) Continue() error { + pc, err := thread.PC() + if err != nil { + return err + } + // Check whether we are stopped at a breakpoint, and + // if so, single step over it before continuing. + if _, ok := thread.dbp.FindBreakpoint(pc); ok { + if err := thread.StepInstruction(); err != nil { + return err + } + } + return thread.resume() +} + +// StepInstruction steps a single instruction. +// +// Executes exactly one instruction and then returns. +// If the thread is at a breakpoint, we first clear it, +// execute the instruction, and then replace the breakpoint. +// Otherwise we simply execute the next instruction. +func (thread *Thread) StepInstruction() (err error) { + thread.running = true + thread.singleStepping = true + defer func() { + thread.singleStepping = false + thread.running = false + }() + pc, err := thread.PC() + if err != nil { + return err + } + + bp, ok := thread.dbp.FindBreakpoint(pc) + if ok { + // Clear the breakpoint so that we can continue execution. + _, err = thread.ClearBreakpoint(bp) + if err != nil { + return err + } + + // Restore breakpoint now that we have passed it. + defer func() { + err = thread.dbp.writeSoftwareBreakpoint(thread, bp.Addr) + }() + } + + err = thread.singleStep() + if err != nil { + if _, exited := err.(proc.ProcessExitedError); exited { + return err + } + return fmt.Errorf("step failed: %s", err.Error()) + } + return nil +} + +// Location returns the threads location, including the file:line +// of the corresponding source code, the function we're in +// and the current instruction address. +func (thread *Thread) Location() (*proc.Location, error) { + pc, err := thread.PC() + if err != nil { + return nil, err + } + f, l, fn := thread.dbp.bi.PCToLine(pc) + return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil +} + +func (thread *Thread) Arch() proc.Arch { + return thread.dbp.bi.Arch +} + +func (thread *Thread) BinInfo() *proc.BinaryInfo { + return &thread.dbp.bi +} + +// SetPC sets the PC for this thread. +func (thread *Thread) SetPC(pc uint64) error { + regs, err := thread.Registers(false) + if err != nil { + return err + } + return regs.SetPC(thread, pc) +} + +// Stopped returns whether the thread is stopped at +// the operating system level. Actual implementation +// is OS dependant, look in OS thread file. +func (thread *Thread) Stopped() bool { + return thread.stopped() +} + +// Halt stops this thread from executing. Actual +// implementation is OS dependant. Look in OS +// thread file. +func (thread *Thread) Halt() (err error) { + defer func() { + if err == nil { + thread.running = false + } + }() + if thread.Stopped() { + return + } + err = thread.halt() + return +} + +// SetCurrentBreakpoint sets the current breakpoint that this +// thread is stopped at as CurrentBreakpoint on the thread struct. +func (thread *Thread) SetCurrentBreakpoint() error { + thread.CurrentBreakpoint = nil + pc, err := thread.PC() + if err != nil { + return err + } + if bp, ok := thread.dbp.FindBreakpoint(pc); ok { + thread.CurrentBreakpoint = bp + if err = thread.SetPC(bp.Addr); err != nil { + return err + } + thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.CheckCondition(thread) + if thread.CurrentBreakpoint != nil && thread.BreakpointConditionMet { + if g, err := proc.GetG(thread); err == nil { + thread.CurrentBreakpoint.HitCount[g.ID]++ + } + thread.CurrentBreakpoint.TotalHitCount++ + } + } + return nil +} + +func (thread *Thread) clearBreakpointState() { + thread.CurrentBreakpoint = nil + thread.BreakpointConditionMet = false + thread.BreakpointConditionError = nil +} + +func (th *Thread) Breakpoint() (*proc.Breakpoint, bool, error) { + return th.CurrentBreakpoint, (th.CurrentBreakpoint != nil && th.BreakpointConditionMet), th.BreakpointConditionError +} + +func (th *Thread) ThreadID() int { + return th.ID +} + +// ClearBreakpoint clears the specified breakpoint. +func (thread *Thread) ClearBreakpoint(bp *proc.Breakpoint) (*proc.Breakpoint, error) { + if _, err := thread.WriteMemory(uintptr(bp.Addr), bp.OriginalData); err != nil { + return nil, fmt.Errorf("could not clear breakpoint %s", err) + } + return bp, nil +} + +// Registers obtains register values from the debugged process. +func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) { + return registers(t, floatingPoint) +} + +// PC returns the current PC for this thread. +func (t *Thread) PC() (uint64, error) { + regs, err := t.Registers(false) + if err != nil { + return 0, err + } + return regs.PC(), nil +} diff --git a/pkg/proc/threads_darwin.c b/pkg/proc/native/threads_darwin.c similarity index 100% rename from pkg/proc/threads_darwin.c rename to pkg/proc/native/threads_darwin.c diff --git a/pkg/proc/threads_darwin.go b/pkg/proc/native/threads_darwin.go similarity index 95% rename from pkg/proc/threads_darwin.go rename to pkg/proc/native/threads_darwin.go index 33a8c5fb..cc54ebd0 100644 --- a/pkg/proc/threads_darwin.go +++ b/pkg/proc/native/threads_darwin.go @@ -1,4 +1,4 @@ -package proc +package native // #include "threads_darwin.h" // #include "proc_darwin.h" @@ -81,14 +81,14 @@ func (t *Thread) resume() error { return nil } -func threadBlocked(t IThread) bool { +func (t *Thread) Blocked() bool { // TODO(dp) cache the func pc to remove this lookup regs, err := t.Registers(false) if err != nil { return false } pc := regs.PC() - fn := t.BinInfo().goSymTable.PCToFunc(pc) + fn := t.BinInfo().PCToFunc(pc) if fn == nil { return false } @@ -104,7 +104,7 @@ func (t *Thread) stopped() bool { return C.thread_blocked(t.os.threadAct) > C.int(0) } -func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) { +func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) { if len(data) == 0 { return 0, nil } diff --git a/pkg/proc/threads_darwin.h b/pkg/proc/native/threads_darwin.h similarity index 100% rename from pkg/proc/threads_darwin.h rename to pkg/proc/native/threads_darwin.h diff --git a/pkg/proc/threads_linux.go b/pkg/proc/native/threads_linux.go similarity index 88% rename from pkg/proc/threads_linux.go rename to pkg/proc/native/threads_linux.go index 76fb72a2..bea278ee 100644 --- a/pkg/proc/threads_linux.go +++ b/pkg/proc/native/threads_linux.go @@ -1,9 +1,11 @@ -package proc +package native import ( "fmt" sys "golang.org/x/sys/unix" + + "github.com/derekparker/delve/pkg/proc" ) type WaitStatus sys.WaitStatus @@ -59,7 +61,7 @@ func (t *Thread) singleStep() (err error) { if status != nil { rs = status.ExitStatus() } - return ProcessExitedError{Pid: t.dbp.pid, Status: rs} + return proc.ProcessExitedError{Pid: t.dbp.pid, Status: rs} } if wpid == t.ID && status.StopSignal() == sys.SIGTRAP { return nil @@ -67,20 +69,20 @@ func (t *Thread) singleStep() (err error) { } } -func threadBlocked(t IThread) bool { +func (t *Thread) Blocked() bool { regs, err := t.Registers(false) if err != nil { return false } pc := regs.PC() - fn := t.BinInfo().goSymTable.PCToFunc(pc) + fn := t.BinInfo().PCToFunc(pc) if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) { return true } return false } -func (t *Thread) saveRegisters() (Registers, error) { +func (t *Thread) saveRegisters() (proc.Registers, error) { var err error t.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(t.ID, &t.os.registers) }) if err != nil { @@ -94,7 +96,7 @@ func (t *Thread) restoreRegisters() (err error) { return } -func (t *Thread) writeMemory(addr uintptr, data []byte) (written int, err error) { +func (t *Thread) WriteMemory(addr uintptr, data []byte) (written int, err error) { if len(data) == 0 { return } diff --git a/pkg/proc/threads_windows.go b/pkg/proc/native/threads_windows.go similarity index 91% rename from pkg/proc/threads_windows.go rename to pkg/proc/native/threads_windows.go index 2899d71f..ce1bfaad 100644 --- a/pkg/proc/threads_windows.go +++ b/pkg/proc/native/threads_windows.go @@ -1,9 +1,12 @@ -package proc +package native import ( + "errors" "syscall" sys "golang.org/x/sys/windows" + + "github.com/derekparker/delve/pkg/proc" ) // WaitStatus is a synonym for the platform-specific WaitStatus @@ -57,7 +60,7 @@ func (t *Thread) singleStep() error { } if tid == 0 { t.dbp.postExit() - return ProcessExitedError{Pid: t.dbp.pid, Status: exitCode} + return proc.ProcessExitedError{Pid: t.dbp.pid, Status: exitCode} } if t.dbp.os.breakThread == t.ID { @@ -103,7 +106,7 @@ func (t *Thread) resume() error { return err } -func threadBlocked(t IThread) bool { +func (t *Thread) Blocked() bool { // TODO: Probably incorrect - what are the runtime functions that // indicate blocking on Windows? regs, err := t.Registers(false) @@ -111,7 +114,7 @@ func threadBlocked(t IThread) bool { return false } pc := regs.PC() - fn := t.BinInfo().goSymTable.PCToFunc(pc) + fn := t.BinInfo().PCToFunc(pc) if fn == nil { return false } @@ -129,7 +132,7 @@ func (t *Thread) stopped() bool { return true } -func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) { +func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) { var count uintptr err := _WriteProcessMemory(t.dbp.os.hProcess, addr, &data[0], uintptr(len(data)), &count) if err != nil { @@ -138,6 +141,8 @@ func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) { return int(count), nil } +var ErrShortRead = errors.New("short read") + func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) { if len(buf) == 0 { return 0, nil diff --git a/pkg/proc/zsyscall_windows.go b/pkg/proc/native/zsyscall_windows.go similarity index 99% rename from pkg/proc/zsyscall_windows.go rename to pkg/proc/native/zsyscall_windows.go index ee2b33ca..753528b8 100644 --- a/pkg/proc/zsyscall_windows.go +++ b/pkg/proc/native/zsyscall_windows.go @@ -1,6 +1,6 @@ // MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT -package proc +package native import ( "syscall" diff --git a/pkg/proc/proc.go b/pkg/proc/proc.go index 5281c10c..830f1fd9 100644 --- a/pkg/proc/proc.go +++ b/pkg/proc/proc.go @@ -8,48 +8,13 @@ import ( "go/ast" "go/constant" "go/token" - "os" "path/filepath" - "runtime" "strconv" "strings" - "sync" "golang.org/x/debug/dwarf" ) -// Process represents all of the information the debugger -// is holding onto regarding the process we are debugging. -type Process struct { - bi BinaryInfo - pid int // Process Pid - Process *os.Process // Pointer to process struct for the actual process we are debugging - - // Breakpoint table, holds information on breakpoints. - // Maps instruction address to Breakpoint struct. - breakpoints map[uint64]*Breakpoint - - // List of threads mapped as such: pid -> *Thread - threads map[int]*Thread - - // Active thread - currentThread *Thread - - // Goroutine that will be used by default to set breakpoint, eval variables, etc... - // Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread - selectedGoroutine *G - - allGCache []*G - os *OSProcessDetails - breakpointIDCounter int - internalBreakpointIDCounter int - firstStart bool - halt bool - exited bool - ptraceChan chan func() - ptraceDoneChan chan interface{} -} - type functionDebugInfo struct { lowpc, highpc uint64 offset dwarf.Offset @@ -57,25 +22,6 @@ type functionDebugInfo struct { var NotExecutableErr = errors.New("not an executable file") -// New returns an initialized Process struct. Before returning, -// it will also launch a goroutine in order to handle ptrace(2) -// functions. For more information, see the documentation on -// `handlePtraceFuncs`. -func New(pid int) *Process { - dbp := &Process{ - pid: pid, - threads: make(map[int]*Thread), - breakpoints: make(map[uint64]*Breakpoint), - firstStart: true, - os: new(OSProcessDetails), - ptraceChan: make(chan func()), - ptraceDoneChan: make(chan interface{}), - bi: NewBinaryInfo(runtime.GOOS, runtime.GOARCH), - } - go dbp.handlePtraceFuncs() - return dbp -} - // ProcessExitedError indicates that the process has exited and contains both // process id and exit status. type ProcessExitedError struct { @@ -87,115 +33,9 @@ func (pe ProcessExitedError) Error() string { return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status) } -func (dbp *Process) BinInfo() *BinaryInfo { - return &dbp.bi -} - -// Detach from the process being debugged, optionally killing it. -func (dbp *Process) Detach(kill bool) (err error) { - if dbp.exited { - return nil - } - if dbp.Running() { - if err = dbp.Halt(); err != nil { - return - } - } - if !kill { - // Clean up any breakpoints we've set. - for _, bp := range dbp.breakpoints { - if bp != nil { - _, err := dbp.ClearBreakpoint(bp.Addr) - if err != nil { - return err - } - } - } - } - dbp.execPtraceFunc(func() { - err = dbp.detach(kill) - if err != nil { - return - } - if kill { - err = killProcess(dbp.pid) - } - }) - dbp.bi.Close() - return -} - -// Exited returns whether the debugged -// process has exited. -func (dbp *Process) Exited() bool { - return dbp.exited -} - -// Running returns whether the debugged -// process is currently executing. -func (dbp *Process) Running() bool { - for _, th := range dbp.threads { - if th.running { - return true - } - } - return false -} - -func (dbp *Process) Pid() int { - return dbp.pid -} - -func (dbp *Process) SelectedGoroutine() *G { - return dbp.selectedGoroutine -} - -func (dbp *Process) ThreadList() []IThread { - r := make([]IThread, 0, len(dbp.threads)) - for _, v := range dbp.threads { - r = append(r, v) - } - return r -} - -func (dbp *Process) FindThread(threadID int) (IThread, bool) { - th, ok := dbp.threads[threadID] - return th, ok -} - -func (dbp *Process) CurrentThread() IThread { - return dbp.currentThread -} - -func (dbp *Process) Breakpoints() map[uint64]*Breakpoint { - return dbp.breakpoints -} - -// LoadInformation finds the executable and then uses it -// to parse the following information: -// * Dwarf .debug_frame section -// * Dwarf .debug_line section -// * Go symbol table. -func (dbp *Process) LoadInformation(path string) error { - var wg sync.WaitGroup - - path = findExecutable(path, dbp.pid) - - wg.Add(1) - go dbp.loadProcessInformation(&wg) - dbp.bi.LoadBinaryInfo(path, &wg) - wg.Wait() - - return nil -} - -func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) { - return FindFileLocation(dbp.currentThread, dbp.breakpoints, &dbp.bi, fileName, lineno) -} - // FindFileLocation returns the PC for a given file:line. // Assumes that `file` is normailzed to lower case and '/' on Windows. -func FindFileLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fileName string, lineno int) (uint64, error) { +func FindFileLocation(mem MemoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fileName string, lineno int) (uint64, error) { pc, fn, err := bi.goSymTable.LineToPC(fileName, lineno) if err != nil { return 0, err @@ -206,17 +46,13 @@ func FindFileLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, return pc, nil } -func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) { - return FindFunctionLocation(dbp.currentThread, dbp.breakpoints, &dbp.bi, funcName, firstLine, lineOffset) -} - // FindFunctionLocation finds address of a function's line // If firstLine == true is passed FindFunctionLocation will attempt to find the first line of the function // If lineOffset is passed FindFunctionLocation will return the address of that line // Pass lineOffset == 0 and firstLine == false if you want the address for the function's entry point // Note that setting breakpoints at that address will cause surprising behavior: // https://github.com/derekparker/delve/issues/170 -func FindFunctionLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, funcName string, firstLine bool, lineOffset int) (uint64, error) { +func FindFunctionLocation(mem MemoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, funcName string, firstLine bool, lineOffset int) (uint64, error) { origfn := bi.goSymTable.LookupFunc(funcName) if origfn == nil { return 0, fmt.Errorf("Could not find function %s\n", funcName) @@ -233,93 +69,6 @@ func FindFunctionLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoi return origfn.Entry, nil } -// CurrentLocation returns the location of the current thread. -func (dbp *Process) CurrentLocation() (*Location, error) { - return dbp.currentThread.Location() -} - -// RequestManualStop sets the `halt` flag and -// sends SIGSTOP to all threads. -func (dbp *Process) RequestManualStop() error { - if dbp.exited { - return &ProcessExitedError{} - } - dbp.halt = true - return dbp.requestManualStop() -} - -// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide -// break point table. Setting a break point must be thread specific due to -// ptrace actions needing the thread to be in a signal-delivery-stop. -func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) { - tid := dbp.currentThread.ID - - if bp, ok := dbp.FindBreakpoint(addr); ok { - return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr} - } - - f, l, fn := dbp.bi.goSymTable.PCToLine(uint64(addr)) - if fn == nil { - return nil, InvalidAddressError{address: addr} - } - - newBreakpoint := &Breakpoint{ - FunctionName: fn.Name, - File: f, - Line: l, - Addr: addr, - Kind: kind, - Cond: cond, - HitCount: map[int]uint64{}, - } - - if kind != UserBreakpoint { - dbp.internalBreakpointIDCounter++ - newBreakpoint.ID = dbp.internalBreakpointIDCounter - } else { - dbp.breakpointIDCounter++ - newBreakpoint.ID = dbp.breakpointIDCounter - } - - thread := dbp.threads[tid] - originalData := make([]byte, dbp.bi.arch.BreakpointSize()) - _, err := thread.ReadMemory(originalData, uintptr(addr)) - if err != nil { - return nil, err - } - if err := dbp.writeSoftwareBreakpoint(thread, addr); err != nil { - return nil, err - } - newBreakpoint.OriginalData = originalData - dbp.breakpoints[addr] = newBreakpoint - - return newBreakpoint, nil -} - -// ClearBreakpoint clears the breakpoint at addr. -func (dbp *Process) ClearBreakpoint(addr uint64) (*Breakpoint, error) { - if dbp.exited { - return nil, &ProcessExitedError{} - } - bp, ok := dbp.FindBreakpoint(addr) - if !ok { - return nil, NoBreakpointError{addr: addr} - } - - if _, err := dbp.currentThread.ClearBreakpoint(bp); err != nil { - return nil, err - } - - delete(dbp.breakpoints, addr) - - return bp, nil -} - -// Status returns the status of the current main thread context. -func (dbp *Process) Status() *WaitStatus { - return dbp.currentThread.Status -} - // Next continues execution until the next source line. func Next(dbp Continuable) (err error) { if dbp.Exited() { @@ -343,33 +92,6 @@ func Next(dbp Continuable) (err error) { return Continue(dbp) } -func (dbp *Process) ContinueOnce() (IThread, error) { - if dbp.exited { - return nil, &ProcessExitedError{} - } - - if err := dbp.resume(); err != nil { - return nil, err - } - - dbp.allGCache = nil - for _, th := range dbp.threads { - th.clearBreakpointState() - } - - trapthread, err := dbp.trapWait(-1) - if err != nil { - return nil, err - } - if err := dbp.Halt(); err != nil { - return nil, dbp.exitGuard(err) - } - if err := dbp.setCurrentBreakpoints(trapthread); err != nil { - return nil, err - } - return trapthread, err -} - // Continuable is the subinterface of target.Interface used to implement // Continue/Next/etc. type Continuable interface { @@ -442,7 +164,7 @@ func Continue(dbp Continuable) error { // here we either set a breakpoint into the destination of the CALL // instruction or we determined that the called function is hidden, // either way we need to resume execution - if err = setStepIntoBreakpoint(dbp, text, sameGoroutineCondition(dbp.SelectedGoroutine())); err != nil { + if err = setStepIntoBreakpoint(dbp, text, SameGoroutineCondition(dbp.SelectedGoroutine())); err != nil { return err } } else { @@ -528,8 +250,9 @@ func Step(dbp Continuable) (err error) { return Continue(dbp) } -// Returns an expression that evaluates to true when the current goroutine is g -func sameGoroutineCondition(g *G) ast.Expr { +// SameGoroutineCondition returns an expression that evaluates to true when +// the current goroutine is g. +func SameGoroutineCondition(g *G) ast.Expr { if g == nil { return nil } @@ -546,39 +269,12 @@ func sameGoroutineCondition(g *G) ast.Expr { } } -// StepInstruction will continue the current thread for exactly -// one instruction. This method affects only the thread -// asssociated with the selected goroutine. All other -// threads will remain stopped. -func (dbp *Process) StepInstruction() (err error) { - if dbp.selectedGoroutine == nil { - return errors.New("cannot single step: no selected goroutine") - } - if dbp.selectedGoroutine.thread == nil { - // Step called on parked goroutine - if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, NextBreakpoint, sameGoroutineCondition(dbp.selectedGoroutine)); err != nil { - return err - } - return Continue(dbp) - } - dbp.allGCache = nil - if dbp.exited { - return &ProcessExitedError{} - } - dbp.selectedGoroutine.thread.(*Thread).clearBreakpointState() - err = dbp.selectedGoroutine.thread.(*Thread).StepInstruction() - if err != nil { - return err - } - return dbp.selectedGoroutine.thread.(*Thread).SetCurrentBreakpoint() -} - // StepOut will continue until the current goroutine exits the // function currently being executed or a deferred function is executed func StepOut(dbp Continuable) error { selg := dbp.SelectedGoroutine() curthread := dbp.CurrentThread() - cond := sameGoroutineCondition(selg) + cond := SameGoroutineCondition(selg) topframe, err := topframe(selg, curthread) if err != nil { @@ -631,50 +327,12 @@ func StepOut(dbp Continuable) error { return Continue(dbp) } -// SwitchThread changes from current thread to the thread specified by `tid`. -func (dbp *Process) SwitchThread(tid int) error { - if dbp.exited { - return &ProcessExitedError{} - } - if th, ok := dbp.threads[tid]; ok { - dbp.currentThread = th - dbp.selectedGoroutine, _ = GetG(dbp.currentThread) - return nil - } - return fmt.Errorf("thread %d does not exist", tid) -} - -// SwitchGoroutine changes from current thread to the thread -// running the specified goroutine. -func (dbp *Process) SwitchGoroutine(gid int) error { - if dbp.exited { - return &ProcessExitedError{} - } - g, err := FindGoroutine(dbp, gid) - if err != nil { - return err - } - if g == nil { - // user specified -1 and selectedGoroutine is nil - return nil - } - if g.thread != nil { - return dbp.SwitchThread(g.thread.ThreadID()) - } - dbp.selectedGoroutine = g - return nil -} - // If the argument of GoroutinesInfo implements AllGCache GoroutinesInfo // will use the pointer returned by AllGCache as a cache. type AllGCache interface { AllGCache() *[]*G } -func (dbp *Process) AllGCache() *[]*G { - return &dbp.allGCache -} - // GoroutinesInfo returns an array of G structures representing the information // Delve cares about from the internal runtime G structure. func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) { @@ -695,7 +353,7 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) { threads := dbp.ThreadList() for _, th := range threads { - if threadBlocked(th) { + if th.Blocked() { continue } g, _ := GetG(th) @@ -724,12 +382,12 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) { return nil, err } } - faddr := make([]byte, dbp.BinInfo().arch.PtrSize()) + faddr := make([]byte, dbp.BinInfo().Arch.PtrSize()) _, err = dbp.CurrentThread().ReadMemory(faddr, uintptr(allgentryaddr)) allgptr := binary.LittleEndian.Uint64(faddr) for i := uint64(0); i < allglen; i++ { - gvar, err := newGVariable(dbp.CurrentThread(), uintptr(allgptr+(i*uint64(dbp.BinInfo().arch.PtrSize()))), true) + gvar, err := newGVariable(dbp.CurrentThread(), uintptr(allgptr+(i*uint64(dbp.BinInfo().Arch.PtrSize()))), true) if err != nil { return nil, err } @@ -742,7 +400,7 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) { if err != nil { return nil, err } - g.thread = thread + g.Thread = thread // Prefer actual thread location information. g.CurrentLoc = *loc } @@ -758,153 +416,8 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) { return allg, nil } -func (g *G) Thread() IThread { - return g.thread -} - -// Halt stops all threads. -func (dbp *Process) Halt() (err error) { - if dbp.exited { - return &ProcessExitedError{} - } - for _, th := range dbp.threads { - if err := th.Halt(); err != nil { - return err - } - } - return nil -} - -// Registers obtains register values from the -// "current" thread of the traced process. -func (dbp *Process) Registers() (Registers, error) { - return dbp.currentThread.Registers(false) -} - -// PC returns the PC of the current thread. -func (dbp *Process) PC() (uint64, error) { - return dbp.currentThread.PC() -} - -// CurrentBreakpoint returns the breakpoint the current thread -// is stopped at. -func (dbp *Process) CurrentBreakpoint() *Breakpoint { - return dbp.currentThread.CurrentBreakpoint -} - -// FindBreakpointByID finds the breakpoint for the given ID. -func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) { - for _, bp := range dbp.breakpoints { - if bp.ID == id { - return bp, true - } - } - return nil, false -} - -// FindBreakpoint finds the breakpoint for the given pc. -func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) { - // Check to see if address is past the breakpoint, (i.e. breakpoint was hit). - if bp, ok := dbp.breakpoints[pc-uint64(dbp.bi.arch.BreakpointSize())]; ok { - return bp, true - } - // Directly use addr to lookup breakpoint. - if bp, ok := dbp.breakpoints[pc]; ok { - return bp, true - } - return nil, false -} - -// Returns a new Process struct. -func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, error) { - if attach { - var err error - dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) }) - if err != nil { - return nil, err - } - _, _, err = dbp.wait(dbp.pid, 0) - if err != nil { - return nil, err - } - } - - proc, err := os.FindProcess(dbp.pid) - if err != nil { - return nil, err - } - - dbp.Process = proc - err = dbp.LoadInformation(path) - if err != nil { - return nil, err - } - - if err := dbp.updateThreadList(); err != nil { - return nil, err - } - - scope := &EvalScope{0, 0, dbp.currentThread, nil, dbp.BinInfo()} - ver, isextld, err := scope.getGoInformation() - if err != nil { - return nil, err - } - - dbp.bi.arch.SetGStructOffset(ver, isextld) - // selectedGoroutine can not be set correctly by the call to updateThreadList - // because without calling SetGStructOffset we can not read the G struct of currentThread - // but without calling updateThreadList we can not examine memory to determine - // the offset of g struct inside TLS - dbp.selectedGoroutine, _ = GetG(dbp.currentThread) - - panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0) - if err == nil { - bp, err := dbp.SetBreakpoint(panicpc, UserBreakpoint, nil) - if err == nil { - bp.Name = "unrecovered-panic" - bp.ID = -1 - dbp.breakpointIDCounter-- - } - } - - return dbp, nil -} - -func (dbp *Process) ClearInternalBreakpoints() error { - for _, bp := range dbp.breakpoints { - if !bp.Internal() { - continue - } - if _, err := dbp.ClearBreakpoint(bp.Addr); err != nil { - return err - } - } - for i := range dbp.threads { - if dbp.threads[i].CurrentBreakpoint != nil && dbp.threads[i].CurrentBreakpoint.Internal() { - dbp.threads[i].CurrentBreakpoint = nil - } - } - return nil -} - -func (dbp *Process) handlePtraceFuncs() { - // We must ensure here that we are running on the same thread during - // while invoking the ptrace(2) syscall. This is due to the fact that ptrace(2) expects - // all commands after PTRACE_ATTACH to come from the same thread. - runtime.LockOSThread() - - for fn := range dbp.ptraceChan { - fn() - dbp.ptraceDoneChan <- nil - } -} - -func (dbp *Process) execPtraceFunc(fn func()) { - dbp.ptraceChan <- fn - <-dbp.ptraceDoneChan -} - -func (scope *EvalScope) getGoInformation() (ver GoVersion, isextld bool, err error) { +func GetGoInformation(p Continuable) (ver GoVersion, isextld bool, err error) { + scope := &EvalScope{0, 0, p.CurrentThread(), nil, p.BinInfo()} vv, err := scope.packageVarAddr("runtime.buildVersion") if err != nil { return ver, false, fmt.Errorf("Could not determine version number: %v", err) @@ -921,7 +434,7 @@ func (scope *EvalScope) getGoInformation() (ver GoVersion, isextld bool, err err return } - rdr := scope.bi.DwarfReader() + rdr := scope.BinInfo.DwarfReader() rdr.Seek(0) for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() { if err != nil { @@ -976,11 +489,11 @@ func ConvertEvalScope(dbp EvalScopeConvertible, gid, frame int) (*EvalScope, err return ThreadScope(ct) } - var thread memoryReadWriter - if g.thread == nil { + var thread MemoryReadWriter + if g.Thread == nil { thread = ct } else { - thread = g.thread + thread = g.Thread } locs, err := g.Stacktrace(frame) @@ -1001,9 +514,3 @@ func ConvertEvalScope(dbp EvalScopeConvertible, gid, frame int) (*EvalScope, err func FrameToScope(p EvalScopeConvertible, frame Stackframe) *EvalScope { return &EvalScope{frame.Current.PC, frame.CFA, p.CurrentThread(), nil, p.BinInfo()} } - -func (dbp *Process) postExit() { - dbp.exited = true - close(dbp.ptraceChan) - close(dbp.ptraceDoneChan) -} diff --git a/pkg/proc/proc_general_test.go b/pkg/proc/proc_general_test.go new file mode 100644 index 00000000..56fa8a04 --- /dev/null +++ b/pkg/proc/proc_general_test.go @@ -0,0 +1,14 @@ +package proc + +import ( + "testing" +) + +func TestIssue554(t *testing.T) { + // unsigned integer overflow in proc.(*memCache).contains was + // causing it to always return true for address 0xffffffffffffffff + mem := memCache{0x20, make([]byte, 100), nil} + if mem.contains(0xffffffffffffffff, 40) { + t.Fatalf("should be false") + } +} diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index b067f205..999d41dd 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -1,8 +1,7 @@ -package proc +package proc_test import ( "bytes" - "debug/gosym" "flag" "fmt" "go/ast" @@ -20,10 +19,14 @@ import ( "testing" "time" + "github.com/derekparker/delve/pkg/proc" + "github.com/derekparker/delve/pkg/proc/gdbserial" + "github.com/derekparker/delve/pkg/proc/native" protest "github.com/derekparker/delve/pkg/proc/test" + "github.com/derekparker/delve/pkg/target" ) -var normalLoadConfig = LoadConfig{true, 1, 64, 64, -1} +var normalLoadConfig = proc.LoadConfig{true, 1, 64, 64, -1} var testBackend string func init() { @@ -43,48 +46,15 @@ func TestMain(m *testing.M) { os.Exit(protest.RunTestsWithFixtures(m)) } -// copy of target.Interface (plus LineToPC) -type IProcess interface { - Pid() int - Exited() bool - Running() bool - - BinInfo() *BinaryInfo - - FindThread(threadID int) (IThread, bool) - ThreadList() []IThread - CurrentThread() IThread - - SelectedGoroutine() *G - - ContinueOnce() (trapthread IThread, err error) - StepInstruction() error - SwitchThread(int) error - SwitchGoroutine(int) error - RequestManualStop() error - Halt() error - Kill() error - Detach(bool) error - - Breakpoints() map[uint64]*Breakpoint - SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) - ClearBreakpoint(addr uint64) (*Breakpoint, error) - ClearInternalBreakpoints() error - - FindFileLocation(fileName string, lineNumber int) (uint64, error) - FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) - FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) -} - -func withTestProcess(name string, t testing.TB, fn func(p IProcess, fixture protest.Fixture)) { +func withTestProcess(name string, t testing.TB, fn func(p target.Interface, fixture protest.Fixture)) { fixture := protest.BuildFixture(name) - var p IProcess + var p target.Interface var err error switch testBackend { case "native": - p, err = Launch([]string{fixture.Path}, ".") + p, err = native.Launch([]string{fixture.Path}, ".") case "lldb": - p, err = LLDBLaunch([]string{fixture.Path}, ".") + p, err = gdbserial.LLDBLaunch([]string{fixture.Path}, ".") default: t.Fatalf("unknown backend %q", testBackend) } @@ -100,16 +70,16 @@ func withTestProcess(name string, t testing.TB, fn func(p IProcess, fixture prot fn(p, fixture) } -func withTestProcessArgs(name string, t testing.TB, wd string, fn func(p IProcess, fixture protest.Fixture), args []string) { +func withTestProcessArgs(name string, t testing.TB, wd string, fn func(p target.Interface, fixture protest.Fixture), args []string) { fixture := protest.BuildFixture(name) - var p IProcess + var p target.Interface var err error switch testBackend { case "native": - p, err = Launch(append([]string{fixture.Path}, args...), wd) + p, err = native.Launch(append([]string{fixture.Path}, args...), wd) case "lldb": - p, err = LLDBLaunch(append([]string{fixture.Path}, args...), wd) + p, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd) default: t.Fatal("unknown backend") } @@ -125,7 +95,7 @@ func withTestProcessArgs(name string, t testing.TB, wd string, fn func(p IProces fn(p, fixture) } -func getRegisters(p IProcess, t *testing.T) Registers { +func getRegisters(p target.Interface, t *testing.T) proc.Registers { regs, err := p.CurrentThread().Registers(false) if err != nil { t.Fatal("Registers():", err) @@ -134,7 +104,7 @@ func getRegisters(p IProcess, t *testing.T) Registers { return regs } -func dataAtAddr(thread memoryReadWriter, addr uint64) ([]byte, error) { +func dataAtAddr(thread proc.MemoryReadWriter, addr uint64) ([]byte, error) { data := make([]byte, 1) _, err := thread.ReadMemory(data, uintptr(addr)) return data, err @@ -148,7 +118,7 @@ func assertNoError(err error, t testing.TB, s string) { } } -func currentPC(p IProcess, t *testing.T) uint64 { +func currentPC(p target.Interface, t *testing.T) uint64 { regs, err := p.CurrentThread().Registers(false) if err != nil { t.Fatal(err) @@ -157,16 +127,16 @@ func currentPC(p IProcess, t *testing.T) uint64 { return regs.PC() } -func currentLineNumber(p IProcess, t *testing.T) (string, int) { +func currentLineNumber(p target.Interface, t *testing.T) (string, int) { pc := currentPC(p, t) - f, l, _ := p.BinInfo().goSymTable.PCToLine(pc) + f, l, _ := p.BinInfo().PCToLine(pc) return f, l } func TestExit(t *testing.T) { - withTestProcess("continuetestprog", t, func(p IProcess, fixture protest.Fixture) { - err := Continue(p) - pe, ok := err.(ProcessExitedError) + withTestProcess("continuetestprog", t, func(p target.Interface, fixture protest.Fixture) { + err := proc.Continue(p) + pe, ok := err.(proc.ProcessExitedError) if !ok { t.Fatalf("Continue() returned unexpected error type %s", err) } @@ -180,12 +150,12 @@ func TestExit(t *testing.T) { } func TestExitAfterContinue(t *testing.T) { - withTestProcess("continuetestprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("continuetestprog", t, func(p target.Interface, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.sayhi") assertNoError(err, t, "setFunctionBreakpoint()") - assertNoError(Continue(p), t, "First Continue()") - err = Continue(p) - pe, ok := err.(ProcessExitedError) + assertNoError(proc.Continue(p), t, "First Continue()") + err = proc.Continue(p) + pe, ok := err.(proc.ProcessExitedError) if !ok { t.Fatalf("Continue() returned unexpected error type %s", pe) } @@ -198,20 +168,20 @@ func TestExitAfterContinue(t *testing.T) { }) } -func setFunctionBreakpoint(p IProcess, fname string) (*Breakpoint, error) { +func setFunctionBreakpoint(p target.Interface, fname string) (*proc.Breakpoint, error) { addr, err := p.FindFunctionLocation(fname, true, 0) if err != nil { return nil, err } - return p.SetBreakpoint(addr, UserBreakpoint, nil) + return p.SetBreakpoint(addr, proc.UserBreakpoint, nil) } -func setFileBreakpoint(p IProcess, t *testing.T, fixture protest.Fixture, lineno int) *Breakpoint { +func setFileBreakpoint(p target.Interface, t *testing.T, fixture protest.Fixture, lineno int) *proc.Breakpoint { addr, err := p.FindFileLocation(fixture.Source, lineno) if err != nil { t.Fatalf("FindFileLocation: %v", err) } - bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil) + bp, err := p.SetBreakpoint(addr, proc.UserBreakpoint, nil) if err != nil { t.Fatalf("SetBreakpoint: %v", err) } @@ -220,18 +190,15 @@ func setFileBreakpoint(p IProcess, t *testing.T, fixture protest.Fixture, lineno func TestHalt(t *testing.T) { stopChan := make(chan interface{}) - withTestProcess("loopprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("loopprog", t, func(p target.Interface, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.loop") assertNoError(err, t, "SetBreakpoint") - assertNoError(Continue(p), t, "Continue") + assertNoError(proc.Continue(p), t, "Continue") if p.Running() { t.Fatal("process still running") } - if p, ok := p.(*Process); ok { - for _, th := range p.threads { - if th.running != false { - t.Fatal("expected running = false for thread", th.ID) - } + if p, ok := p.(*native.Process); ok { + for _, th := range p.ThreadList() { _, err := th.Registers(false) assertNoError(err, t, "Registers") } @@ -248,18 +215,17 @@ func TestHalt(t *testing.T) { } } }() - assertNoError(Continue(p), t, "Continue") + assertNoError(proc.Continue(p), t, "Continue") <-stopChan // Loop through threads and make sure they are all // actually stopped, err will not be nil if the process // is still running. - if p, ok := p.(*Process); ok { - for _, th := range p.threads { - if !th.Stopped() { - t.Fatal("expected thread to be stopped, but was not") - } - if th.running != false { - t.Fatal("expected running = false for thread", th.ID) + if p, ok := p.(*native.Process); ok { + for _, th := range p.ThreadList() { + if th, ok := th.(*native.Thread); ok { + if !th.Stopped() { + t.Fatal("expected thread to be stopped, but was not") + } } _, err := th.Registers(false) assertNoError(err, t, "Registers") @@ -269,13 +235,13 @@ func TestHalt(t *testing.T) { } func TestStep(t *testing.T) { - withTestProcess("testprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testprog", t, func(p target.Interface, fixture protest.Fixture) { helloworldaddr, err := p.FindFunctionLocation("main.helloworld", false, 0) assertNoError(err, t, "FindFunctionLocation") - _, err = p.SetBreakpoint(helloworldaddr, UserBreakpoint, nil) + _, err = p.SetBreakpoint(helloworldaddr, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") regs := getRegisters(p, t) rip := regs.PC() @@ -291,13 +257,13 @@ func TestStep(t *testing.T) { } func TestBreakpoint(t *testing.T) { - withTestProcess("testprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testprog", t, func(p target.Interface, fixture protest.Fixture) { helloworldaddr, err := p.FindFunctionLocation("main.helloworld", false, 0) assertNoError(err, t, "FindFunctionLocation") - bp, err := p.SetBreakpoint(helloworldaddr, UserBreakpoint, nil) + bp, err := p.SetBreakpoint(helloworldaddr, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") regs, err := p.CurrentThread().Registers(false) assertNoError(err, t, "Registers") @@ -308,27 +274,27 @@ func TestBreakpoint(t *testing.T) { } if pc-1 != bp.Addr && pc != bp.Addr { - f, l, _ := p.BinInfo().goSymTable.PCToLine(pc) + f, l, _ := p.BinInfo().PCToLine(pc) t.Fatalf("Break not respected:\nPC:%#v %s:%d\nFN:%#v \n", pc, f, l, bp.Addr) } }) } func TestBreakpointInSeperateGoRoutine(t *testing.T) { - withTestProcess("testthreads", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testthreads", t, func(p target.Interface, fixture protest.Fixture) { fnentry, err := p.FindFunctionLocation("main.anotherthread", false, 0) assertNoError(err, t, "FindFunctionLocation") - _, err = p.SetBreakpoint(fnentry, UserBreakpoint, nil) + _, err = p.SetBreakpoint(fnentry, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint") - assertNoError(Continue(p), t, "Continue") + assertNoError(proc.Continue(p), t, "Continue") regs, err := p.CurrentThread().Registers(false) assertNoError(err, t, "Registers") pc := regs.PC() - f, l, _ := p.BinInfo().goSymTable.PCToLine(pc) + f, l, _ := p.BinInfo().PCToLine(pc) if f != "testthreads.go" && l != 8 { t.Fatal("Program did not hit breakpoint") } @@ -336,8 +302,8 @@ func TestBreakpointInSeperateGoRoutine(t *testing.T) { } func TestBreakpointWithNonExistantFunction(t *testing.T) { - withTestProcess("testprog", t, func(p IProcess, fixture protest.Fixture) { - _, err := p.SetBreakpoint(0, UserBreakpoint, nil) + withTestProcess("testprog", t, func(p target.Interface, fixture protest.Fixture) { + _, err := p.SetBreakpoint(0, proc.UserBreakpoint, nil) if err == nil { t.Fatal("Should not be able to break at non existant function") } @@ -345,10 +311,10 @@ func TestBreakpointWithNonExistantFunction(t *testing.T) { } func TestClearBreakpointBreakpoint(t *testing.T) { - withTestProcess("testprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testprog", t, func(p target.Interface, fixture protest.Fixture) { fnentry, err := p.FindFunctionLocation("main.sleepytime", false, 0) assertNoError(err, t, "FindFunctionLocation") - bp, err := p.SetBreakpoint(fnentry, UserBreakpoint, nil) + bp, err := p.SetBreakpoint(fnentry, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") bp, err = p.ClearBreakpoint(fnentry) @@ -372,7 +338,7 @@ type nextTest struct { begin, end int } -func countBreakpoints(p IProcess) int { +func countBreakpoints(p target.Interface) int { bpcount := 0 for _, bp := range p.Breakpoints() { if bp.ID >= 0 { @@ -390,8 +356,8 @@ const ( ) func testseq(program string, contFunc contFunc, testcases []nextTest, initialLocation string, t *testing.T) { - withTestProcess(program, t, func(p IProcess, fixture protest.Fixture) { - var bp *Breakpoint + withTestProcess(program, t, func(p target.Interface, fixture protest.Fixture) { + var bp *proc.Breakpoint var err error if initialLocation != "" { bp, err = setFunctionBreakpoint(p, initialLocation) @@ -399,10 +365,10 @@ func testseq(program string, contFunc contFunc, testcases []nextTest, initialLoc var pc uint64 pc, err = p.FindFileLocation(fixture.Source, testcases[0].begin) assertNoError(err, t, "FindFileLocation()") - bp, err = p.SetBreakpoint(pc, UserBreakpoint, nil) + bp, err = p.SetBreakpoint(pc, proc.UserBreakpoint, nil) } assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") p.ClearBreakpoint(bp.Addr) regs, err := p.CurrentThread().Registers(false) assertNoError(err, t, "Registers") @@ -418,9 +384,9 @@ func testseq(program string, contFunc contFunc, testcases []nextTest, initialLoc switch contFunc { case contNext: - assertNoError(Next(p), t, "Next() returned an error") + assertNoError(proc.Next(p), t, "Next() returned an error") case contStep: - assertNoError(Step(p), t, "Step() returned an error") + assertNoError(proc.Step(p), t, "Step() returned an error") } f, ln = currentLineNumber(p, t) @@ -440,9 +406,9 @@ func testseq(program string, contFunc contFunc, testcases []nextTest, initialLoc func TestNextGeneral(t *testing.T) { var testcases []nextTest - ver, _ := ParseVersionString(runtime.Version()) + ver, _ := proc.ParseVersionString(runtime.Version()) - if ver.Major < 0 || ver.AfterOrEqual(GoVersion{1, 7, -1, 0, 0}) { + if ver.Major < 0 || ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0}) { testcases = []nextTest{ {17, 19}, {19, 20}, @@ -490,10 +456,10 @@ func TestNextConcurrent(t *testing.T) { {9, 10}, {10, 11}, } - withTestProcess("parallel_next", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("parallel_next", t, func(p target.Interface, fixture protest.Fixture) { bp, err := setFunctionBreakpoint(p, "main.sayhi") assertNoError(err, t, "SetBreakpoint") - assertNoError(Continue(p), t, "Continue") + assertNoError(proc.Continue(p), t, "Continue") f, ln := currentLineNumber(p, t) initV, err := evalVariable(p, "n") initVval, _ := constant.Int64Val(initV.Value) @@ -501,7 +467,7 @@ func TestNextConcurrent(t *testing.T) { _, err = p.ClearBreakpoint(bp.Addr) assertNoError(err, t, "ClearBreakpoint()") for _, tc := range testcases { - g, err := GetG(p.CurrentThread()) + g, err := proc.GetG(p.CurrentThread()) assertNoError(err, t, "GetG()") if p.SelectedGoroutine().ID != g.ID { t.Fatalf("SelectedGoroutine not CurrentThread's goroutine: %d %d", g.ID, p.SelectedGoroutine().ID) @@ -509,7 +475,7 @@ func TestNextConcurrent(t *testing.T) { if ln != tc.begin { t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln) } - assertNoError(Next(p), t, "Next() returned an error") + assertNoError(proc.Next(p), t, "Next() returned an error") f, ln = currentLineNumber(p, t) if ln != tc.end { t.Fatalf("Program did not continue to correct next location expected %d was %s:%d", tc.end, filepath.Base(f), ln) @@ -531,16 +497,16 @@ func TestNextConcurrentVariant2(t *testing.T) { {9, 10}, {10, 11}, } - withTestProcess("parallel_next", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("parallel_next", t, func(p target.Interface, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.sayhi") assertNoError(err, t, "SetBreakpoint") - assertNoError(Continue(p), t, "Continue") + assertNoError(proc.Continue(p), t, "Continue") f, ln := currentLineNumber(p, t) initV, err := evalVariable(p, "n") initVval, _ := constant.Int64Val(initV.Value) assertNoError(err, t, "EvalVariable") for _, tc := range testcases { - g, err := GetG(p.CurrentThread()) + g, err := proc.GetG(p.CurrentThread()) assertNoError(err, t, "GetG()") if p.SelectedGoroutine().ID != g.ID { t.Fatalf("SelectedGoroutine not CurrentThread's goroutine: %d %d", g.ID, p.SelectedGoroutine().ID) @@ -548,7 +514,7 @@ func TestNextConcurrentVariant2(t *testing.T) { if ln != tc.begin { t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln) } - assertNoError(Next(p), t, "Next() returned an error") + assertNoError(proc.Next(p), t, "Next() returned an error") var vval int64 for { v, err := evalVariable(p, "n") @@ -563,7 +529,7 @@ func TestNextConcurrentVariant2(t *testing.T) { if vval == initVval { t.Fatal("Initial breakpoint triggered twice for the same goroutine") } - assertNoError(Continue(p), t, "Continue 2") + assertNoError(proc.Continue(p), t, "Continue 2") } } f, ln = currentLineNumber(p, t) @@ -600,7 +566,7 @@ func TestNextNetHTTP(t *testing.T) { {11, 12}, {12, 13}, } - withTestProcess("testnextnethttp", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testnextnethttp", t, func(p target.Interface, fixture protest.Fixture) { go func() { for !p.Running() { time.Sleep(50 * time.Millisecond) @@ -616,7 +582,7 @@ func TestNextNetHTTP(t *testing.T) { } http.Get("http://localhost:9191") }() - if err := Continue(p); err != nil { + if err := proc.Continue(p); err != nil { t.Fatal(err) } f, ln := currentLineNumber(p, t) @@ -625,7 +591,7 @@ func TestNextNetHTTP(t *testing.T) { t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln) } - assertNoError(Next(p), t, "Next() returned an error") + assertNoError(proc.Next(p), t, "Next() returned an error") f, ln = currentLineNumber(p, t) if ln != tc.end { @@ -636,8 +602,8 @@ func TestNextNetHTTP(t *testing.T) { } func TestRuntimeBreakpoint(t *testing.T) { - withTestProcess("testruntimebreakpoint", t, func(p IProcess, fixture protest.Fixture) { - err := Continue(p) + withTestProcess("testruntimebreakpoint", t, func(p target.Interface, fixture protest.Fixture) { + err := proc.Continue(p) if err != nil { t.Fatal(err) } @@ -651,28 +617,28 @@ func TestRuntimeBreakpoint(t *testing.T) { }) } -func returnAddress(thread IThread) (uint64, error) { - locations, err := ThreadStacktrace(thread, 2) +func returnAddress(thread proc.IThread) (uint64, error) { + locations, err := proc.ThreadStacktrace(thread, 2) if err != nil { return 0, err } if len(locations) < 2 { - return 0, NoReturnAddr{locations[0].Current.Fn.BaseName()} + return 0, proc.NoReturnAddr{locations[0].Current.Fn.BaseName()} } return locations[1].Current.PC, nil } func TestFindReturnAddress(t *testing.T) { - withTestProcess("testnextprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testnextprog", t, func(p target.Interface, fixture protest.Fixture) { start, _, err := p.BinInfo().LineToPC(fixture.Source, 24) if err != nil { t.Fatal(err) } - _, err = p.SetBreakpoint(start, UserBreakpoint, nil) + _, err = p.SetBreakpoint(start, proc.UserBreakpoint, nil) if err != nil { t.Fatal(err) } - err = Continue(p) + err = proc.Continue(p) if err != nil { t.Fatal(err) } @@ -688,14 +654,14 @@ func TestFindReturnAddress(t *testing.T) { } func TestFindReturnAddressTopOfStackFn(t *testing.T) { - withTestProcess("testreturnaddress", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testreturnaddress", t, func(p target.Interface, fixture protest.Fixture) { fnName := "runtime.rt0_go" fnentry, err := p.FindFunctionLocation(fnName, false, 0) assertNoError(err, t, "FindFunctionLocation") - if _, err := p.SetBreakpoint(fnentry, UserBreakpoint, nil); err != nil { + if _, err := p.SetBreakpoint(fnentry, proc.UserBreakpoint, nil); err != nil { t.Fatal(err) } - if err := Continue(p); err != nil { + if err := proc.Continue(p); err != nil { t.Fatal(err) } if _, err := returnAddress(p.CurrentThread()); err == nil { @@ -705,7 +671,7 @@ func TestFindReturnAddressTopOfStackFn(t *testing.T) { } func TestSwitchThread(t *testing.T) { - withTestProcess("testnextprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testnextprog", t, func(p target.Interface, fixture protest.Fixture) { // With invalid thread id err := p.SwitchThread(-1) if err == nil { @@ -715,11 +681,11 @@ func TestSwitchThread(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = p.SetBreakpoint(pc, UserBreakpoint, nil) + _, err = p.SetBreakpoint(pc, proc.UserBreakpoint, nil) if err != nil { t.Fatal(err) } - err = Continue(p) + err = proc.Continue(p) if err != nil { t.Fatal(err) } @@ -755,20 +721,20 @@ func TestCGONext(t *testing.T) { return } - withTestProcess("cgotest", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("cgotest", t, func(p target.Interface, fixture protest.Fixture) { pc, err := p.FindFunctionLocation("main.main", true, 0) if err != nil { t.Fatal(err) } - _, err = p.SetBreakpoint(pc, UserBreakpoint, nil) + _, err = p.SetBreakpoint(pc, proc.UserBreakpoint, nil) if err != nil { t.Fatal(err) } - err = Continue(p) + err = proc.Continue(p) if err != nil { t.Fatal(err) } - err = Next(p) + err = proc.Next(p) if err != nil { t.Fatal(err) } @@ -780,7 +746,7 @@ type loc struct { fn string } -func (l1 *loc) match(l2 Stackframe) bool { +func (l1 *loc) match(l2 proc.Stackframe) bool { if l1.line >= 0 { if l1.line != l2.Call.Line { return false @@ -794,13 +760,13 @@ func TestStacktrace(t *testing.T) { {{4, "main.stacktraceme"}, {8, "main.func1"}, {16, "main.main"}}, {{4, "main.stacktraceme"}, {8, "main.func1"}, {12, "main.func2"}, {17, "main.main"}}, } - withTestProcess("stacktraceprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("stacktraceprog", t, func(p target.Interface, fixture protest.Fixture) { bp, err := setFunctionBreakpoint(p, "main.stacktraceme") assertNoError(err, t, "BreakByLocation()") for i := range stacks { - assertNoError(Continue(p), t, "Continue()") - locations, err := ThreadStacktrace(p.CurrentThread(), 40) + assertNoError(proc.Continue(p), t, "Continue()") + locations, err := proc.ThreadStacktrace(p.CurrentThread(), 40) assertNoError(err, t, "Stacktrace()") if len(locations) != len(stacks[i])+2 { @@ -820,15 +786,15 @@ func TestStacktrace(t *testing.T) { } p.ClearBreakpoint(bp.Addr) - Continue(p) + proc.Continue(p) }) } func TestStacktrace2(t *testing.T) { - withTestProcess("retstack", t, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), t, "Continue()") + withTestProcess("retstack", t, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), t, "Continue()") - locations, err := ThreadStacktrace(p.CurrentThread(), 40) + locations, err := proc.ThreadStacktrace(p.CurrentThread(), 40) assertNoError(err, t, "Stacktrace()") if !stackMatch([]loc{{-1, "main.f"}, {16, "main.main"}}, locations, false) { for i := range locations { @@ -837,8 +803,8 @@ func TestStacktrace2(t *testing.T) { t.Fatalf("Stack error at main.f()\n%v\n", locations) } - assertNoError(Continue(p), t, "Continue()") - locations, err = ThreadStacktrace(p.CurrentThread(), 40) + assertNoError(proc.Continue(p), t, "Continue()") + locations, err = proc.ThreadStacktrace(p.CurrentThread(), 40) assertNoError(err, t, "Stacktrace()") if !stackMatch([]loc{{-1, "main.g"}, {17, "main.main"}}, locations, false) { for i := range locations { @@ -850,7 +816,7 @@ func TestStacktrace2(t *testing.T) { } -func stackMatch(stack []loc, locations []Stackframe, skipRuntime bool) bool { +func stackMatch(stack []loc, locations []proc.Stackframe, skipRuntime bool) bool { if len(stack) > len(locations) { return false } @@ -876,13 +842,13 @@ func TestStacktraceGoroutine(t *testing.T) { mainStack := []loc{{13, "main.stacktraceme"}, {26, "main.main"}} agoroutineStacks := [][]loc{[]loc{{8, "main.agoroutine"}}, []loc{{9, "main.agoroutine"}}, []loc{{10, "main.agoroutine"}}} - withTestProcess("goroutinestackprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("goroutinestackprog", t, func(p target.Interface, fixture protest.Fixture) { bp, err := setFunctionBreakpoint(p, "main.stacktraceme") assertNoError(err, t, "BreakByLocation()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") - gs, err := GoroutinesInfo(p) + gs, err := proc.GoroutinesInfo(p) assertNoError(err, t, "GoroutinesInfo") agoroutineCount := 0 @@ -930,7 +896,7 @@ func TestStacktraceGoroutine(t *testing.T) { } p.ClearBreakpoint(bp.Addr) - Continue(p) + proc.Continue(p) }) } @@ -939,7 +905,7 @@ func TestKill(t *testing.T) { // k command presumably works but leaves the process around? return } - withTestProcess("testprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testprog", t, func(p target.Interface, fixture protest.Fixture) { if err := p.Kill(); err != nil { t.Fatal(err) } @@ -955,13 +921,13 @@ func TestKill(t *testing.T) { }) } -func testGSupportFunc(name string, t *testing.T, p IProcess, fixture protest.Fixture) { +func testGSupportFunc(name string, t *testing.T, p target.Interface, fixture protest.Fixture) { bp, err := setFunctionBreakpoint(p, "main.main") assertNoError(err, t, name+": BreakByLocation()") - assertNoError(Continue(p), t, name+": Continue()") + assertNoError(proc.Continue(p), t, name+": Continue()") - g, err := GetG(p.CurrentThread()) + g, err := proc.GetG(p.CurrentThread()) assertNoError(err, t, name+": GetG()") if g == nil { @@ -974,7 +940,7 @@ func testGSupportFunc(name string, t *testing.T, p IProcess, fixture protest.Fix } func TestGetG(t *testing.T) { - withTestProcess("testprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testprog", t, func(p target.Interface, fixture protest.Fixture) { testGSupportFunc("nocgo", t, p, fixture) }) @@ -986,13 +952,13 @@ func TestGetG(t *testing.T) { return } - withTestProcess("cgotest", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("cgotest", t, func(p target.Interface, fixture protest.Fixture) { testGSupportFunc("cgo", t, p, fixture) }) } func TestContinueMulti(t *testing.T) { - withTestProcess("integrationprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("integrationprog", t, func(p target.Interface, fixture protest.Fixture) { bp1, err := setFunctionBreakpoint(p, "main.main") assertNoError(err, t, "BreakByLocation()") @@ -1002,7 +968,7 @@ func TestContinueMulti(t *testing.T) { mainCount := 0 sayhiCount := 0 for { - err := Continue(p) + err := proc.Continue(p) if p.Exited() { break } @@ -1027,8 +993,8 @@ func TestContinueMulti(t *testing.T) { }) } -func versionAfterOrEqual(t *testing.T, verStr string, ver GoVersion) { - pver, ok := ParseVersionString(verStr) +func versionAfterOrEqual(t *testing.T, verStr string, ver proc.GoVersion) { + pver, ok := proc.ParseVersionString(verStr) if !ok { t.Fatalf("Could not parse version string <%s>", verStr) } @@ -1039,13 +1005,13 @@ func versionAfterOrEqual(t *testing.T, verStr string, ver GoVersion) { } func TestParseVersionString(t *testing.T) { - versionAfterOrEqual(t, "go1.4", GoVersion{1, 4, 0, 0, 0}) - versionAfterOrEqual(t, "go1.5.0", GoVersion{1, 5, 0, 0, 0}) - versionAfterOrEqual(t, "go1.4.2", GoVersion{1, 4, 2, 0, 0}) - versionAfterOrEqual(t, "go1.5beta2", GoVersion{1, 5, -1, 2, 0}) - versionAfterOrEqual(t, "go1.5rc2", GoVersion{1, 5, -1, 0, 2}) - versionAfterOrEqual(t, "go1.6.1 (appengine-1.9.37)", GoVersion{1, 6, 1, 0, 0}) - ver, ok := ParseVersionString("devel +17efbfc Tue Jul 28 17:39:19 2015 +0000 linux/amd64") + versionAfterOrEqual(t, "go1.4", proc.GoVersion{1, 4, 0, 0, 0}) + versionAfterOrEqual(t, "go1.5.0", proc.GoVersion{1, 5, 0, 0, 0}) + versionAfterOrEqual(t, "go1.4.2", proc.GoVersion{1, 4, 2, 0, 0}) + versionAfterOrEqual(t, "go1.5beta2", proc.GoVersion{1, 5, -1, 2, 0}) + versionAfterOrEqual(t, "go1.5rc2", proc.GoVersion{1, 5, -1, 0, 2}) + versionAfterOrEqual(t, "go1.6.1 (appengine-1.9.37)", proc.GoVersion{1, 6, 1, 0, 0}) + ver, ok := proc.ParseVersionString("devel +17efbfc Tue Jul 28 17:39:19 2015 +0000 linux/amd64") if !ok { t.Fatalf("Could not parse devel version string") } @@ -1055,12 +1021,12 @@ func TestParseVersionString(t *testing.T) { } func TestBreakpointOnFunctionEntry(t *testing.T) { - withTestProcess("testprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testprog", t, func(p target.Interface, fixture protest.Fixture) { addr, err := p.FindFunctionLocation("main.main", false, 0) assertNoError(err, t, "FindFunctionLocation()") - _, err = p.SetBreakpoint(addr, UserBreakpoint, nil) + _, err = p.SetBreakpoint(addr, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") _, ln := currentLineNumber(p, t) if ln != 17 { t.Fatalf("Wrong line number: %d (expected: 17)\n", ln) @@ -1069,9 +1035,9 @@ func TestBreakpointOnFunctionEntry(t *testing.T) { } func TestProcessReceivesSIGCHLD(t *testing.T) { - withTestProcess("sigchldprog", t, func(p IProcess, fixture protest.Fixture) { - err := Continue(p) - _, ok := err.(ProcessExitedError) + withTestProcess("sigchldprog", t, func(p target.Interface, fixture protest.Fixture) { + err := proc.Continue(p) + _, ok := err.(proc.ProcessExitedError) if !ok { t.Fatalf("Continue() returned unexpected error type %v", err) } @@ -1079,17 +1045,17 @@ func TestProcessReceivesSIGCHLD(t *testing.T) { } func TestIssue239(t *testing.T) { - withTestProcess("is sue239", t, func(p IProcess, fixture protest.Fixture) { - pos, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 17) + withTestProcess("is sue239", t, func(p target.Interface, fixture protest.Fixture) { + pos, _, err := p.BinInfo().LineToPC(fixture.Source, 17) assertNoError(err, t, "LineToPC()") - _, err = p.SetBreakpoint(pos, UserBreakpoint, nil) + _, err = p.SetBreakpoint(pos, proc.UserBreakpoint, nil) assertNoError(err, t, fmt.Sprintf("SetBreakpoint(%d)", pos)) - assertNoError(Continue(p), t, fmt.Sprintf("Continue()")) + assertNoError(proc.Continue(p), t, fmt.Sprintf("Continue()")) }) } -func evalVariable(p IProcess, symbol string) (*Variable, error) { - scope, err := GoroutineScope(p.CurrentThread()) +func evalVariable(p target.Interface, symbol string) (*proc.Variable, error) { + scope, err := proc.GoroutineScope(p.CurrentThread()) if err != nil { return nil, err @@ -1097,8 +1063,8 @@ func evalVariable(p IProcess, symbol string) (*Variable, error) { return scope.EvalVariable(symbol, normalLoadConfig) } -func setVariable(p IProcess, symbol, value string) error { - scope, err := GoroutineScope(p.CurrentThread()) +func setVariable(p target.Interface, symbol, value string) error { + scope, err := proc.GoroutineScope(p.CurrentThread()) if err != nil { return err } @@ -1141,8 +1107,8 @@ func TestVariableEvaluation(t *testing.T) { {"ba", reflect.Slice, nil, 200, 200, 64}, } - withTestProcess("testvariables", t, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), t, "Continue() returned an error") + withTestProcess("testvariables", t, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), t, "Continue() returned an error") for _, tc := range testcases { v, err := evalVariable(p, tc.name) @@ -1191,13 +1157,13 @@ func TestVariableEvaluation(t *testing.T) { } func TestFrameEvaluation(t *testing.T) { - withTestProcess("goroutinestackprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("goroutinestackprog", t, func(p target.Interface, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.stacktraceme") assertNoError(err, t, "setFunctionBreakpoint") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") // Testing evaluation on goroutines - gs, err := GoroutinesInfo(p) + gs, err := proc.GoroutinesInfo(p) assertNoError(err, t, "GoroutinesInfo") found := make([]bool, 10) for _, g := range gs { @@ -1219,7 +1185,7 @@ func TestFrameEvaluation(t *testing.T) { continue } - scope, err := ConvertEvalScope(p, g.ID, frame) + scope, err := proc.ConvertEvalScope(p, g.ID, frame) assertNoError(err, t, "ConvertEvalScope()") t.Logf("scope = %v", scope) v, err := scope.EvalVariable("i", normalLoadConfig) @@ -1239,12 +1205,12 @@ func TestFrameEvaluation(t *testing.T) { } // Testing evaluation on frames - assertNoError(Continue(p), t, "Continue() 2") - g, err := GetG(p.CurrentThread()) + assertNoError(proc.Continue(p), t, "Continue() 2") + g, err := proc.GetG(p.CurrentThread()) assertNoError(err, t, "GetG()") for i := 0; i <= 3; i++ { - scope, err := ConvertEvalScope(p, g.ID, i+1) + scope, err := proc.ConvertEvalScope(p, g.ID, i+1) assertNoError(err, t, fmt.Sprintf("ConvertEvalScope() on frame %d", i+1)) v, err := scope.EvalVariable("n", normalLoadConfig) assertNoError(err, t, fmt.Sprintf("EvalVariable() on frame %d", i+1)) @@ -1258,8 +1224,8 @@ func TestFrameEvaluation(t *testing.T) { } func TestPointerSetting(t *testing.T) { - withTestProcess("testvariables2", t, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), t, "Continue() returned an error") + withTestProcess("testvariables2", t, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), t, "Continue() returned an error") pval := func(n int64) { variable, err := evalVariable(p, "p1") @@ -1273,7 +1239,7 @@ func TestPointerSetting(t *testing.T) { pval(1) // change p1 to point to i2 - scope, err := GoroutineScope(p.CurrentThread()) + scope, err := proc.GoroutineScope(p.CurrentThread()) assertNoError(err, t, "Scope()") i2addr, err := scope.EvalExpression("i2", normalLoadConfig) assertNoError(err, t, "EvalExpression()") @@ -1287,8 +1253,8 @@ func TestPointerSetting(t *testing.T) { } func TestVariableFunctionScoping(t *testing.T) { - withTestProcess("testvariables", t, func(p IProcess, fixture protest.Fixture) { - err := Continue(p) + withTestProcess("testvariables", t, func(p target.Interface, fixture protest.Fixture) { + err := proc.Continue(p) assertNoError(err, t, "Continue() returned an error") _, err = evalVariable(p, "a1") @@ -1298,7 +1264,7 @@ func TestVariableFunctionScoping(t *testing.T) { assertNoError(err, t, "Unable to find variable a1") // Move scopes, a1 exists here by a2 does not - err = Continue(p) + err = proc.Continue(p) assertNoError(err, t, "Continue() returned an error") _, err = evalVariable(p, "a1") @@ -1312,8 +1278,8 @@ func TestVariableFunctionScoping(t *testing.T) { } func TestRecursiveStructure(t *testing.T) { - withTestProcess("testvariables2", t, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), t, "Continue()") + withTestProcess("testvariables2", t, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), t, "Continue()") v, err := evalVariable(p, "aas") assertNoError(err, t, "EvalVariable()") t.Logf("v: %v\n", v) @@ -1322,8 +1288,8 @@ func TestRecursiveStructure(t *testing.T) { func TestIssue316(t *testing.T) { // A pointer loop that includes one interface should not send dlv into an infinite loop - withTestProcess("testvariables2", t, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), t, "Continue()") + withTestProcess("testvariables2", t, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), t, "Continue()") _, err := evalVariable(p, "iface5") assertNoError(err, t, "EvalVariable()") }) @@ -1331,8 +1297,8 @@ func TestIssue316(t *testing.T) { func TestIssue325(t *testing.T) { // nil pointer dereference when evaluating interfaces to function pointers - withTestProcess("testvariables2", t, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), t, "Continue()") + withTestProcess("testvariables2", t, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), t, "Continue()") iface2fn1v, err := evalVariable(p, "iface2fn1") assertNoError(err, t, "EvalVariable()") t.Logf("iface2fn1: %v\n", iface2fn1v) @@ -1344,15 +1310,15 @@ func TestIssue325(t *testing.T) { } func TestBreakpointCounts(t *testing.T) { - withTestProcess("bpcountstest", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("bpcountstest", t, func(p target.Interface, fixture protest.Fixture) { addr, _, err := p.BinInfo().LineToPC(fixture.Source, 12) assertNoError(err, t, "LineToPC") - bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil) + bp, err := p.SetBreakpoint(addr, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") for { - if err := Continue(p); err != nil { - if _, exited := err.(ProcessExitedError); exited { + if err := proc.Continue(p); err != nil { + if _, exited := err.(proc.ProcessExitedError); exited { break } assertNoError(err, t, "Continue()") @@ -1379,8 +1345,8 @@ func TestBreakpointCounts(t *testing.T) { func BenchmarkArray(b *testing.B) { // each bencharr struct is 128 bytes, bencharr is 64 elements long b.SetBytes(int64(64 * 128)) - withTestProcess("testvariables2", b, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), b, "Continue()") + withTestProcess("testvariables2", b, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), b, "Continue()") for i := 0; i < b.N; i++ { _, err := evalVariable(p, "bencharr") assertNoError(err, b, "EvalVariable()") @@ -1395,15 +1361,15 @@ func TestBreakpointCountsWithDetection(t *testing.T) { return } m := map[int64]int64{} - withTestProcess("bpcountstest", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("bpcountstest", t, func(p target.Interface, fixture protest.Fixture) { addr, _, err := p.BinInfo().LineToPC(fixture.Source, 12) assertNoError(err, t, "LineToPC") - bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil) + bp, err := p.SetBreakpoint(addr, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") for { - if err := Continue(p); err != nil { - if _, exited := err.(ProcessExitedError); exited { + if err := proc.Continue(p); err != nil { + if _, exited := err.(proc.ProcessExitedError); exited { break } assertNoError(err, t, "Continue()") @@ -1412,7 +1378,7 @@ func TestBreakpointCountsWithDetection(t *testing.T) { if bp, _, _ := th.Breakpoint(); bp == nil { continue } - scope, err := GoroutineScope(th) + scope, err := proc.GoroutineScope(th) assertNoError(err, t, "Scope()") v, err := scope.EvalVariable("i", normalLoadConfig) assertNoError(err, t, "evalVariable") @@ -1454,8 +1420,8 @@ func BenchmarkArrayPointer(b *testing.B) { // each bencharr struct is 128 bytes, benchparr is an array of 64 pointers to bencharr // each read will read 64 bencharr structs plus the 64 pointers of benchparr b.SetBytes(int64(64*128 + 64*8)) - withTestProcess("testvariables2", b, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), b, "Continue()") + withTestProcess("testvariables2", b, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), b, "Continue()") for i := 0; i < b.N; i++ { _, err := evalVariable(p, "bencharr") assertNoError(err, b, "EvalVariable()") @@ -1468,8 +1434,8 @@ func BenchmarkMap(b *testing.B) { // each string key has an average of 9 character // reading strings and the map structure imposes a overhead that we ignore here b.SetBytes(int64(41 * (2*8 + 9))) - withTestProcess("testvariables2", b, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), b, "Continue()") + withTestProcess("testvariables2", b, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), b, "Continue()") for i := 0; i < b.N; i++ { _, err := evalVariable(p, "m1") assertNoError(err, b, "EvalVariable()") @@ -1478,18 +1444,14 @@ func BenchmarkMap(b *testing.B) { } func BenchmarkGoroutinesInfo(b *testing.B) { - withTestProcess("testvariables2", b, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), b, "Continue()") + withTestProcess("testvariables2", b, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), b, "Continue()") for i := 0; i < b.N; i++ { - switch p := p.(type) { - case *GdbserverProcess: - p.allGCache = nil - case *Process: - p.allGCache = nil - case *CoreProcess: - p.allGCache = nil + if p, ok := p.(proc.AllGCache); ok { + allgcache := p.AllGCache() + *allgcache = nil } - _, err := GoroutinesInfo(p) + _, err := proc.GoroutinesInfo(p) assertNoError(err, b, "GoroutinesInfo") } }) @@ -1497,18 +1459,18 @@ func BenchmarkGoroutinesInfo(b *testing.B) { func TestIssue262(t *testing.T) { // Continue does not work when the current breakpoint is set on a NOP instruction - withTestProcess("issue262", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("issue262", t, func(p target.Interface, fixture protest.Fixture) { addr, _, err := p.BinInfo().LineToPC(fixture.Source, 11) assertNoError(err, t, "LineToPC") - _, err = p.SetBreakpoint(addr, UserBreakpoint, nil) + _, err = p.SetBreakpoint(addr, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") - err = Continue(p) + assertNoError(proc.Continue(p), t, "Continue()") + err = proc.Continue(p) if err == nil { t.Fatalf("No error on second continue") } - _, exited := err.(ProcessExitedError) + _, exited := err.(proc.ProcessExitedError) if !exited { t.Fatalf("Process did not exit after second continue: %v", err) } @@ -1519,27 +1481,27 @@ func TestIssue305(t *testing.T) { // If 'next' hits a breakpoint on the goroutine it's stepping through // the internal breakpoints aren't cleared preventing further use of // 'next' command - withTestProcess("issue305", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("issue305", t, func(p target.Interface, fixture protest.Fixture) { addr, _, err := p.BinInfo().LineToPC(fixture.Source, 5) assertNoError(err, t, "LineToPC()") - _, err = p.SetBreakpoint(addr, UserBreakpoint, nil) + _, err = p.SetBreakpoint(addr, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") - assertNoError(Next(p), t, "Next() 1") - assertNoError(Next(p), t, "Next() 2") - assertNoError(Next(p), t, "Next() 3") - assertNoError(Next(p), t, "Next() 4") - assertNoError(Next(p), t, "Next() 5") + assertNoError(proc.Next(p), t, "Next() 1") + assertNoError(proc.Next(p), t, "Next() 2") + assertNoError(proc.Next(p), t, "Next() 3") + assertNoError(proc.Next(p), t, "Next() 4") + assertNoError(proc.Next(p), t, "Next() 5") }) } func TestPointerLoops(t *testing.T) { // Pointer loops through map entries, pointers and slices // Regression test for issue #341 - withTestProcess("testvariables2", t, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), t, "Continue()") + withTestProcess("testvariables2", t, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), t, "Continue()") for _, expr := range []string{"mapinf", "ptrinf", "sliceinf"} { t.Logf("requesting %s", expr) v, err := evalVariable(p, expr) @@ -1550,9 +1512,9 @@ func TestPointerLoops(t *testing.T) { } func BenchmarkLocalVariables(b *testing.B) { - withTestProcess("testvariables", b, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), b, "Continue() returned an error") - scope, err := GoroutineScope(p.CurrentThread()) + withTestProcess("testvariables", b, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), b, "Continue() returned an error") + scope, err := proc.GoroutineScope(p.CurrentThread()) assertNoError(err, b, "Scope()") for i := 0; i < b.N; i++ { _, err := scope.LocalVariables(normalLoadConfig) @@ -1562,10 +1524,10 @@ func BenchmarkLocalVariables(b *testing.B) { } func TestCondBreakpoint(t *testing.T) { - withTestProcess("parallel_next", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("parallel_next", t, func(p target.Interface, fixture protest.Fixture) { addr, _, err := p.BinInfo().LineToPC(fixture.Source, 9) assertNoError(err, t, "LineToPC") - bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil) + bp, err := p.SetBreakpoint(addr, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") bp.Cond = &ast.BinaryExpr{ Op: token.EQL, @@ -1573,7 +1535,7 @@ func TestCondBreakpoint(t *testing.T) { Y: &ast.BasicLit{Kind: token.INT, Value: "7"}, } - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") nvar, err := evalVariable(p, "n") assertNoError(err, t, "EvalVariable()") @@ -1586,10 +1548,10 @@ func TestCondBreakpoint(t *testing.T) { } func TestCondBreakpointError(t *testing.T) { - withTestProcess("parallel_next", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("parallel_next", t, func(p target.Interface, fixture protest.Fixture) { addr, _, err := p.BinInfo().LineToPC(fixture.Source, 9) assertNoError(err, t, "LineToPC") - bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil) + bp, err := p.SetBreakpoint(addr, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") bp.Cond = &ast.BinaryExpr{ Op: token.EQL, @@ -1597,7 +1559,7 @@ func TestCondBreakpointError(t *testing.T) { Y: &ast.BasicLit{Kind: token.INT, Value: "7"}, } - err = Continue(p) + err = proc.Continue(p) if err == nil { t.Fatalf("No error on first Continue()") } @@ -1612,9 +1574,9 @@ func TestCondBreakpointError(t *testing.T) { Y: &ast.BasicLit{Kind: token.INT, Value: "7"}, } - err = Continue(p) + err = proc.Continue(p) if err != nil { - if _, exited := err.(ProcessExitedError); !exited { + if _, exited := err.(proc.ProcessExitedError); !exited { t.Fatalf("Unexpected error on second Continue(): %v", err) } } else { @@ -1631,8 +1593,8 @@ func TestCondBreakpointError(t *testing.T) { func TestIssue356(t *testing.T) { // slice with a typedef does not get printed correctly - withTestProcess("testvariables2", t, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), t, "Continue() returned an error") + withTestProcess("testvariables2", t, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), t, "Continue() returned an error") mmvar, err := evalVariable(p, "mainMenu") assertNoError(err, t, "EvalVariable()") if mmvar.Kind != reflect.Slice { @@ -1642,11 +1604,11 @@ func TestIssue356(t *testing.T) { } func TestStepIntoFunction(t *testing.T) { - withTestProcess("teststep", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("teststep", t, func(p target.Interface, fixture protest.Fixture) { // Continue until breakpoint - assertNoError(Continue(p), t, "Continue() returned an error") + assertNoError(proc.Continue(p), t, "Continue() returned an error") // Step into function - assertNoError(Step(p), t, "Step() returned an error") + assertNoError(proc.Step(p), t, "Step() returned an error") // We should now be inside the function. loc, err := p.CurrentThread().Location() if err != nil { @@ -1666,12 +1628,12 @@ func TestStepIntoFunction(t *testing.T) { func TestIssue384(t *testing.T) { // Crash related to reading uninitialized memory, introduced by the memory prefetching optimization - withTestProcess("issue384", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("issue384", t, func(p target.Interface, fixture protest.Fixture) { start, _, err := p.BinInfo().LineToPC(fixture.Source, 13) assertNoError(err, t, "LineToPC()") - _, err = p.SetBreakpoint(start, UserBreakpoint, nil) + _, err = p.SetBreakpoint(start, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") _, err = evalVariable(p, "st") assertNoError(err, t, "EvalVariable()") }) @@ -1679,14 +1641,14 @@ func TestIssue384(t *testing.T) { func TestIssue332_Part1(t *testing.T) { // Next shouldn't step inside a function call - withTestProcess("issue332", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("issue332", t, func(p target.Interface, fixture protest.Fixture) { start, _, err := p.BinInfo().LineToPC(fixture.Source, 8) assertNoError(err, t, "LineToPC()") - _, err = p.SetBreakpoint(start, UserBreakpoint, nil) + _, err = p.SetBreakpoint(start, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") - assertNoError(Next(p), t, "first Next()") - locations, err := ThreadStacktrace(p.CurrentThread(), 2) + assertNoError(proc.Continue(p), t, "Continue()") + assertNoError(proc.Next(p), t, "first Next()") + locations, err := proc.ThreadStacktrace(p.CurrentThread(), 2) assertNoError(err, t, "Stacktrace()") if locations[0].Call.Fn == nil { t.Fatalf("Not on a function") @@ -1705,17 +1667,17 @@ func TestIssue332_Part2(t *testing.T) { // In some parts of the prologue, for some functions, the FDE data is incorrect // which leads to 'next' and 'stack' failing with error "could not find FDE for PC: " // because the incorrect FDE data leads to reading the wrong stack address as the return address - withTestProcess("issue332", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("issue332", t, func(p target.Interface, fixture protest.Fixture) { start, _, err := p.BinInfo().LineToPC(fixture.Source, 8) assertNoError(err, t, "LineToPC()") - _, err = p.SetBreakpoint(start, UserBreakpoint, nil) + _, err = p.SetBreakpoint(start, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") // step until we enter changeMe for { - assertNoError(Step(p), t, "Step()") - locations, err := ThreadStacktrace(p.CurrentThread(), 2) + assertNoError(proc.Step(p), t, "Step()") + locations, err := proc.ThreadStacktrace(p.CurrentThread(), 2) assertNoError(err, t, "Stacktrace()") if locations[0].Call.Fn == nil { t.Fatalf("Not on a function") @@ -1738,18 +1700,18 @@ func TestIssue332_Part2(t *testing.T) { t.Fatalf("Step did not skip the prologue: current pc: %x, first instruction after prologue: %x", pc, pcAfterPrologue) } - assertNoError(Next(p), t, "first Next()") - assertNoError(Next(p), t, "second Next()") - assertNoError(Next(p), t, "third Next()") - err = Continue(p) - if _, exited := err.(ProcessExitedError); !exited { + assertNoError(proc.Next(p), t, "first Next()") + assertNoError(proc.Next(p), t, "second Next()") + assertNoError(proc.Next(p), t, "third Next()") + err = proc.Continue(p) + if _, exited := err.(proc.ProcessExitedError); !exited { assertNoError(err, t, "final Continue()") } }) } func TestIssue396(t *testing.T) { - withTestProcess("callme", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("callme", t, func(p target.Interface, fixture protest.Fixture) { _, err := p.FindFunctionLocation("main.init", true, -1) assertNoError(err, t, "FindFunctionLocation()") }) @@ -1757,16 +1719,16 @@ func TestIssue396(t *testing.T) { func TestIssue414(t *testing.T) { // Stepping until the program exits - withTestProcess("math", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("math", t, func(p target.Interface, fixture protest.Fixture) { start, _, err := p.BinInfo().LineToPC(fixture.Source, 9) assertNoError(err, t, "LineToPC()") - _, err = p.SetBreakpoint(start, UserBreakpoint, nil) + _, err = p.SetBreakpoint(start, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") for { - err := Step(p) + err := proc.Step(p) if err != nil { - if _, exited := err.(ProcessExitedError); exited { + if _, exited := err.(proc.ProcessExitedError); exited { break } } @@ -1776,10 +1738,10 @@ func TestIssue414(t *testing.T) { } func TestPackageVariables(t *testing.T) { - withTestProcess("testvariables", t, func(p IProcess, fixture protest.Fixture) { - err := Continue(p) + withTestProcess("testvariables", t, func(p target.Interface, fixture protest.Fixture) { + err := proc.Continue(p) assertNoError(err, t, "Continue()") - scope, err := GoroutineScope(p.CurrentThread()) + scope, err := proc.GoroutineScope(p.CurrentThread()) assertNoError(err, t, "Scope()") vars, err := scope.PackageVariables(normalLoadConfig) assertNoError(err, t, "PackageVariables()") @@ -1797,20 +1759,20 @@ func TestPackageVariables(t *testing.T) { } func TestIssue149(t *testing.T) { - ver, _ := ParseVersionString(runtime.Version()) - if ver.Major > 0 && !ver.AfterOrEqual(GoVersion{1, 7, -1, 0, 0}) { + ver, _ := proc.ParseVersionString(runtime.Version()) + if ver.Major > 0 && !ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0}) { return } // setting breakpoint on break statement - withTestProcess("break", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("break", t, func(p target.Interface, fixture protest.Fixture) { _, err := p.FindFileLocation(fixture.Source, 8) assertNoError(err, t, "FindFileLocation()") }) } func TestPanicBreakpoint(t *testing.T) { - withTestProcess("panic", t, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), t, "Continue()") + withTestProcess("panic", t, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), t, "Continue()") bp, _, _ := p.CurrentThread().Breakpoint() if bp == nil || bp.Name != "unrecovered-panic" { t.Fatalf("not on unrecovered-panic breakpoint: %v", bp) @@ -1819,13 +1781,13 @@ func TestPanicBreakpoint(t *testing.T) { } func TestCmdLineArgs(t *testing.T) { - expectSuccess := func(p IProcess, fixture protest.Fixture) { - err := Continue(p) + expectSuccess := func(p target.Interface, fixture protest.Fixture) { + err := proc.Continue(p) bp, _, _ := p.CurrentThread().Breakpoint() if bp != nil && bp.Name == "unrecovered-panic" { t.Fatalf("testing args failed on unrecovered-panic breakpoint: %v", bp) } - exit, exited := err.(ProcessExitedError) + exit, exited := err.(proc.ProcessExitedError) if !exited { t.Fatalf("Process did not exit: %v", err) } else { @@ -1835,8 +1797,8 @@ func TestCmdLineArgs(t *testing.T) { } } - expectPanic := func(p IProcess, fixture protest.Fixture) { - Continue(p) + expectPanic := func(p target.Interface, fixture protest.Fixture) { + proc.Continue(p) bp, _, _ := p.CurrentThread().Breakpoint() if bp == nil || bp.Name != "unrecovered-panic" { t.Fatalf("not on unrecovered-panic breakpoint: %v", bp) @@ -1862,7 +1824,7 @@ func TestIssue462(t *testing.T) { if runtime.GOOS == "windows" { return } - withTestProcess("testnextnethttp", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testnextnethttp", t, func(p target.Interface, fixture protest.Fixture) { go func() { for !p.Running() { time.Sleep(50 * time.Millisecond) @@ -1881,42 +1843,33 @@ func TestIssue462(t *testing.T) { p.RequestManualStop() }() - assertNoError(Continue(p), t, "Continue()") - _, err := ThreadStacktrace(p.CurrentThread(), 40) + assertNoError(proc.Continue(p), t, "Continue()") + _, err := proc.ThreadStacktrace(p.CurrentThread(), 40) assertNoError(err, t, "Stacktrace()") }) } -func TestIssue554(t *testing.T) { - // unsigned integer overflow in proc.(*memCache).contains was - // causing it to always return true for address 0xffffffffffffffff - mem := memCache{0x20, make([]byte, 100), nil} - if mem.contains(0xffffffffffffffff, 40) { - t.Fatalf("should be false") - } -} - func TestNextParked(t *testing.T) { - withTestProcess("parallel_next", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("parallel_next", t, func(p target.Interface, fixture protest.Fixture) { bp, err := setFunctionBreakpoint(p, "main.sayhi") assertNoError(err, t, "SetBreakpoint()") // continue until a parked goroutine exists - var parkedg *G + var parkedg *proc.G LookForParkedG: for { - err := Continue(p) - if _, exited := err.(ProcessExitedError); exited { + err := proc.Continue(p) + if _, exited := err.(proc.ProcessExitedError); exited { t.Log("could not find parked goroutine") return } assertNoError(err, t, "Continue()") - gs, err := GoroutinesInfo(p) + gs, err := proc.GoroutinesInfo(p) assertNoError(err, t, "GoroutinesInfo()") for _, g := range gs { - if g.thread == nil { + if g.Thread == nil { parkedg = g break LookForParkedG } @@ -1925,7 +1878,7 @@ func TestNextParked(t *testing.T) { assertNoError(p.SwitchGoroutine(parkedg.ID), t, "SwitchGoroutine()") p.ClearBreakpoint(bp.Addr) - assertNoError(Next(p), t, "Next()") + assertNoError(proc.Next(p), t, "Next()") if p.SelectedGoroutine().ID != parkedg.ID { t.Fatalf("Next did not continue on the selected goroutine, expected %d got %d", parkedg.ID, p.SelectedGoroutine().ID) @@ -1934,26 +1887,26 @@ func TestNextParked(t *testing.T) { } func TestStepParked(t *testing.T) { - withTestProcess("parallel_next", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("parallel_next", t, func(p target.Interface, fixture protest.Fixture) { bp, err := setFunctionBreakpoint(p, "main.sayhi") assertNoError(err, t, "SetBreakpoint()") // continue until a parked goroutine exists - var parkedg *G + var parkedg *proc.G LookForParkedG: for { - err := Continue(p) - if _, exited := err.(ProcessExitedError); exited { + err := proc.Continue(p) + if _, exited := err.(proc.ProcessExitedError); exited { t.Log("could not find parked goroutine") return } assertNoError(err, t, "Continue()") - gs, err := GoroutinesInfo(p) + gs, err := proc.GoroutinesInfo(p) assertNoError(err, t, "GoroutinesInfo()") for _, g := range gs { - if g.thread == nil && g.CurrentLoc.Fn != nil && g.CurrentLoc.Fn.Name == "main.sayhi" { + if g.Thread == nil && g.CurrentLoc.Fn != nil && g.CurrentLoc.Fn.Name == "main.sayhi" { parkedg = g break LookForParkedG } @@ -1972,7 +1925,7 @@ func TestStepParked(t *testing.T) { assertNoError(p.SwitchGoroutine(parkedg.ID), t, "SwitchGoroutine()") p.ClearBreakpoint(bp.Addr) - assertNoError(Step(p), t, "Step()") + assertNoError(proc.Step(p), t, "Step()") if p.SelectedGoroutine().ID != parkedg.ID { t.Fatalf("Step did not continue on the selected goroutine, expected %d got %d", parkedg.ID, p.SelectedGoroutine().ID) @@ -1987,19 +1940,19 @@ func TestIssue509(t *testing.T) { cmd.Dir = nomaindir assertNoError(cmd.Run(), t, "go build") exepath := filepath.Join(nomaindir, "debug") - _, err := Launch([]string{exepath}, ".") + _, err := native.Launch([]string{exepath}, ".") if err == nil { t.Fatalf("expected error but none was generated") } - if err != NotExecutableErr { - t.Fatalf("expected error \"%v\" got \"%v\"", NotExecutableErr, err) + if err != proc.NotExecutableErr { + t.Fatalf("expected error \"%v\" got \"%v\"", proc.NotExecutableErr, err) } os.Remove(exepath) } func TestUnsupportedArch(t *testing.T) { - ver, _ := ParseVersionString(runtime.Version()) - if ver.Major < 0 || !ver.AfterOrEqual(GoVersion{1, 6, -1, 0, 0}) || ver.AfterOrEqual(GoVersion{1, 7, -1, 0, 0}) { + ver, _ := proc.ParseVersionString(runtime.Version()) + if ver.Major < 0 || !ver.AfterOrEqual(proc.GoVersion{1, 6, -1, 0, 0}) || ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0}) { // cross compile (with -N?) works only on select versions of go return } @@ -2021,9 +1974,9 @@ func TestUnsupportedArch(t *testing.T) { } defer os.Remove(outfile) - p, err := Launch([]string{outfile}, ".") + p, err := native.Launch([]string{outfile}, ".") switch err { - case UnsupportedLinuxArchErr, UnsupportedWindowsArchErr, UnsupportedDarwinArchErr: + case proc.UnsupportedLinuxArchErr, proc.UnsupportedWindowsArchErr, proc.UnsupportedDarwinArchErr: // all good case nil: p.Halt() @@ -2037,19 +1990,19 @@ func TestUnsupportedArch(t *testing.T) { func TestIssue573(t *testing.T) { // calls to runtime.duffzero and runtime.duffcopy jump directly into the middle // of the function and the internal breakpoint set by StepInto may be missed. - withTestProcess("issue573", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("issue573", t, func(p target.Interface, fixture protest.Fixture) { fentry, _ := p.FindFunctionLocation("main.foo", false, 0) - _, err := p.SetBreakpoint(fentry, UserBreakpoint, nil) + _, err := p.SetBreakpoint(fentry, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") - assertNoError(Step(p), t, "Step() #1") - assertNoError(Step(p), t, "Step() #2") // Bug exits here. - assertNoError(Step(p), t, "Step() #3") // Third step ought to be possible; program ought not have exited. + assertNoError(proc.Continue(p), t, "Continue()") + assertNoError(proc.Step(p), t, "Step() #1") + assertNoError(proc.Step(p), t, "Step() #2") // Bug exits here. + assertNoError(proc.Step(p), t, "Step() #3") // Third step ought to be possible; program ought not have exited. }) } func TestTestvariables2Prologue(t *testing.T) { - withTestProcess("testvariables2", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testvariables2", t, func(p target.Interface, fixture protest.Fixture) { addrEntry, err := p.FindFunctionLocation("main.main", false, 0) assertNoError(err, t, "FindFunctionLocation - entrypoint") addrPrologue, err := p.FindFunctionLocation("main.main", true, 0) @@ -2136,9 +2089,9 @@ func TestStepDeferReturn(t *testing.T) { func TestStepIgnorePrivateRuntime(t *testing.T) { // Tests that Step will ignore calls to private runtime functions // (such as runtime.convT2E in this case) - ver, _ := ParseVersionString(runtime.Version()) + ver, _ := proc.ParseVersionString(runtime.Version()) - if ver.Major < 0 || ver.AfterOrEqual(GoVersion{1, 7, -1, 0, 0}) { + if ver.Major < 0 || ver.AfterOrEqual(proc.GoVersion{1, 7, -1, 0, 0}) { testseq("teststepprog", contStep, []nextTest{ {21, 13}, {13, 14}, @@ -2159,10 +2112,10 @@ func TestStepIgnorePrivateRuntime(t *testing.T) { func TestIssue561(t *testing.T) { // Step fails to make progress when PC is at a CALL instruction // where a breakpoint is also set. - withTestProcess("issue561", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("issue561", t, func(p target.Interface, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture, 10) - assertNoError(Continue(p), t, "Continue()") - assertNoError(Step(p), t, "Step()") + assertNoError(proc.Continue(p), t, "Continue()") + assertNoError(proc.Step(p), t, "Step()") _, ln := currentLineNumber(p, t) if ln != 5 { t.Fatalf("wrong line number after Step, expected 5 got %d", ln) @@ -2171,10 +2124,10 @@ func TestIssue561(t *testing.T) { } func TestStepOut(t *testing.T) { - withTestProcess("testnextprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testnextprog", t, func(p target.Interface, fixture protest.Fixture) { bp, err := setFunctionBreakpoint(p, "main.helloworld") assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") p.ClearBreakpoint(bp.Addr) f, lno := currentLineNumber(p, t) @@ -2182,7 +2135,7 @@ func TestStepOut(t *testing.T) { t.Fatalf("wrong line number %s:%d, expected %d", f, lno, 13) } - assertNoError(StepOut(p), t, "StepOut()") + assertNoError(proc.StepOut(p), t, "StepOut()") f, lno = currentLineNumber(p, t) if lno != 35 { @@ -2192,13 +2145,13 @@ func TestStepOut(t *testing.T) { } func TestStepConcurrentDirect(t *testing.T) { - withTestProcess("teststepconcurrent", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("teststepconcurrent", t, func(p target.Interface, fixture protest.Fixture) { pc, err := p.FindFileLocation(fixture.Source, 37) assertNoError(err, t, "FindFileLocation()") - bp, err := p.SetBreakpoint(pc, UserBreakpoint, nil) + bp, err := p.SetBreakpoint(pc, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") _, err = p.ClearBreakpoint(bp.Addr) assertNoError(err, t, "ClearBreakpoint()") @@ -2228,7 +2181,7 @@ func TestStepConcurrentDirect(t *testing.T) { // loop exited break } - frames, err := ThreadStacktrace(p.CurrentThread(), 20) + frames, err := proc.ThreadStacktrace(p.CurrentThread(), 20) if err != nil { t.Errorf("Could not get stacktrace of goroutine %d\n", p.SelectedGoroutine().ID) } else { @@ -2247,7 +2200,7 @@ func TestStepConcurrentDirect(t *testing.T) { if i == 0 { count++ } - assertNoError(Step(p), t, "Step()") + assertNoError(proc.Step(p), t, "Step()") } if count != 100 { @@ -2256,7 +2209,7 @@ func TestStepConcurrentDirect(t *testing.T) { }) } -func nextInProgress(p IProcess) bool { +func nextInProgress(p target.Interface) bool { for _, bp := range p.Breakpoints() { if bp.Internal() { return true @@ -2266,10 +2219,10 @@ func nextInProgress(p IProcess) bool { } func TestStepConcurrentPtr(t *testing.T) { - withTestProcess("teststepconcurrent", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("teststepconcurrent", t, func(p target.Interface, fixture protest.Fixture) { pc, err := p.FindFileLocation(fixture.Source, 24) assertNoError(err, t, "FindFileLocation()") - _, err = p.SetBreakpoint(pc, UserBreakpoint, nil) + _, err = p.SetBreakpoint(pc, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") for _, b := range p.Breakpoints() { @@ -2283,8 +2236,8 @@ func TestStepConcurrentPtr(t *testing.T) { kvals := map[int]int64{} count := 0 for { - err := Continue(p) - _, exited := err.(ProcessExitedError) + err := proc.Continue(p) + _, exited := err.(proc.ProcessExitedError) if exited { break } @@ -2313,12 +2266,12 @@ func TestStepConcurrentPtr(t *testing.T) { } kvals[gid] = k - assertNoError(Step(p), t, "Step()") + assertNoError(proc.Step(p), t, "Step()") for nextInProgress(p) { if p.SelectedGoroutine().ID == gid { t.Fatalf("step did not step into function call (but internal breakpoints still active?) (%d %d)", gid, p.SelectedGoroutine().ID) } - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") } if p.SelectedGoroutine().ID != gid { @@ -2345,12 +2298,12 @@ func TestStepConcurrentPtr(t *testing.T) { } func TestStepOutDefer(t *testing.T) { - withTestProcess("testnextdefer", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("testnextdefer", t, func(p target.Interface, fixture protest.Fixture) { pc, err := p.FindFileLocation(fixture.Source, 9) assertNoError(err, t, "FindFileLocation()") - bp, err := p.SetBreakpoint(pc, UserBreakpoint, nil) + bp, err := p.SetBreakpoint(pc, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") p.ClearBreakpoint(bp.Addr) f, lno := currentLineNumber(p, t) @@ -2358,7 +2311,7 @@ func TestStepOutDefer(t *testing.T) { t.Fatalf("worng line number %s:%d, expected %d", f, lno, 5) } - assertNoError(StepOut(p), t, "StepOut()") + assertNoError(proc.StepOut(p), t, "StepOut()") f, l, _ := p.BinInfo().PCToLine(currentPC(p, t)) if f == fixture.Source || l == 6 { @@ -2371,12 +2324,12 @@ func TestStepOutDeferReturnAndDirectCall(t *testing.T) { // StepOut should not step into a deferred function if it is called // directly, only if it is called through a panic. // Here we test the case where the function is called by a deferreturn - withTestProcess("defercall", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("defercall", t, func(p target.Interface, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture, 11) - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") p.ClearBreakpoint(bp.Addr) - assertNoError(StepOut(p), t, "StepOut()") + assertNoError(proc.StepOut(p), t, "StepOut()") f, ln := currentLineNumber(p, t) if ln != 28 { @@ -2385,14 +2338,16 @@ func TestStepOutDeferReturnAndDirectCall(t *testing.T) { }) } +const maxInstructionLength uint64 = 15 + func TestStepOnCallPtrInstr(t *testing.T) { - withTestProcess("teststepprog", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("teststepprog", t, func(p target.Interface, fixture protest.Fixture) { pc, err := p.FindFileLocation(fixture.Source, 10) assertNoError(err, t, "FindFileLocation()") - _, err = p.SetBreakpoint(pc, UserBreakpoint, nil) + _, err = p.SetBreakpoint(pc, proc.UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") found := false @@ -2404,7 +2359,7 @@ func TestStepOnCallPtrInstr(t *testing.T) { regs, err := p.CurrentThread().Registers(false) assertNoError(err, t, "Registers()") pc := regs.PC() - text, err := disassemble(p.CurrentThread(), regs, p.Breakpoints(), p.BinInfo(), pc, pc+maxInstructionLength) + text, err := proc.Disassemble(p, nil, pc, pc+maxInstructionLength) assertNoError(err, t, "Disassemble()") if text[0].IsCall() { found = true @@ -2417,7 +2372,7 @@ func TestStepOnCallPtrInstr(t *testing.T) { t.Fatal("Could not find CALL instruction") } - assertNoError(Step(p), t, "Step()") + assertNoError(proc.Step(p), t, "Step()") f, ln := currentLineNumber(p, t) if ln != 5 { @@ -2439,8 +2394,8 @@ func TestIssue594(t *testing.T) { // back to the target. // In particular the target should be able to cause a nil pointer // dereference panic and recover from it. - withTestProcess("issue594", t, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), t, "Continue()") + withTestProcess("issue594", t, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), t, "Continue()") f, ln := currentLineNumber(p, t) if ln != 21 { t.Fatalf("Program stopped at %s:%d, expected :21", f, ln) @@ -2452,12 +2407,12 @@ func TestStepOutPanicAndDirectCall(t *testing.T) { // StepOut should not step into a deferred function if it is called // directly, only if it is called through a panic. // Here we test the case where the function is called by a panic - withTestProcess("defercall", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("defercall", t, func(p target.Interface, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture, 17) - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") p.ClearBreakpoint(bp.Addr) - assertNoError(StepOut(p), t, "StepOut()") + assertNoError(proc.StepOut(p), t, "StepOut()") f, ln := currentLineNumber(p, t) if ln != 5 { @@ -2472,11 +2427,11 @@ func TestWorkDir(t *testing.T) { if runtime.GOOS == "darwin" { wd = "/private/tmp" } - withTestProcessArgs("workdir", t, wd, func(p IProcess, fixture protest.Fixture) { + withTestProcessArgs("workdir", t, wd, func(p target.Interface, fixture protest.Fixture) { addr, _, err := p.BinInfo().LineToPC(fixture.Source, 14) assertNoError(err, t, "LineToPC") - p.SetBreakpoint(addr, UserBreakpoint, nil) - Continue(p) + p.SetBreakpoint(addr, proc.UserBreakpoint, nil) + proc.Continue(p) v, err := evalVariable(p, "pwd") assertNoError(err, t, "EvalVariable") str := constant.StringVal(v.Value) @@ -2496,8 +2451,8 @@ func TestNegativeIntEvaluation(t *testing.T) { {"ni16", "int16", int64(-5)}, {"ni32", "int32", int64(-5)}, } - withTestProcess("testvariables2", t, func(p IProcess, fixture protest.Fixture) { - assertNoError(Continue(p), t, "Continue()") + withTestProcess("testvariables2", t, func(p target.Interface, fixture protest.Fixture) { + assertNoError(proc.Continue(p), t, "Continue()") for _, tc := range testcases { v, err := evalVariable(p, tc.name) assertNoError(err, t, "EvalVariable()") @@ -2513,14 +2468,14 @@ func TestNegativeIntEvaluation(t *testing.T) { func TestIssue683(t *testing.T) { // Step panics when source file can not be found - withTestProcess("issue683", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("issue683", t, func(p target.Interface, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.main") assertNoError(err, t, "setFunctionBreakpoint()") - assertNoError(Continue(p), t, "First Continue()") + assertNoError(proc.Continue(p), t, "First Continue()") for i := 0; i < 20; i++ { // eventually an error about the source file not being found will be // returned, the important thing is that we shouldn't panic - err := Step(p) + err := proc.Step(p) if err != nil { break } @@ -2529,10 +2484,10 @@ func TestIssue683(t *testing.T) { } func TestIssue664(t *testing.T) { - withTestProcess("issue664", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("issue664", t, func(p target.Interface, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture, 4) - assertNoError(Continue(p), t, "Continue()") - assertNoError(Next(p), t, "Next()") + assertNoError(proc.Continue(p), t, "Continue()") + assertNoError(proc.Next(p), t, "Next()") f, ln := currentLineNumber(p, t) if ln != 5 { t.Fatalf("Did not continue to line 5: %s:%d", f, ln) @@ -2542,15 +2497,15 @@ func TestIssue664(t *testing.T) { // Benchmarks (*Processs).Continue + (*Scope).FunctionArguments func BenchmarkTrace(b *testing.B) { - withTestProcess("traceperf", b, func(p IProcess, fixture protest.Fixture) { + withTestProcess("traceperf", b, func(p target.Interface, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.PerfCheck") assertNoError(err, b, "setFunctionBreakpoint()") b.ResetTimer() for i := 0; i < b.N; i++ { - assertNoError(Continue(p), b, "Continue()") - s, err := GoroutineScope(p.CurrentThread()) + assertNoError(proc.Continue(p), b, "Continue()") + s, err := proc.GoroutineScope(p.CurrentThread()) assertNoError(err, b, "Scope()") - _, err = s.FunctionArguments(LoadConfig{false, 0, 64, 0, 3}) + _, err = s.FunctionArguments(proc.LoadConfig{false, 0, 64, 0, 3}) assertNoError(err, b, "FunctionArguments()") } b.StopTimer() @@ -2562,17 +2517,17 @@ func TestNextInDeferReturn(t *testing.T) { // instruction leaves the curg._defer field non-nil but with curg._defer.fn // field being nil. // We need to deal with this without panicing. - withTestProcess("defercall", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("defercall", t, func(p target.Interface, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "runtime.deferreturn") assertNoError(err, t, "setFunctionBreakpoint()") - assertNoError(Continue(p), t, "First Continue()") + assertNoError(proc.Continue(p), t, "First Continue()") for i := 0; i < 20; i++ { - assertNoError(Next(p), t, fmt.Sprintf("Next() %d", i)) + assertNoError(proc.Next(p), t, fmt.Sprintf("Next() %d", i)) } }) } -func getg(goid int, gs []*G) *G { +func getg(goid int, gs []*proc.G) *proc.G { for _, g := range gs { if g.ID == goid { return g @@ -2589,7 +2544,7 @@ func TestStacktraceWithBarriers(t *testing.T) { // struct. // In Go 1.9 stack barriers have been removed and this test must be disabled. - if ver, _ := ParseVersionString(runtime.Version()); ver.Major < 0 || ver.AfterOrEqual(GoVersion{1, 9, -1, 0, 0}) { + if ver, _ := proc.ParseVersionString(runtime.Version()); ver.Major < 0 || ver.AfterOrEqual(proc.GoVersion{1, 9, -1, 0, 0}) { return } @@ -2598,19 +2553,19 @@ func TestStacktraceWithBarriers(t *testing.T) { defer os.Setenv("GODEBUG", godebugOld) os.Setenv("GODEBUG", "gcrescanstacks=1") - withTestProcess("binarytrees", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("binarytrees", t, func(p target.Interface, fixture protest.Fixture) { // We want to get a user goroutine with a stack barrier, to get that we execute the program until runtime.gcInstallStackBarrier is executed AND the goroutine it was executed onto contains a call to main.bottomUpTree _, err := setFunctionBreakpoint(p, "runtime.gcInstallStackBarrier") assertNoError(err, t, "setFunctionBreakpoint()") stackBarrierGoids := []int{} for len(stackBarrierGoids) == 0 { - err := Continue(p) - if _, exited := err.(ProcessExitedError); exited { + err := proc.Continue(p) + if _, exited := err.(proc.ProcessExitedError); exited { t.Logf("Could not run test") return } assertNoError(err, t, "Continue()") - gs, err := GoroutinesInfo(p) + gs, err := proc.GoroutinesInfo(p) assertNoError(err, t, "GoroutinesInfo()") for _, th := range p.ThreadList() { if bp, _, _ := th.Breakpoint(); bp == nil { @@ -2640,9 +2595,9 @@ func TestStacktraceWithBarriers(t *testing.T) { t.Logf("stack barrier goids: %v\n", stackBarrierGoids) - assertNoError(StepOut(p), t, "StepOut()") + assertNoError(proc.StepOut(p), t, "StepOut()") - gs, err := GoroutinesInfo(p) + gs, err := proc.GoroutinesInfo(p) assertNoError(err, t, "GoroutinesInfo()") for _, goid := range stackBarrierGoids { @@ -2709,18 +2664,18 @@ func TestAttachDetach(t *testing.T) { } } - var p IProcess + var p target.Interface var err error switch testBackend { case "native": - p, err = Attach(cmd.Process.Pid) + p, err = native.Attach(cmd.Process.Pid) case "lldb": path := "" if runtime.GOOS == "darwin" { path = fixture.Path } - p, err = LLDBAttach(cmd.Process.Pid, path) + p, err = gdbserial.LLDBAttach(cmd.Process.Pid, path) default: err = fmt.Errorf("unknown backend %q", testBackend) } @@ -2731,7 +2686,7 @@ func TestAttachDetach(t *testing.T) { http.Get("http://localhost:9191") }() - assertNoError(Continue(p), t, "Continue") + assertNoError(proc.Continue(p), t, "Continue") f, ln := currentLineNumber(p, t) if ln != 11 { diff --git a/pkg/proc/proc_unix_test.go b/pkg/proc/proc_unix_test.go index 5e4bcffc..c21bedeb 100644 --- a/pkg/proc/proc_unix_test.go +++ b/pkg/proc/proc_unix_test.go @@ -1,14 +1,16 @@ // +build linux darwin -package proc +package proc_test import ( + "runtime" "syscall" "testing" "time" - "runtime" + "github.com/derekparker/delve/pkg/proc" protest "github.com/derekparker/delve/pkg/proc/test" + "github.com/derekparker/delve/pkg/target" ) func TestIssue419(t *testing.T) { @@ -17,10 +19,10 @@ func TestIssue419(t *testing.T) { return } // SIGINT directed at the inferior should be passed along not swallowed by delve - withTestProcess("issue419", t, func(p IProcess, fixture protest.Fixture) { + withTestProcess("issue419", t, func(p target.Interface, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.main") assertNoError(err, t, "SetBreakpoint()") - assertNoError(Continue(p), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") go func() { for { time.Sleep(500 * time.Millisecond) @@ -39,8 +41,8 @@ func TestIssue419(t *testing.T) { } } }() - err = Continue(p) - if _, exited := err.(ProcessExitedError); !exited { + err = proc.Continue(p) + if _, exited := err.(proc.ProcessExitedError); !exited { t.Fatalf("Unexpected error after Continue(): %v\n", err) } }) diff --git a/pkg/proc/registers.go b/pkg/proc/registers.go index b1e14c66..01975f43 100644 --- a/pkg/proc/registers.go +++ b/pkg/proc/registers.go @@ -32,15 +32,18 @@ type Register struct { Value string } -func appendWordReg(regs []Register, name string, value uint16) []Register { +// AppendWordReg appends a word (16 bit) register to regs. +func AppendWordReg(regs []Register, name string, value uint16) []Register { return append(regs, Register{name, fmt.Sprintf("%#04x", value)}) } -func appendDwordReg(regs []Register, name string, value uint32) []Register { +// AppendDwordReg appends a double word (32 bit) register to regs. +func AppendDwordReg(regs []Register, name string, value uint32) []Register { return append(regs, Register{name, fmt.Sprintf("%#08x", value)}) } -func appendQwordReg(regs []Register, name string, value uint64) []Register { +// AppendQwordReg appends a quad word (64 bit) register to regs. +func AppendQwordReg(regs []Register, name string, value uint64) []Register { return append(regs, Register{name, fmt.Sprintf("%#016x", value)}) } @@ -48,7 +51,18 @@ func appendFlagReg(regs []Register, name string, value uint64, descr flagRegiste return append(regs, Register{name, descr.Describe(value, size)}) } -func appendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64) []Register { +// AppendEflagReg appends EFLAG register to regs. +func AppendEflagReg(regs []Register, name string, value uint64) []Register { + return appendFlagReg(regs, name, value, eflagsDescription, 64) +} + +// AppendMxcsrReg appends MXCSR register to regs. +func AppendMxcsrReg(regs []Register, name string, value uint64) []Register { + return appendFlagReg(regs, name, value, mxcsrDescription, 32) +} + +// AppendX87Reg appends a 80 bit float register to regs. +func AppendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64) []Register { var f float64 fset := false @@ -103,7 +117,8 @@ func appendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64) return append(regs, Register{fmt.Sprintf("ST(%d)", index), fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)}) } -func appendSSEReg(regs []Register, name string, xmm []byte) []Register { +// AppendSSEReg appends a 256 bit SSE register to regs. +func AppendSSEReg(regs []Register, name string, xmm []byte) []Register { buf := bytes.NewReader(xmm) var out bytes.Buffer @@ -141,20 +156,6 @@ func appendSSEReg(regs []Register, name string, xmm []byte) []Register { var UnknownRegisterError = errors.New("unknown register") -// Registers obtains register values from the debugged process. -func (t *Thread) Registers(floatingPoint bool) (Registers, error) { - return registers(t, floatingPoint) -} - -// PC returns the current PC for this thread. -func (t *Thread) PC() (uint64, error) { - regs, err := t.Registers(false) - if err != nil { - return 0, err - } - return regs.PC(), nil -} - type flagRegisterDescr []flagDescr type flagDescr struct { name string diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go index 5f3af8a0..a1f64938 100644 --- a/pkg/proc/stack.go +++ b/pkg/proc/stack.go @@ -15,11 +15,11 @@ const runtimeStackBarrier = "runtime.stackBarrier" // NoReturnAddr is returned when return address // could not be found during stack trace. type NoReturnAddr struct { - fn string + Fn string } func (nra NoReturnAddr) Error() string { - return fmt.Sprintf("could not find return address for %s", nra.fn) + return fmt.Sprintf("could not find return address for %s", nra.Fn) } // Stackframe represents a frame in a system stack. @@ -54,12 +54,12 @@ func (g *G) stackIterator() (*stackIterator, error) { if err != nil { return nil, err } - if g.thread != nil { - regs, err := g.thread.Registers(false) + if g.Thread != nil { + regs, err := g.Thread.Registers(false) if err != nil { return nil, err } - return newStackIterator(g.variable.bi, g.thread, regs.PC(), regs.SP(), regs.BP(), stkbar, g.stkbarPos), nil + return newStackIterator(g.variable.bi, g.Thread, regs.PC(), regs.SP(), regs.BP(), stkbar, g.stkbarPos), nil } return newStackIterator(g.variable.bi, g.variable.mem, g.PC, g.SP, 0, stkbar, g.stkbarPos), nil } @@ -90,7 +90,7 @@ type stackIterator struct { atend bool frame Stackframe bi *BinaryInfo - mem memoryReadWriter + mem MemoryReadWriter err error stackBarrierPC uint64 @@ -102,7 +102,7 @@ type savedLR struct { val uint64 } -func newStackIterator(bi *BinaryInfo, mem memoryReadWriter, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator { +func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator { stackBarrierFunc := bi.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9 var stackBarrierPC uint64 if stackBarrierFunc != nil && stkbar != nil { @@ -161,7 +161,7 @@ func (it *stackIterator) Next() bool { it.top = false it.pc = it.frame.Ret it.sp = uint64(it.frame.CFA) - it.bp, _ = readUintRaw(it.mem, uintptr(it.bp), int64(it.bi.arch.PtrSize())) + it.bp, _ = readUintRaw(it.mem, uintptr(it.bp), int64(it.bi.Arch.PtrSize())) return true } @@ -185,8 +185,8 @@ func (it *stackIterator) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, err return Stackframe{}, err } // When no FDE is available attempt to use BP instead - retaddr := uintptr(int(bp) + it.bi.arch.PtrSize()) - cfa := int64(retaddr) + int64(it.bi.arch.PtrSize()) + retaddr := uintptr(int(bp) + it.bi.Arch.PtrSize()) + cfa := int64(retaddr) + int64(it.bi.Arch.PtrSize()) return it.newStackframe(pc, cfa, retaddr, nil, top) } @@ -202,7 +202,7 @@ func (it *stackIterator) newStackframe(pc uint64, cfa int64, retaddr uintptr, fd return Stackframe{}, NullAddrError{} } f, l, fn := it.bi.PCToLine(pc) - ret, err := readUintRaw(it.mem, retaddr, int64(it.bi.arch.PtrSize())) + ret, err := readUintRaw(it.mem, retaddr, int64(it.bi.Arch.PtrSize())) if err != nil { return Stackframe{}, err } diff --git a/pkg/proc/threads.go b/pkg/proc/threads.go index 8a501ccf..bf565674 100644 --- a/pkg/proc/threads.go +++ b/pkg/proc/threads.go @@ -13,27 +13,9 @@ import ( "golang.org/x/debug/dwarf" ) -// Thread represents a single thread in the traced process -// ID represents the thread id or port, Process holds a reference to the -// Process struct that contains info on the process as -// a whole, and Status represents the last result of a `wait` call -// on this thread. -type Thread struct { - ID int // Thread ID or mach port - Status *WaitStatus // Status returned from last wait call - CurrentBreakpoint *Breakpoint // Breakpoint thread is currently stopped at - BreakpointConditionMet bool // Output of evaluating the breakpoint's condition - BreakpointConditionError error // Error evaluating the breakpoint's condition - - dbp *Process - singleStepping bool - running bool - os *OSSpecificDetails -} - // IThread represents a thread. type IThread interface { - memoryReadWriter + MemoryReadWriter Location() (*Location, error) // Breakpoint will return the breakpoint that this thread is stopped at or // nil if the thread is not stopped at any breakpoint. @@ -47,6 +29,8 @@ type IThread interface { Arch() Arch BinInfo() *BinaryInfo StepInstruction() error + // Blocked returns true if the thread is blocked + Blocked() bool } // Location represents the location of a thread. @@ -59,88 +43,6 @@ type Location struct { Fn *gosym.Func } -// Continue the execution of this thread. -// -// If we are currently at a breakpoint, we'll clear it -// first and then resume execution. Thread will continue until -// it hits a breakpoint or is signaled. -func (thread *Thread) Continue() error { - pc, err := thread.PC() - if err != nil { - return err - } - // Check whether we are stopped at a breakpoint, and - // if so, single step over it before continuing. - if _, ok := thread.dbp.FindBreakpoint(pc); ok { - if err := thread.StepInstruction(); err != nil { - return err - } - } - return thread.resume() -} - -// StepInstruction steps a single instruction. -// -// Executes exactly one instruction and then returns. -// If the thread is at a breakpoint, we first clear it, -// execute the instruction, and then replace the breakpoint. -// Otherwise we simply execute the next instruction. -func (thread *Thread) StepInstruction() (err error) { - thread.running = true - thread.singleStepping = true - defer func() { - thread.singleStepping = false - thread.running = false - }() - pc, err := thread.PC() - if err != nil { - return err - } - - bp, ok := thread.dbp.FindBreakpoint(pc) - if ok { - // Clear the breakpoint so that we can continue execution. - _, err = thread.ClearBreakpoint(bp) - if err != nil { - return err - } - - // Restore breakpoint now that we have passed it. - defer func() { - err = thread.dbp.writeSoftwareBreakpoint(thread, bp.Addr) - }() - } - - err = thread.singleStep() - if err != nil { - if _, exited := err.(ProcessExitedError); exited { - return err - } - return fmt.Errorf("step failed: %s", err.Error()) - } - return nil -} - -// Location returns the threads location, including the file:line -// of the corresponding source code, the function we're in -// and the current instruction address. -func (thread *Thread) Location() (*Location, error) { - pc, err := thread.PC() - if err != nil { - return nil, err - } - f, l, fn := thread.dbp.bi.PCToLine(pc) - return &Location{PC: pc, File: f, Line: l, Fn: fn}, nil -} - -func (thread *Thread) Arch() Arch { - return thread.dbp.bi.arch -} - -func (thread *Thread) BinInfo() *BinaryInfo { - return &thread.dbp.bi -} - // ThreadBlockedError is returned when the thread // is blocked in the scheduler. type ThreadBlockedError struct{} @@ -155,7 +57,7 @@ func topframe(g *G, thread IThread) (Stackframe, error) { var err error if g == nil { - if threadBlocked(thread) { + if thread.Blocked() { return Stackframe{}, ThreadBlockedError{} } frames, err = ThreadStacktrace(thread, 0) @@ -194,11 +96,11 @@ func next(dbp Continuable, stepInto bool) error { }() csource := filepath.Ext(topframe.Current.File) != ".go" - var thread memoryReadWriter = curthread + var thread MemoryReadWriter = curthread var regs Registers - if selg != nil && selg.Thread() != nil { - thread = selg.Thread() - regs, err = selg.Thread().Registers(false) + if selg != nil && selg.Thread != nil { + thread = selg.Thread + regs, err = selg.Thread.Registers(false) if err != nil { return err } @@ -215,7 +117,7 @@ func next(dbp Continuable, stepInto bool) error { } } - cond := sameGoroutineCondition(selg) + cond := SameGoroutineCondition(selg) if stepInto { for _, instr := range text { @@ -361,15 +263,6 @@ func setInternalBreakpoints(dbp Continuable, curpc uint64, pcs []uint64, kind Br return nil } -// SetPC sets the PC for this thread. -func (thread *Thread) SetPC(pc uint64) error { - regs, err := thread.Registers(false) - if err != nil { - return err - } - return regs.SetPC(thread, pc) -} - func getGVariable(thread IThread) (*Variable, error) { arch := thread.Arch() regs, err := thread.Registers(false) @@ -435,7 +328,7 @@ func GetG(thread IThread) (g *G, err error) { g, err = gaddr.parseG() if err == nil { - g.thread = thread + g.Thread = thread if loc, err := thread.Location(); err == nil { g.CurrentLoc = *loc } @@ -443,29 +336,6 @@ func GetG(thread IThread) (g *G, err error) { return } -// Stopped returns whether the thread is stopped at -// the operating system level. Actual implementation -// is OS dependant, look in OS thread file. -func (thread *Thread) Stopped() bool { - return thread.stopped() -} - -// Halt stops this thread from executing. Actual -// implementation is OS dependant. Look in OS -// thread file. -func (thread *Thread) Halt() (err error) { - defer func() { - if err == nil { - thread.running = false - } - }() - if thread.Stopped() { - return - } - err = thread.halt() - return -} - // ThreadScope returns an EvalScope for this thread. func ThreadScope(thread IThread) (*EvalScope, error) { locations, err := ThreadStacktrace(thread, 0) @@ -494,36 +364,6 @@ func GoroutineScope(thread IThread) (*EvalScope, error) { return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, gvar, thread.BinInfo()}, nil } -// SetCurrentBreakpoint sets the current breakpoint that this -// thread is stopped at as CurrentBreakpoint on the thread struct. -func (thread *Thread) SetCurrentBreakpoint() error { - thread.CurrentBreakpoint = nil - pc, err := thread.PC() - if err != nil { - return err - } - if bp, ok := thread.dbp.FindBreakpoint(pc); ok { - thread.CurrentBreakpoint = bp - if err = thread.SetPC(bp.Addr); err != nil { - return err - } - thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.checkCondition(thread) - if thread.CurrentBreakpoint != nil && thread.BreakpointConditionMet { - if g, err := GetG(thread); err == nil { - thread.CurrentBreakpoint.HitCount[g.ID]++ - } - thread.CurrentBreakpoint.TotalHitCount++ - } - } - return nil -} - -func (thread *Thread) clearBreakpointState() { - thread.CurrentBreakpoint = nil - thread.BreakpointConditionMet = false - thread.BreakpointConditionError = nil -} - func onRuntimeBreakpoint(thread IThread) bool { loc, err := thread.Location() if err != nil { @@ -551,13 +391,5 @@ func onNextGoroutine(thread IThread, breakpoints map[uint64]*Breakpoint) (bool, bp.Kind = NextDeferBreakpoint }() } - return bp.checkCondition(thread) -} - -func (th *Thread) Breakpoint() (*Breakpoint, bool, error) { - return th.CurrentBreakpoint, (th.CurrentBreakpoint != nil && th.BreakpointConditionMet), th.BreakpointConditionError -} - -func (th *Thread) ThreadID() int { - return th.ID + return bp.CheckCondition(thread) } diff --git a/pkg/proc/types.go b/pkg/proc/types.go index 3fe7fc3b..32aea044 100644 --- a/pkg/proc/types.go +++ b/pkg/proc/types.go @@ -69,7 +69,7 @@ func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (dwarf.Type, error) { if err != nil { return nil, err } - return pointerTo(ptyp, bi.arch), nil + return pointerTo(ptyp, bi.Arch), nil } return bi.findType(exprToString(expr)) } @@ -380,7 +380,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string if err != nil { return "", err } - prtyp := pointerTo(rtyp, _type.bi.arch) + prtyp := pointerTo(rtyp, _type.bi.Arch) uadd := _type.RealType.Common().ByteSize if ut := uncommon(_type, tflag); ut != nil { @@ -407,7 +407,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string for i := int64(0); i < inCount; i++ { argtype := cursortyp.maybeDereference() - cursortyp.Addr += uintptr(_type.bi.arch.PtrSize()) + cursortyp.Addr += uintptr(_type.bi.Arch.PtrSize()) argtypename, _, err := nameOfRuntimeType(argtype) if err != nil { return "", err @@ -434,7 +434,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string buf.WriteString(" (") for i := int64(0); i < outCount; i++ { argtype := cursortyp.maybeDereference() - cursortyp.Addr += uintptr(_type.bi.arch.PtrSize()) + cursortyp.Addr += uintptr(_type.bi.Arch.PtrSize()) argtypename, _, err := nameOfRuntimeType(argtype) if err != nil { return "", err @@ -582,7 +582,7 @@ func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) { if err != nil { return nil, err } - prtyp := pointerTo(rtyp, _type.bi.arch) + prtyp := pointerTo(rtyp, _type.bi.Arch) uintptrtyp, err := _type.bi.findType("uintptr") if err != nil { @@ -602,7 +602,7 @@ func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) { newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType { r := newStructType("[]"+elemtype.Common().Name, uintptr(3*uintptrtyp.Size())) - appendField(r, "array", pointerTo(elemtype, _type.bi.arch), 0) + appendField(r, "array", pointerTo(elemtype, _type.bi.Arch), 0) appendField(r, "len", uintptrtyp, uintptr(uintptrtyp.Size())) appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size())) return &dwarf.SliceType{StructType: *r, ElemType: elemtype} diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index 12af9f94..5f9f2b10 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -50,7 +50,7 @@ type Variable struct { DwarfType dwarf.Type RealType dwarf.Type Kind reflect.Kind - mem memoryReadWriter + mem MemoryReadWriter bi *BinaryInfo Value constant.Value @@ -129,7 +129,7 @@ type G struct { CurrentLoc Location // Thread that this goroutine is currently allocated to - thread IThread + Thread IThread variable *Variable } @@ -137,11 +137,11 @@ type G struct { // EvalScope is the scope for variable evaluation. Contains the thread, // current location (PC), and canonical frame address. type EvalScope struct { - PC uint64 // Current instruction of the evaluation frame - CFA int64 // Stack address of the evaluation frame - Mem memoryReadWriter // Target's memory - gvar *Variable - bi *BinaryInfo + PC uint64 // Current instruction of the evaluation frame + CFA int64 // Stack address of the evaluation frame + Mem MemoryReadWriter // Target's memory + Gvar *Variable + BinInfo *BinaryInfo } // IsNilErr is returned when a variable is nil. @@ -154,7 +154,7 @@ func (err *IsNilErr) Error() string { } func (scope *EvalScope) newVariable(name string, addr uintptr, dwarfType dwarf.Type) *Variable { - return newVariable(name, addr, dwarfType, scope.bi, scope.Mem) + return newVariable(name, addr, dwarfType, scope.BinInfo, scope.Mem) } func newVariableFromThread(t IThread, name string, addr uintptr, dwarfType dwarf.Type) *Variable { @@ -165,7 +165,7 @@ func (v *Variable) newVariable(name string, addr uintptr, dwarfType dwarf.Type) return newVariable(name, addr, dwarfType, v.bi, v.mem) } -func newVariable(name string, addr uintptr, dwarfType dwarf.Type, bi *BinaryInfo, mem memoryReadWriter) *Variable { +func newVariable(name string, addr uintptr, dwarfType dwarf.Type, bi *BinaryInfo, mem MemoryReadWriter) *Variable { v := &Variable{ Name: name, Addr: addr, @@ -191,7 +191,7 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, bi *BinaryInfo v.stride = 1 v.fieldType = &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}} if v.Addr != 0 { - v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.bi.arch, v.Addr) + v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.bi.Arch, v.Addr) } case *dwarf.SliceType: v.Kind = reflect.Slice @@ -256,7 +256,7 @@ func resolveTypedef(typ dwarf.Type) dwarf.Type { } } -func newConstant(val constant.Value, mem memoryReadWriter) *Variable { +func newConstant(val constant.Value, mem MemoryReadWriter) *Variable { v := &Variable{Value: val, mem: mem, loaded: true} switch val.Kind() { case constant.Int: @@ -322,33 +322,23 @@ func (v *Variable) toField(field *dwarf.StructField) (*Variable, error) { // DwarfReader returns the DwarfReader containing the // Dwarf information for the target process. func (scope *EvalScope) DwarfReader() *reader.Reader { - return scope.bi.DwarfReader() + return scope.BinInfo.DwarfReader() } // Type returns the Dwarf type entry at `offset`. func (scope *EvalScope) Type(offset dwarf.Offset) (dwarf.Type, error) { - return scope.bi.dwarf.Type(offset) + return scope.BinInfo.dwarf.Type(offset) } // PtrSize returns the size of a pointer. func (scope *EvalScope) PtrSize() int { - return scope.bi.arch.PtrSize() + return scope.BinInfo.Arch.PtrSize() } // ChanRecvBlocked returns whether the goroutine is blocked on // a channel read operation. func (g *G) ChanRecvBlocked() bool { - return (g.thread == nil) && (g.WaitReason == chanRecv) -} - -// chanRecvReturnAddr returns the address of the return from a channel read. -func (g *G) chanRecvReturnAddr(dbp *Process) (uint64, error) { - locs, err := g.Stacktrace(4) - if err != nil { - return 0, err - } - topLoc := locs[len(locs)-1] - return topLoc.Current.PC, nil + return (g.Thread == nil) && (g.WaitReason == chanRecv) } // NoGError returned when a G could not be found @@ -367,7 +357,7 @@ func (gvar *Variable) parseG() (*G, error) { _, deref := gvar.RealType.(*dwarf.PtrType) if deref { - gaddrbytes := make([]byte, gvar.bi.arch.PtrSize()) + gaddrbytes := make([]byte, gvar.bi.Arch.PtrSize()) _, err := mem.ReadMemory(gaddrbytes, uintptr(gaddr)) if err != nil { return nil, fmt.Errorf("error derefing *G %s", err) @@ -376,8 +366,8 @@ func (gvar *Variable) parseG() (*G, error) { } if gaddr == 0 { id := 0 - if thread, ok := mem.(*Thread); ok { - id = thread.ID + if thread, ok := mem.(IThread); ok { + id = thread.ThreadID() } return nil, NoGError{tid: id} } @@ -586,7 +576,7 @@ func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry, cfg LoadCon func (scope *EvalScope) extractVarInfo(varName string) (*Variable, error) { reader := scope.DwarfReader() - off, err := scope.bi.findFunctionDebugInfo(scope.PC) + off, err := scope.BinInfo.findFunctionDebugInfo(scope.PC) if err != nil { return nil, err } @@ -655,19 +645,6 @@ func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) { return vars, nil } -// EvalPackageVariable will evaluate the package level variable -// specified by 'name'. -func (dbp *Process) EvalPackageVariable(name string, cfg LoadConfig) (*Variable, error) { - scope := &EvalScope{0, 0, dbp.currentThread, nil, dbp.BinInfo()} - - v, err := scope.packageVarAddr(name) - if err != nil { - return nil, err - } - v.loadValue(cfg) - return v, nil -} - func (scope *EvalScope) packageVarAddr(name string) (*Variable, error) { reader := scope.DwarfReader() for entry, err := reader.NextPackageVariable(); entry != nil; entry, err = reader.NextPackageVariable() { @@ -941,7 +918,7 @@ func (v *Variable) setValue(y *Variable) error { return err } -func readStringInfo(mem memoryReadWriter, arch Arch, addr uintptr) (uintptr, int64, error) { +func readStringInfo(mem MemoryReadWriter, arch Arch, addr uintptr) (uintptr, int64, error) { // string data structure is always two ptrs in size. Addr, followed by len // http://research.swtch.com/godata @@ -971,7 +948,7 @@ func readStringInfo(mem memoryReadWriter, arch Arch, addr uintptr) (uintptr, int return addr, strlen, nil } -func readStringValue(mem memoryReadWriter, addr uintptr, strlen int64, cfg LoadConfig) (string, error) { +func readStringValue(mem MemoryReadWriter, addr uintptr, strlen int64, cfg LoadConfig) (string, error) { count := strlen if count > int64(cfg.MaxStringLen) { count = int64(cfg.MaxStringLen) @@ -1103,7 +1080,7 @@ func (v *Variable) writeComplex(real, imag float64, size int64) error { return imagaddr.writeFloatRaw(imag, int64(size/2)) } -func readIntRaw(mem memoryReadWriter, addr uintptr, size int64) (int64, error) { +func readIntRaw(mem MemoryReadWriter, addr uintptr, size int64) (int64, error) { var n int64 val := make([]byte, int(size)) @@ -1140,11 +1117,11 @@ func (v *Variable) writeUint(value uint64, size int64) error { binary.LittleEndian.PutUint64(val, uint64(value)) } - _, err := v.mem.writeMemory(v.Addr, val) + _, err := v.mem.WriteMemory(v.Addr, val) return err } -func readUintRaw(mem memoryReadWriter, addr uintptr, size int64) (uint64, error) { +func readUintRaw(mem MemoryReadWriter, addr uintptr, size int64) (uint64, error) { var n uint64 val := make([]byte, int(size)) @@ -1201,19 +1178,19 @@ func (v *Variable) writeFloatRaw(f float64, size int64) error { binary.Write(buf, binary.LittleEndian, n) } - _, err := v.mem.writeMemory(v.Addr, buf.Bytes()) + _, err := v.mem.WriteMemory(v.Addr, buf.Bytes()) return err } func (v *Variable) writeBool(value bool) error { val := []byte{0} val[0] = *(*byte)(unsafe.Pointer(&value)) - _, err := v.mem.writeMemory(v.Addr, val) + _, err := v.mem.WriteMemory(v.Addr, val) return err } func (v *Variable) readFunctionPtr() { - val := make([]byte, v.bi.arch.PtrSize()) + val := make([]byte, v.bi.Arch.PtrSize()) _, err := v.mem.ReadMemory(val, v.Addr) if err != nil { v.Unreadable = err @@ -1644,7 +1621,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig if kind&kindDirectIface == 0 { realtyp := resolveTypedef(typ) if _, isptr := realtyp.(*dwarf.PtrType); !isptr { - typ = pointerTo(typ, v.bi.arch) + typ = pointerTo(typ, v.bi.Arch) } } @@ -1662,7 +1639,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig // Fetches all variables of a specific type in the current function scope func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) { reader := scope.DwarfReader() - off, err := scope.bi.findFunctionDebugInfo(scope.PC) + off, err := scope.BinInfo.findFunctionDebugInfo(scope.PC) if err != nil { return nil, err } diff --git a/pkg/target/target.go b/pkg/target/target.go index f768a621..5e99f400 100644 --- a/pkg/target/target.go +++ b/pkg/target/target.go @@ -5,6 +5,9 @@ import ( "go/ast" "github.com/derekparker/delve/pkg/proc" + "github.com/derekparker/delve/pkg/proc/core" + "github.com/derekparker/delve/pkg/proc/gdbserial" + "github.com/derekparker/delve/pkg/proc/native" ) // Target represents the target of the debugger. This @@ -89,6 +92,6 @@ type VariableEval interface { ConvertEvalScope(gid, frame int) (*proc.EvalScope, error) } -var _ Interface = &proc.Process{} -var _ Interface = &proc.CoreProcess{} -var _ Interface = &proc.GdbserverProcess{} +var _ Interface = &native.Process{} +var _ Interface = &core.CoreProcess{} +var _ Interface = &gdbserial.GdbserverProcess{} diff --git a/service/api/conversions.go b/service/api/conversions.go index ddada85e..35fbdce7 100644 --- a/service/api/conversions.go +++ b/service/api/conversions.go @@ -201,7 +201,7 @@ func ConvertFunction(fn *gosym.Func) *Function { // ConvertGoroutine converts from proc.G to api.Goroutine. func ConvertGoroutine(g *proc.G) *Goroutine { - th := g.Thread() + th := g.Thread tid := 0 if th != nil { tid = th.ThreadID() diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index a10325b5..f9ce1957 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -14,6 +14,9 @@ import ( "time" "github.com/derekparker/delve/pkg/proc" + "github.com/derekparker/delve/pkg/proc/core" + "github.com/derekparker/delve/pkg/proc/gdbserial" + "github.com/derekparker/delve/pkg/proc/native" "github.com/derekparker/delve/pkg/target" "github.com/derekparker/delve/service/api" ) @@ -77,7 +80,7 @@ func New(config *Config) (*Debugger, error) { case d.config.CoreFile != "": log.Printf("opening core file %s (executable %s)", d.config.CoreFile, d.config.ProcessArgs[0]) - p, err := proc.OpenCore(d.config.CoreFile, d.config.ProcessArgs[0]) + p, err := core.OpenCore(d.config.CoreFile, d.config.ProcessArgs[0]) if err != nil { return nil, err } @@ -100,14 +103,14 @@ func New(config *Config) (*Debugger, error) { func (d *Debugger) Launch(processArgs []string, wd string) (target.Interface, error) { switch d.config.Backend { case "native": - return proc.Launch(processArgs, wd) + return native.Launch(processArgs, wd) case "lldb": - return proc.LLDBLaunch(processArgs, wd) + return gdbserial.LLDBLaunch(processArgs, wd) case "default": if runtime.GOOS == "darwin" { - return proc.LLDBLaunch(processArgs, wd) + return gdbserial.LLDBLaunch(processArgs, wd) } - return proc.Launch(processArgs, wd) + return native.Launch(processArgs, wd) default: return nil, fmt.Errorf("unknown backend %q", d.config.Backend) } @@ -121,20 +124,20 @@ var ErrNoAttachPath = errors.New("must specify executable path on macOS") func (d *Debugger) Attach(pid int, path string) (target.Interface, error) { switch d.config.Backend { case "native": - return proc.Attach(pid) + return native.Attach(pid) case "lldb": if runtime.GOOS == "darwin" && path == "" { return nil, ErrNoAttachPath } - return proc.LLDBAttach(pid, path) + return gdbserial.LLDBAttach(pid, path) case "default": if runtime.GOOS == "darwin" { if path == "" { return nil, ErrNoAttachPath } - return proc.LLDBAttach(pid, path) + return gdbserial.LLDBAttach(pid, path) } - return proc.Attach(pid) + return native.Attach(pid) default: return nil, fmt.Errorf("unknown backend %q", d.config.Backend) } diff --git a/service/test/variables_test.go b/service/test/variables_test.go index 133678e5..ed7af1d7 100644 --- a/service/test/variables_test.go +++ b/service/test/variables_test.go @@ -8,6 +8,8 @@ import ( "testing" "github.com/derekparker/delve/pkg/proc" + "github.com/derekparker/delve/pkg/proc/gdbserial" + "github.com/derekparker/delve/pkg/proc/native" "github.com/derekparker/delve/pkg/target" "github.com/derekparker/delve/service/api" @@ -84,9 +86,9 @@ func withTestProcess(name string, t *testing.T, fn func(p target.Interface, fixt var err error switch testBackend { case "native": - p, err = proc.Launch([]string{fixture.Path}, ".") + p, err = native.Launch([]string{fixture.Path}, ".") case "lldb": - p, err = proc.LLDBLaunch([]string{fixture.Path}, ".") + p, err = gdbserial.LLDBLaunch([]string{fixture.Path}, ".") default: t.Fatalf("unknown backend %q", testBackend) }