proc: refactoring: split backends to separate packages

- move native backend to pkg/proc/native
- move gdbserver backend to pkg/proc/gdbserial
- move core dumps backend to pkg/proc/core
This commit is contained in:
aarzilli 2017-04-21 08:55:53 +02:00 committed by Derek Parker
parent 92dad944d7
commit 15bac71979
55 changed files with 1571 additions and 1517 deletions

@ -22,13 +22,13 @@ import (
type BinaryInfo struct { type BinaryInfo struct {
lastModified time.Time // Time the executable of this process was last modified lastModified time.Time // Time the executable of this process was last modified
goos string GOOS string
closer io.Closer closer io.Closer
// Maps package names to package paths, needed to lookup types inside DWARF info // Maps package names to package paths, needed to lookup types inside DWARF info
packageMap map[string]string packageMap map[string]string
arch Arch Arch Arch
dwarf *dwarf.Data dwarf *dwarf.Data
frameEntries frame.FrameDescriptionEntries frameEntries frame.FrameDescriptionEntries
lineInfo line.DebugLines 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") var UnsupportedDarwinArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported")
func NewBinaryInfo(goos, goarch string) BinaryInfo { 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) // TODO: find better way to determine proc arch (perhaps use executable file info)
switch goarch { switch goarch {
case "amd64": case "amd64":
r.arch = AMD64Arch(goos) r.Arch = AMD64Arch(goos)
} }
return r return r
@ -63,7 +63,7 @@ func (bininfo *BinaryInfo) LoadBinaryInfo(path string, wg *sync.WaitGroup) error
bininfo.lastModified = fi.ModTime() bininfo.lastModified = fi.ModTime()
} }
switch bininfo.goos { switch bininfo.GOOS {
case "linux": case "linux":
return bininfo.LoadBinaryInfoElf(path, wg) return bininfo.LoadBinaryInfoElf(path, wg)
case "windows": case "windows":
@ -112,6 +112,11 @@ func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *gosy
return bi.goSymTable.LineToPC(filename, lineno) 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 { func (bi *BinaryInfo) Close() error {
return bi.closer.Close() return bi.closer.Close()
} }

@ -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) 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 // BreakpointExistsError is returned when trying to set a breakpoint at
// an address that already has a breakpoint set for it. // an address that already has a breakpoint set for it.
type BreakpointExistsError struct { type BreakpointExistsError struct {
file string File string
line int Line int
addr uint64 Addr uint64
} }
func (bpe BreakpointExistsError) Error() string { 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 // InvalidAddressError represents the result of
// attempting to set a breakpoint at an invalid address. // attempting to set a breakpoint at an invalid address.
type InvalidAddressError struct { type InvalidAddressError struct {
address uint64 Address uint64
} }
func (iae InvalidAddressError) Error() string { 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 { // CheckCondition evaluates bp's condition on thread.
_, err := thread.writeMemory(uintptr(addr), dbp.bi.arch.BreakpointInstruction()) func (bp *Breakpoint) CheckCondition(thread IThread) (bool, error) {
return err
}
func (bp *Breakpoint) checkCondition(thread IThread) (bool, error) {
if bp.Cond == nil { if bp.Cond == nil {
return true, nil return true, nil
} }
@ -154,9 +142,9 @@ func (bp *Breakpoint) Internal() bool {
// NoBreakpointError is returned when trying to // NoBreakpointError is returned when trying to
// clear a breakpoint that does not exist. // clear a breakpoint that does not exist.
type NoBreakpointError struct { type NoBreakpointError struct {
addr uint64 Addr uint64
} }
func (nbp NoBreakpointError) Error() string { func (nbp NoBreakpointError) Error() string {
return fmt.Sprintf("no breakpoint at %#v", nbp.addr) return fmt.Sprintf("no breakpoint at %#v", nbp.Addr)
} }

@ -1,4 +1,4 @@
package proc package core
import ( import (
"debug/gosym" "debug/gosym"
@ -7,6 +7,8 @@ import (
"go/ast" "go/ast"
"io" "io"
"sync" "sync"
"github.com/derekparker/delve/pkg/proc"
) )
// A SplicedMemory represents a memory space formed from multiple regions, // A SplicedMemory represents a memory space formed from multiple regions,
@ -29,11 +31,11 @@ type SplicedMemory struct {
type readerEntry struct { type readerEntry struct {
offset uintptr offset uintptr
length uintptr length uintptr
reader MemoryReader reader proc.MemoryReader
} }
// Add adds a new region to the SplicedMemory, which may override existing regions. // 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 { if length == 0 {
return return
} }
@ -141,12 +143,12 @@ func (r *OffsetReaderAt) ReadMemory(buf []byte, addr uintptr) (n int, err error)
} }
type CoreProcess struct { type CoreProcess struct {
bi BinaryInfo bi proc.BinaryInfo
core *Core core *Core
breakpoints map[uint64]*Breakpoint breakpoints map[uint64]*proc.Breakpoint
currentThread *LinuxPrStatus currentThread *LinuxPrStatus
selectedGoroutine *G selectedGoroutine *proc.G
allGCache []*G allGCache []*proc.G
} }
type CoreThread struct { type CoreThread struct {
@ -165,8 +167,8 @@ func OpenCore(corePath, exePath string) (*CoreProcess, error) {
} }
p := &CoreProcess{ p := &CoreProcess{
core: core, core: core,
breakpoints: make(map[uint64]*Breakpoint), breakpoints: make(map[uint64]*proc.Breakpoint),
bi: NewBinaryInfo("linux", "amd64"), bi: proc.NewBinaryInfo("linux", "amd64"),
} }
var wg sync.WaitGroup var wg sync.WaitGroup
@ -178,19 +180,18 @@ func OpenCore(corePath, exePath string) (*CoreProcess, error) {
break break
} }
scope := &EvalScope{0, 0, p.CurrentThread(), nil, &p.bi} ver, isextld, err := proc.GetGoInformation(p)
ver, isextld, err := scope.getGoInformation()
if err != nil { if err != nil {
return nil, err return nil, err
} }
p.bi.arch.SetGStructOffset(ver, isextld) p.bi.Arch.SetGStructOffset(ver, isextld)
p.selectedGoroutine, _ = GetG(p.CurrentThread()) p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
return p, nil return p, nil
} }
func (p *CoreProcess) BinInfo() *BinaryInfo { func (p *CoreProcess) BinInfo() *proc.BinaryInfo {
return &p.bi return &p.bi
} }
@ -202,16 +203,16 @@ func (thread *CoreThread) ReadMemory(data []byte, addr uintptr) (n int, err erro
return n, err 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 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) 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 return nil, false, nil
} }
@ -219,16 +220,16 @@ func (t *CoreThread) ThreadID() int {
return int(t.th.Pid) 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 //TODO(aarzilli): handle floating point registers
return &t.th.Reg, nil return &t.th.Reg, nil
} }
func (t *CoreThread) Arch() Arch { func (t *CoreThread) Arch() proc.Arch {
return t.p.bi.arch return t.p.bi.Arch
} }
func (t *CoreThread) BinInfo() *BinaryInfo { func (t *CoreThread) BinInfo() *proc.BinaryInfo {
return &t.p.bi return &t.p.bi
} }
@ -236,19 +237,23 @@ func (t *CoreThread) StepInstruction() error {
return ErrContinueCore 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 return p.breakpoints
} }
func (p *CoreProcess) ClearBreakpoint(addr uint64) (*Breakpoint, error) { func (p *CoreProcess) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
return nil, NoBreakpointError{addr: addr} return nil, proc.NoBreakpointError{Addr: addr}
} }
func (p *CoreProcess) ClearInternalBreakpoints() error { func (p *CoreProcess) ClearInternalBreakpoints() error {
return nil return nil
} }
func (p *CoreProcess) ContinueOnce() (IThread, error) { func (p *CoreProcess) ContinueOnce() (proc.IThread, error) {
return nil, ErrContinueCore return nil, ErrContinueCore
} }
@ -260,7 +265,7 @@ func (p *CoreProcess) RequestManualStop() error {
return nil return nil
} }
func (p *CoreProcess) CurrentThread() IThread { func (p *CoreProcess) CurrentThread() proc.IThread {
return &CoreThread{p.currentThread, p} return &CoreThread{p.currentThread, p}
} }
@ -273,18 +278,18 @@ func (p *CoreProcess) Exited() bool {
} }
func (p *CoreProcess) FindFileLocation(fileName string, lineNumber int) (uint64, error) { 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) { 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) { 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 return &p.allGCache
} }
@ -304,16 +309,16 @@ func (p *CoreProcess) Running() bool {
return false return false
} }
func (p *CoreProcess) SelectedGoroutine() *G { func (p *CoreProcess) SelectedGoroutine() *proc.G {
return p.selectedGoroutine 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 return nil, ErrWriteCore
} }
func (p *CoreProcess) SwitchGoroutine(gid int) error { func (p *CoreProcess) SwitchGoroutine(gid int) error {
g, err := FindGoroutine(p, gid) g, err := proc.FindGoroutine(p, gid)
if err != nil { if err != nil {
return err return err
} }
@ -321,8 +326,8 @@ func (p *CoreProcess) SwitchGoroutine(gid int) error {
// user specified -1 and selectedGoroutine is nil // user specified -1 and selectedGoroutine is nil
return nil return nil
} }
if g.thread != nil { if g.Thread != nil {
return p.SwitchThread(g.thread.ThreadID()) return p.SwitchThread(g.Thread.ThreadID())
} }
p.selectedGoroutine = g p.selectedGoroutine = g
return nil return nil
@ -331,21 +336,21 @@ func (p *CoreProcess) SwitchGoroutine(gid int) error {
func (p *CoreProcess) SwitchThread(tid int) error { func (p *CoreProcess) SwitchThread(tid int) error {
if th, ok := p.core.Threads[tid]; ok { if th, ok := p.core.Threads[tid]; ok {
p.currentThread = th p.currentThread = th
p.selectedGoroutine, _ = GetG(p.CurrentThread()) p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
return nil return nil
} }
return fmt.Errorf("thread %d does not exist", tid) return fmt.Errorf("thread %d does not exist", tid)
} }
func (p *CoreProcess) ThreadList() []IThread { func (p *CoreProcess) ThreadList() []proc.IThread {
r := make([]IThread, 0, len(p.core.Threads)) r := make([]proc.IThread, 0, len(p.core.Threads))
for _, v := range p.core.Threads { for _, v := range p.core.Threads {
r = append(r, &CoreThread{v, p}) r = append(r, &CoreThread{v, p})
} }
return r 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] t, ok := p.core.Threads[threadID]
return &CoreThread{t, p}, ok return &CoreThread{t, p}, ok
} }

@ -1,4 +1,4 @@
package proc package core
import ( import (
"bytes" "bytes"
@ -13,6 +13,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/derekparker/delve/pkg/proc"
"github.com/derekparker/delve/pkg/proc/test" "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.Errorf("read apport log: %q, %v", apport, err)
t.Fatalf("ReadCore() failed: %v", err) t.Fatalf("ReadCore() failed: %v", err)
} }
gs, err := GoroutinesInfo(p) gs, err := proc.GoroutinesInfo(p)
if err != nil || len(gs) == 0 { if err != nil || len(gs) == 0 {
t.Fatalf("GoroutinesInfo() = %v, %v; wanted at least one goroutine", gs, err) t.Fatalf("GoroutinesInfo() = %v, %v; wanted at least one goroutine", gs, err)
} }
var panicking *G var panicking *proc.G
var panickingStack []Stackframe var panickingStack []proc.Stackframe
for _, g := range gs { for _, g := range gs {
stack, err := g.Stacktrace(10) stack, err := g.Stacktrace(10)
if err != nil { 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) 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 // Walk backward, because the current function seems to be main.main
// in the actual call to panic(). // in the actual call to panic().
for i := len(panickingStack) - 1; i >= 0; i-- { for i := len(panickingStack) - 1; i >= 0; i-- {
@ -189,7 +190,7 @@ func TestCore(t *testing.T) {
if mainFrame == nil { if mainFrame == nil {
t.Fatalf("Couldn't find main in stack %v", panickingStack) 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 { if err != nil {
t.Fatalf("Couldn't EvalVariable(msg, ...): %v", err) t.Fatalf("Couldn't EvalVariable(msg, ...): %v", err)
} }

@ -1,4 +1,4 @@
package proc package core
import ( import (
"bytes" "bytes"
@ -11,6 +11,8 @@ import (
"golang.org/x/debug/elf" "golang.org/x/debug/elf"
"golang.org/x/arch/x86/x86asm" "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 // 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 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") return errors.New("not supported")
} }
func (r *LinuxCoreRegisters) Slice() []Register { func (r *LinuxCoreRegisters) Slice() []proc.Register {
var regs = []struct { var regs = []struct {
k string k string
v uint64 v uint64
@ -272,12 +274,12 @@ func (r *LinuxCoreRegisters) Slice() []Register {
{"Fs", r.Fs}, {"Fs", r.Fs},
{"Gs", r.Gs}, {"Gs", r.Gs},
} }
out := make([]Register, 0, len(regs)) out := make([]proc.Register, 0, len(regs))
for _, reg := range regs { for _, reg := range regs {
if reg.k == "Eflags" { if reg.k == "Eflags" {
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64) out = proc.AppendEflagReg(out, reg.k, reg.v)
} else { } else {
out = appendQwordReg(out, reg.k, reg.v) out = proc.AppendQwordReg(out, reg.k, reg.v)
} }
} }
return out return out
@ -328,7 +330,7 @@ func readCore(corePath, exePath string) (*Core, error) {
} }
type Core struct { type Core struct {
MemoryReader proc.MemoryReader
Threads map[int]*LinuxPrStatus Threads map[int]*LinuxPrStatus
Pid int Pid int
} }
@ -448,7 +450,7 @@ func skipPadding(r io.ReadSeeker, pad int64) error {
return nil 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{} memory := &SplicedMemory{}
// For now, assume all file mappings are to the exe. // For now, assume all file mappings are to the exe.

@ -35,16 +35,16 @@ func Disassemble(dbp DisassembleInfo, g *G, startPC, endPC uint64) ([]AsmInstruc
} }
var regs Registers var regs Registers
var mem memoryReadWriter = dbp.CurrentThread() var mem MemoryReadWriter = dbp.CurrentThread()
if g.thread != nil { if g.Thread != nil {
mem = g.thread mem = g.Thread
regs, _ = g.thread.Registers(false) regs, _ = g.Thread.Registers(false)
} }
return disassemble(mem, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC) 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)) mem := make([]byte, int(endPC-startPC))
_, err := memrw.ReadMemory(mem, uintptr(startPC)) _, err := memrw.ReadMemory(mem, uintptr(startPC))
if err != nil { if err != nil {

@ -63,7 +63,7 @@ func (inst *AsmInstruction) IsCall() bool {
return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL 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 { if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
return nil 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 // 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 // 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) text, err := disassemble(mem, nil, breakpoints, bi, fn.Entry, fn.End)
if err != nil { if err != nil {
return fn.Entry, err return fn.Entry, err

@ -67,10 +67,10 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
// try to interpret the selector as a package variable // try to interpret the selector as a package variable
if maybePkg, ok := node.X.(*ast.Ident); ok { if maybePkg, ok := node.X.(*ast.Ident); ok {
if maybePkg.Name == "runtime" && node.Sel.Name == "curg" { if maybePkg.Name == "runtime" && node.Sel.Name == "curg" {
if scope.gvar == nil { if scope.Gvar == nil {
return nilVariable, 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 { } else if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil {
return v, nil return v, nil
} }
@ -144,7 +144,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
fnnode = p.X fnnode = p.X
} }
styp, err := scope.bi.findTypeExpr(fnnode) styp, err := scope.BinInfo.findTypeExpr(fnnode)
if err != nil { if err != nil {
return nil, err 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()) 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 v.loaded = true
switch ttyp := typ.(type) { switch ttyp := typ.(type) {
@ -457,7 +457,7 @@ func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
return v, nil return v, nil
} }
// if it's not a local variable then it could be a package variable w/o explicit package name // 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 fn != nil {
if v, err = scope.packageVarAddr(fn.PackageName() + "." + node.Name); err == nil { if v, err = scope.packageVarAddr(fn.PackageName() + "." + node.Name); err == nil {
v.Name = node.Name v.Name = node.Name
@ -495,7 +495,7 @@ func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, err
if xv.Children[0].Addr == 0 { if xv.Children[0].Addr == 0 {
return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type)) 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 { if err != nil {
return nil, err return nil, err
} }
@ -640,7 +640,7 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) {
xev.OnlyAddr = true xev.OnlyAddr = true
typename := "*" + xev.DwarfType.Common().Name 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.Children = []Variable{*xev}
rv.loaded = true rv.loaded = true

@ -59,7 +59,7 @@
// //
// Therefore the following code will assume lldb-server-like behavior. // Therefore the following code will assume lldb-server-like behavior.
package proc package gdbserial
import ( import (
"debug/gosym" "debug/gosym"
@ -77,6 +77,8 @@ import (
"time" "time"
"golang.org/x/arch/x86/x86asm" "golang.org/x/arch/x86/x86asm"
"github.com/derekparker/delve/pkg/proc"
) )
const ( const (
@ -94,17 +96,17 @@ const heartbeatInterval = 10 * time.Second
// GdbserverProcess implements target.Interface using a connection to a // GdbserverProcess implements target.Interface using a connection to a
// debugger stub that understands Gdb Remote Serial Protocol. // debugger stub that understands Gdb Remote Serial Protocol.
type GdbserverProcess struct { type GdbserverProcess struct {
bi BinaryInfo bi proc.BinaryInfo
conn gdbConn conn gdbConn
threads map[int]*GdbserverThread threads map[int]*GdbserverThread
currentThread *GdbserverThread currentThread *GdbserverThread
selectedGoroutine *G selectedGoroutine *proc.G
exited bool exited bool
ctrlC bool // ctrl-c was sent to stop inferior ctrlC bool // ctrl-c was sent to stop inferior
breakpoints map[uint64]*Breakpoint breakpoints map[uint64]*proc.Breakpoint
breakpointIDCounter int breakpointIDCounter int
internalBreakpointIDCounter int internalBreakpointIDCounter int
@ -115,7 +117,7 @@ type GdbserverProcess struct {
process *exec.Cmd process *exec.Cmd
allGCache []*G allGCache []*proc.G
} }
// GdbserverThread is a thread of GdbserverProcess. // GdbserverThread is a thread of GdbserverProcess.
@ -123,7 +125,7 @@ type GdbserverThread struct {
ID int ID int
strID string strID string
regs gdbRegisters regs gdbRegisters
CurrentBreakpoint *Breakpoint CurrentBreakpoint *proc.Breakpoint
BreakpointConditionMet bool BreakpointConditionMet bool
BreakpointConditionError error BreakpointConditionError error
p *GdbserverProcess p *GdbserverProcess
@ -175,8 +177,8 @@ func GdbserverConnect(addr string, path string, pid int, attempts int) (*Gdbserv
}, },
threads: make(map[int]*GdbserverThread), threads: make(map[int]*GdbserverThread),
bi: NewBinaryInfo(runtime.GOOS, runtime.GOARCH), bi: proc.NewBinaryInfo(runtime.GOOS, runtime.GOARCH),
breakpoints: make(map[uint64]*Breakpoint), breakpoints: make(map[uint64]*proc.Breakpoint),
gcmdok: true, gcmdok: true,
threadStopInfo: 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 := proc.GetGoInformation(p)
ver, isextld, err := scope.getGoInformation()
if err != nil { if err != nil {
conn.Close() conn.Close()
p.bi.Close() p.bi.Close()
return nil, err return nil, err
} }
p.bi.arch.SetGStructOffset(ver, isextld) p.bi.Arch.SetGStructOffset(ver, isextld)
p.selectedGoroutine, _ = GetG(p.CurrentThread()) p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
panicpc, err := p.FindFunctionLocation("runtime.startpanic", true, 0) panicpc, err := p.FindFunctionLocation("runtime.startpanic", true, 0)
if err == nil { if err == nil {
bp, err := p.SetBreakpoint(panicpc, UserBreakpoint, nil) bp, err := p.SetBreakpoint(panicpc, proc.UserBreakpoint, nil)
if err == nil { if err == nil {
bp.Name = "unrecovered-panic" bp.Name = "unrecovered-panic"
bp.ID = -1 bp.ID = -1
@ -298,7 +299,7 @@ const debugserverExecutable = "/Library/Developer/CommandLineTools/Library/Priva
func LLDBLaunch(cmd []string, wd string) (*GdbserverProcess, error) { func LLDBLaunch(cmd []string, wd string) (*GdbserverProcess, error) {
// check that the argument to Launch is an executable file // check that the argument to Launch is an executable file
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 { if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
return nil, NotExecutableErr return nil, proc.NotExecutableErr
} }
port := unusedPort() port := unusedPort()
@ -400,7 +401,7 @@ func (p *GdbserverProcess) loadProcessInfo(pid int) (int, string, error) {
return pid, pi["name"], nil return pid, pi["name"], nil
} }
func (p *GdbserverProcess) BinInfo() *BinaryInfo { func (p *GdbserverProcess) BinInfo() *proc.BinaryInfo {
return &p.bi return &p.bi
} }
@ -417,39 +418,39 @@ func (p *GdbserverProcess) Running() bool {
} }
func (p *GdbserverProcess) FindFileLocation(fileName string, lineNumber int) (uint64, error) { 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) { 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) { 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] thread, ok := p.threads[threadID]
return thread, ok return thread, ok
} }
func (p *GdbserverProcess) ThreadList() []IThread { func (p *GdbserverProcess) ThreadList() []proc.IThread {
r := make([]IThread, 0, len(p.threads)) r := make([]proc.IThread, 0, len(p.threads))
for _, thread := range p.threads { for _, thread := range p.threads {
r = append(r, thread) r = append(r, thread)
} }
return r return r
} }
func (p *GdbserverProcess) CurrentThread() IThread { func (p *GdbserverProcess) CurrentThread() proc.IThread {
return p.currentThread return p.currentThread
} }
func (p *GdbserverProcess) AllGCache() *[]*G { func (p *GdbserverProcess) AllGCache() *[]*proc.G {
return &p.allGCache return &p.allGCache
} }
func (p *GdbserverProcess) SelectedGoroutine() *G { func (p *GdbserverProcess) SelectedGoroutine() *proc.G {
return p.selectedGoroutine return p.selectedGoroutine
} }
@ -460,9 +461,9 @@ const (
stopSignal = 0x13 stopSignal = 0x13
) )
func (p *GdbserverProcess) ContinueOnce() (IThread, error) { func (p *GdbserverProcess) ContinueOnce() (proc.IThread, error) {
if p.exited { 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 // step threads stopped at any breakpoint over their breakpoint
@ -491,7 +492,7 @@ continueLoop:
tu.done = false tu.done = false
threadID, sig, err = p.conn.resume(sig, &tu) threadID, sig, err = p.conn.resume(sig, &tu)
if err != nil { if err != nil {
if _, exited := err.(ProcessExitedError); exited { if _, exited := err.(proc.ProcessExitedError); exited {
p.exited = true p.exited = true
} }
return nil, err return nil, err
@ -547,35 +548,35 @@ func (p *GdbserverProcess) StepInstruction() error {
if p.selectedGoroutine == nil { if p.selectedGoroutine == nil {
return errors.New("cannot single step: no selected goroutine") return errors.New("cannot single step: no selected goroutine")
} }
if p.selectedGoroutine.thread == nil { if p.selectedGoroutine.Thread == nil {
if _, err := p.SetBreakpoint(p.selectedGoroutine.PC, NextBreakpoint, sameGoroutineCondition(p.selectedGoroutine)); err != nil { if _, err := p.SetBreakpoint(p.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(p.selectedGoroutine)); err != nil {
return err return err
} }
return Continue(p) return proc.Continue(p)
} }
p.allGCache = nil p.allGCache = nil
if p.exited { if p.exited {
return &ProcessExitedError{Pid: p.conn.pid} return &proc.ProcessExitedError{Pid: p.conn.pid}
} }
p.selectedGoroutine.thread.(*GdbserverThread).clearBreakpointState() p.selectedGoroutine.Thread.(*GdbserverThread).clearBreakpointState()
err := p.selectedGoroutine.thread.(*GdbserverThread).StepInstruction() err := p.selectedGoroutine.Thread.(*GdbserverThread).StepInstruction()
if err != nil { if err != nil {
return err return err
} }
return p.selectedGoroutine.thread.(*GdbserverThread).SetCurrentBreakpoint() return p.selectedGoroutine.Thread.(*GdbserverThread).SetCurrentBreakpoint()
} }
func (p *GdbserverProcess) SwitchThread(tid int) error { func (p *GdbserverProcess) SwitchThread(tid int) error {
if th, ok := p.threads[tid]; ok { if th, ok := p.threads[tid]; ok {
p.currentThread = th p.currentThread = th
p.selectedGoroutine, _ = GetG(p.CurrentThread()) p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
return nil return nil
} }
return fmt.Errorf("thread %d does not exist", tid) return fmt.Errorf("thread %d does not exist", tid)
} }
func (p *GdbserverProcess) SwitchGoroutine(gid int) error { func (p *GdbserverProcess) SwitchGoroutine(gid int) error {
g, err := FindGoroutine(p, gid) g, err := proc.FindGoroutine(p, gid)
if err != nil { if err != nil {
return err return err
} }
@ -583,8 +584,8 @@ func (p *GdbserverProcess) SwitchGoroutine(gid int) error {
// user specified -1 and selectedGoroutine is nil // user specified -1 and selectedGoroutine is nil
return nil return nil
} }
if g.thread != nil { if g.Thread != nil {
return p.SwitchThread(g.thread.ThreadID()) return p.SwitchThread(g.Thread.ThreadID())
} }
p.selectedGoroutine = g p.selectedGoroutine = g
return nil return nil
@ -605,7 +606,7 @@ func (p *GdbserverProcess) Kill() error {
return nil return nil
} }
err := p.conn.kill() err := p.conn.kill()
if _, exited := err.(ProcessExitedError); exited { if _, exited := err.(proc.ProcessExitedError); exited {
p.exited = true p.exited = true
return nil return nil
} }
@ -615,7 +616,7 @@ func (p *GdbserverProcess) Kill() error {
func (p *GdbserverProcess) Detach(kill bool) error { func (p *GdbserverProcess) Detach(kill bool) error {
if kill { if kill {
if err := p.Kill(); err != nil { if err := p.Kill(); err != nil {
if _, exited := err.(ProcessExitedError); !exited { if _, exited := err.(proc.ProcessExitedError); !exited {
return err return err
} }
} }
@ -632,13 +633,13 @@ func (p *GdbserverProcess) Detach(kill bool) error {
return p.bi.Close() return p.bi.Close()
} }
func (p *GdbserverProcess) Breakpoints() map[uint64]*Breakpoint { func (p *GdbserverProcess) Breakpoints() map[uint64]*proc.Breakpoint {
return p.breakpoints 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). // 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 return bp, true
} }
// Directly use addr to lookup breakpoint. // Directly use addr to lookup breakpoint.
@ -648,16 +649,16 @@ func (p *GdbserverProcess) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
return nil, false 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 { 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)) f, l, fn := p.bi.PCToLine(uint64(addr))
if fn == nil { if fn == nil {
return nil, InvalidAddressError{address: addr} return nil, proc.InvalidAddressError{Address: addr}
} }
newBreakpoint := &Breakpoint{ newBreakpoint := &proc.Breakpoint{
FunctionName: fn.Name, FunctionName: fn.Name,
File: f, File: f,
Line: l, Line: l,
@ -667,7 +668,7 @@ func (p *GdbserverProcess) SetBreakpoint(addr uint64, kind BreakpointKind, cond
HitCount: map[int]uint64{}, HitCount: map[int]uint64{},
} }
if kind != UserBreakpoint { if kind != proc.UserBreakpoint {
p.internalBreakpointIDCounter++ p.internalBreakpointIDCounter++
newBreakpoint.ID = p.internalBreakpointIDCounter newBreakpoint.ID = p.internalBreakpointIDCounter
} else { } else {
@ -682,13 +683,13 @@ func (p *GdbserverProcess) SetBreakpoint(addr uint64, kind BreakpointKind, cond
return newBreakpoint, nil return newBreakpoint, nil
} }
func (p *GdbserverProcess) ClearBreakpoint(addr uint64) (*Breakpoint, error) { func (p *GdbserverProcess) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
if p.exited { if p.exited {
return nil, &ProcessExitedError{Pid: p.conn.pid} return nil, &proc.ProcessExitedError{Pid: p.conn.pid}
} }
bp := p.breakpoints[addr] bp := p.breakpoints[addr]
if bp == nil { if bp == nil {
return nil, NoBreakpointError{addr: addr} return nil, proc.NoBreakpointError{Addr: addr}
} }
if err := p.conn.clearBreakpoint(addr); err != nil { 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 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) 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) regs, err := t.Registers(false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
pc := regs.PC() pc := regs.PC()
f, l, fn := t.p.bi.PCToLine(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 return t.CurrentBreakpoint, (t.CurrentBreakpoint != nil && t.BreakpointConditionMet), t.BreakpointConditionError
} }
@ -871,15 +872,15 @@ func (t *GdbserverThread) ThreadID() int {
return t.ID return t.ID
} }
func (t *GdbserverThread) Registers(floatingPoint bool) (Registers, error) { func (t *GdbserverThread) Registers(floatingPoint bool) (proc.Registers, error) {
return &t.regs, nil return &t.regs, nil
} }
func (t *GdbserverThread) Arch() Arch { func (t *GdbserverThread) Arch() proc.Arch {
return t.p.bi.arch return t.p.bi.Arch
} }
func (t *GdbserverThread) BinInfo() *BinaryInfo { func (t *GdbserverThread) BinInfo() *proc.BinaryInfo {
return &t.p.bi return &t.p.bi
} }
@ -903,16 +904,37 @@ func (t *GdbserverThread) StepInstruction() error {
return t.reloadRegisters() 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 // loadGInstr returns the correct MOV instruction for the current
// OS/architecture that can be executed to load the address of G from an // OS/architecture that can be executed to load the address of G from an
// inferior's thread. // inferior's thread.
func (p *GdbserverProcess) loadGInstr() []byte { func (p *GdbserverProcess) loadGInstr() []byte {
switch p.bi.goos { switch p.bi.GOOS {
case "windows": case "windows":
//TODO(aarzilli): implement //TODO(aarzilli): implement
panic("not implemented") panic("not implemented")
case "linux": case "linux":
switch p.bi.arch.GStructOffset() { switch p.bi.Arch.GStructOffset() {
case 0xfffffffffffffff8, 0x0: case 0xfffffffffffffff8, 0x0:
// mov rcx,QWORD PTR fs:0xfffffffffffffff8 // mov rcx,QWORD PTR fs:0xfffffffffffffff8
return []byte{0x64, 0x48, 0x8B, 0x0C, 0x25, 0xF8, 0xFF, 0xFF, 0xFF} 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 { func (t *GdbserverThread) reloadGAtPC() error {
movinstr := t.p.loadGInstr() movinstr := t.p.loadGInstr()
if gdbserverThreadBlocked(t) { if t.Blocked() {
t.regs.tls = 0 t.regs.tls = 0
t.regs.gaddr = 0 t.regs.gaddr = 0
t.regs.hasgaddr = true t.regs.hasgaddr = true
@ -1038,13 +1060,13 @@ func (t *GdbserverThread) reloadGAtPC() error {
return err return err
} }
_, err = t.writeMemory(uintptr(pc), movinstr) _, err = t.WriteMemory(uintptr(pc), movinstr)
if err != nil { if err != nil {
return err return err
} }
defer func() { defer func() {
_, err0 := t.writeMemory(uintptr(pc), savedcode) _, err0 := t.WriteMemory(uintptr(pc), savedcode)
if err == nil { if err == nil {
err = err0 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 // a MOV instruction that loads the address of the current G in the RCX
// register. // register.
func (t *GdbserverThread) reloadGAlloc() error { func (t *GdbserverThread) reloadGAlloc() error {
if gdbserverThreadBlocked(t) { if t.Blocked() {
t.regs.tls = 0 t.regs.tls = 0
t.regs.gaddr = 0 t.regs.gaddr = 0
t.regs.hasgaddr = true t.regs.hasgaddr = true
@ -1118,27 +1140,6 @@ func (t *GdbserverThread) reloadGAlloc() error {
return err 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() { func (t *GdbserverThread) clearBreakpointState() {
t.setbp = false t.setbp = false
t.CurrentBreakpoint = nil t.CurrentBreakpoint = nil
@ -1160,9 +1161,9 @@ func (thread *GdbserverThread) SetCurrentBreakpoint() error {
} }
} }
thread.CurrentBreakpoint = bp thread.CurrentBreakpoint = bp
thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.checkCondition(thread) thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.CheckCondition(thread)
if thread.CurrentBreakpoint != nil && thread.BreakpointConditionMet { 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.HitCount[g.ID]++
} }
thread.CurrentBreakpoint.TotalHitCount++ thread.CurrentBreakpoint.TotalHitCount++
@ -1365,10 +1366,10 @@ func (regs *gdbRegisters) Get(n int) (uint64, error) {
return regs.byName("r15"), nil 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) regs.setPC(pc)
t := thread.(*GdbserverThread) t := thread.(*GdbserverThread)
if t.p.gcmdok { 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) return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value)
} }
func (regs *gdbRegisters) Slice() []Register { func (regs *gdbRegisters) Slice() []proc.Register {
r := make([]Register, 0, len(regs.regsInfo)) r := make([]proc.Register, 0, len(regs.regsInfo))
for _, reginfo := range regs.regsInfo { for _, reginfo := range regs.regsInfo {
switch { switch {
case reginfo.Name == "eflags": 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": 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: 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: 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: 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: case reginfo.Bitsize == 80:
idx := 0 idx := 0
for _, stprefix := range []string{"stmm", "st"} { for _, stprefix := range []string{"stmm", "st"} {
@ -1401,10 +1402,10 @@ func (regs *gdbRegisters) Slice() []Register {
} }
} }
value := regs.regs[reginfo.Name].value 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: 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: case reginfo.Bitsize == 256:
if !strings.HasPrefix(strings.ToLower(reginfo.Name), "ymm") { if !strings.HasPrefix(strings.ToLower(reginfo.Name), "ymm") {
@ -1413,8 +1414,8 @@ func (regs *gdbRegisters) Slice() []Register {
value := regs.regs[reginfo.Name].value value := regs.regs[reginfo.Name].value
xmmName := "x" + reginfo.Name[1:] xmmName := "x" + reginfo.Name[1:]
r = appendSSEReg(r, strings.ToUpper(xmmName), value[:16]) r = proc.AppendSSEReg(r, strings.ToUpper(xmmName), value[:16])
r = appendSSEReg(r, strings.ToUpper(reginfo.Name), value[16:]) r = proc.AppendSSEReg(r, strings.ToUpper(reginfo.Name), value[16:])
} }
} }
return r return r

@ -1,4 +1,4 @@
package proc package gdbserial
import ( import (
"bufio" "bufio"
@ -12,6 +12,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/derekparker/delve/pkg/proc"
) )
type gdbConn struct { type gdbConn struct {
@ -630,7 +632,7 @@ func (conn *gdbConn) parseStopPacket(resp []byte, threadID string, tu *threadUpd
semicolon = len(resp) semicolon = len(resp)
} }
status, _ := strconv.ParseUint(string(resp[1:semicolon]), 16, 8) 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': case 'N':
// we were singlestepping the thread and the thread exited // we were singlestepping the thread and the thread exited

@ -1,6 +1,6 @@
// +build linux darwin // +build linux darwin
package proc package gdbserial
import "syscall" import "syscall"

@ -1,4 +1,4 @@
package proc package gdbserial
import "syscall" import "syscall"

@ -11,15 +11,15 @@ type MemoryReader interface {
ReadMemory(buf []byte, addr uintptr) (n int, err error) ReadMemory(buf []byte, addr uintptr) (n int, err error)
} }
type memoryReadWriter interface { type MemoryReadWriter interface {
MemoryReader MemoryReader
writeMemory(addr uintptr, data []byte) (written int, err error) WriteMemory(addr uintptr, data []byte) (written int, err error)
} }
type memCache struct { type memCache struct {
cacheAddr uintptr cacheAddr uintptr
cache []byte cache []byte
mem memoryReadWriter mem MemoryReadWriter
} }
func (m *memCache) contains(addr uintptr, size int) bool { 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) return m.mem.ReadMemory(data, addr)
} }
func (m *memCache) writeMemory(addr uintptr, data []byte) (written int, err error) { func (m *memCache) WriteMemory(addr uintptr, data []byte) (written int, err error) {
return m.mem.writeMemory(addr, data) 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 { if !cacheEnabled {
return mem return mem
} }

@ -11,7 +11,7 @@ type moduleData struct {
typemapVar *Variable typemapVar *Variable
} }
func loadModuleData(bi *BinaryInfo, mem memoryReadWriter) (err error) { func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) (err error) {
bi.loadModuleDataOnce.Do(func() { bi.loadModuleDataOnce.Do(func() {
scope := &EvalScope{0, 0, mem, nil, bi} scope := &EvalScope{0, 0, mem, nil, bi}
var md *Variable var md *Variable
@ -56,7 +56,7 @@ func loadModuleData(bi *BinaryInfo, mem memoryReadWriter) (err error) {
return 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 // See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
if err := loadModuleData(bi, mem); err != nil { if err := loadModuleData(bi, mem); err != nil {
return nil, err return nil, err
@ -93,7 +93,7 @@ func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryRea
return newVariable("", res, rtyp, bi, mem), nil 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 // See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
if err = loadModuleData(bi, mem); err != nil { if err = loadModuleData(bi, mem); err != nil {
return "", "", 0, err return "", "", 0, err
@ -118,7 +118,7 @@ func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryRea
return loadName(bi, resv.Addr, mem) 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} scope := &EvalScope{0, 0, mem, nil, bi}
reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs") reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs")
if err != nil { if err != nil {
@ -140,7 +140,7 @@ const (
nameflagHasPkg = 1 << 2 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 off := addr
namedata := make([]byte, 3) namedata := make([]byte, 3)
_, err = mem.ReadMemory(namedata, off) _, err = mem.ReadMemory(namedata, off)

525
pkg/proc/native/proc.go Normal file

@ -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
}
*/

@ -1,4 +1,4 @@
package proc package native
// #include "proc_darwin.h" // #include "proc_darwin.h"
// #include "threads_darwin.h" // #include "threads_darwin.h"
@ -15,6 +15,8 @@ import (
"unsafe" "unsafe"
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/pkg/proc"
) )
// OSProcessDetails holds Darwin specific information. // OSProcessDetails holds Darwin specific information.
@ -36,7 +38,7 @@ type OSProcessDetails struct {
func Launch(cmd []string, wd string) (*Process, error) { func Launch(cmd []string, wd string) (*Process, error) {
// check that the argument to Launch is an executable file // check that the argument to Launch is an executable file
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 { 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]) argv0Go, err := filepath.Abs(cmd[0])
if err != nil { if err != nil {
@ -285,7 +287,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
return nil, err return nil, err
} }
dbp.postExit() 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: case C.MACH_RCV_INTERRUPTED:
if !dbp.halt { if !dbp.halt {
@ -393,7 +395,7 @@ func (dbp *Process) exitGuard(err error) error {
_, status, werr := dbp.wait(dbp.pid, sys.WNOHANG) _, status, werr := dbp.wait(dbp.pid, sys.WNOHANG)
if werr == nil && status.Exited() { if werr == nil && status.Exited() {
dbp.postExit() dbp.postExit()
return ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()} return proc.ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
} }
return err return err
} }

@ -1,4 +1,4 @@
package proc package native
import ( import (
"bytes" "bytes"
@ -16,6 +16,8 @@ import (
"time" "time"
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/pkg/proc"
) )
// Process statuses // Process statuses
@ -43,34 +45,34 @@ type OSProcessDetails struct {
// to be supplied to that process. `wd` is working directory of the program. // to be supplied to that process. `wd` is working directory of the program.
func Launch(cmd []string, wd string) (*Process, error) { func Launch(cmd []string, wd string) (*Process, error) {
var ( var (
proc *exec.Cmd process *exec.Cmd
err error err error
) )
// check that the argument to Launch is an executable file // check that the argument to Launch is an executable file
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 { if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
return nil, NotExecutableErr return nil, proc.NotExecutableErr
} }
dbp := New(0) dbp := New(0)
dbp.execPtraceFunc(func() { dbp.execPtraceFunc(func() {
proc = exec.Command(cmd[0]) process = exec.Command(cmd[0])
proc.Args = cmd process.Args = cmd
proc.Stdout = os.Stdout process.Stdout = os.Stdout
proc.Stderr = os.Stderr process.Stderr = os.Stderr
proc.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true} process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true}
if wd != "" { if wd != "" {
proc.Dir = wd process.Dir = wd
} }
err = proc.Start() err = process.Start()
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
dbp.pid = proc.Process.Pid dbp.pid = process.Process.Pid
_, _, err = dbp.wait(proc.Process.Pid, 0) _, _, err = dbp.wait(process.Process.Pid, 0)
if err != nil { if err != nil {
return nil, fmt.Errorf("waiting for target execve failed: %s", err) 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. // 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 status.Exited() {
if wpid == dbp.pid { if wpid == dbp.pid {
dbp.postExit() dbp.postExit()
return nil, ProcessExitedError{Pid: wpid, Status: status.ExitStatus()} return nil, proc.ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
} }
delete(dbp.threads, wpid) delete(dbp.threads, wpid)
continue continue
@ -246,7 +248,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
// TODO(dp) alert user about unexpected signals here. // TODO(dp) alert user about unexpected signals here.
if err := th.resumeWithSig(int(status.StopSignal())); err != nil { if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
if err == sys.ESRCH { if err == sys.ESRCH {
return nil, ProcessExitedError{Pid: dbp.pid} return nil, proc.ProcessExitedError{Pid: dbp.pid}
} }
return nil, err return nil, err
} }

@ -1,8 +1,10 @@
package proc package native
import ( import (
"debug/pe"
"errors" "errors"
"fmt" "fmt"
"io"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
@ -11,6 +13,8 @@ import (
"unsafe" "unsafe"
sys "golang.org/x/sys/windows" sys "golang.org/x/sys/windows"
"github.com/derekparker/delve/pkg/proc"
) )
// OSProcessDetails holds Windows specific information. // OSProcessDetails holds Windows specific information.
@ -19,6 +23,19 @@ type OSProcessDetails struct {
breakThread int 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. // Launch creates and begins debugging a new process.
func Launch(cmd []string, wd string) (*Process, error) { func Launch(cmd []string, wd string) (*Process, error) {
argv0Go, err := filepath.Abs(cmd[0]) argv0Go, err := filepath.Abs(cmd[0])
@ -35,7 +52,7 @@ func Launch(cmd []string, wd string) (*Process, error) {
_, closer, err := openExecutablePathPE(argv0Go) _, closer, err := openExecutablePathPE(argv0Go)
if err != nil { if err != nil {
return nil, NotExecutableErr return nil, proc.NotExecutableErr
} }
closer.Close() closer.Close()
@ -129,7 +146,7 @@ func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
} }
if tid == 0 { if tid == 0 {
dbp.postExit() 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 // Suspend all threads so that the call to _ContinueDebugEvent will
// not resume the target. // not resume the target.
@ -330,9 +347,9 @@ func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, e
// this exception anymore. // this exception anymore.
atbp := true atbp := true
if thread, found := dbp.threads[tid]; found { 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 { if _, err := thread.ReadMemory(data, exception.ExceptionRecord.ExceptionAddress); err == nil {
instr := dbp.bi.arch.BreakpointInstruction() instr := dbp.bi.Arch.BreakpointInstruction()
for i := range instr { for i := range instr {
if data[i] != instr[i] { if data[i] != instr[i] {
atbp = false atbp = false
@ -388,7 +405,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
} }
if tid == 0 { if tid == 0 {
dbp.postExit() dbp.postExit()
return nil, ProcessExitedError{Pid: dbp.pid, Status: exitCode} return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: exitCode}
} }
th := dbp.threads[tid] th := dbp.threads[tid]
return th, nil return th, nil

@ -1,4 +1,4 @@
package proc package native
import sys "golang.org/x/sys/unix" import sys "golang.org/x/sys/unix"

@ -1,4 +1,4 @@
package proc package native
import ( import (
"encoding/binary" "encoding/binary"

@ -1,4 +1,4 @@
package proc package native
import ( import (
"fmt" "fmt"

@ -1,4 +1,4 @@
package proc package native
// #include "threads_darwin.h" // #include "threads_darwin.h"
import "C" import "C"
@ -8,6 +8,8 @@ import (
"unsafe" "unsafe"
"golang.org/x/arch/x86/x86asm" "golang.org/x/arch/x86/x86asm"
"github.com/derekparker/delve/pkg/proc"
) )
// Regs represents CPU registers on an AMD64 processor. // Regs represents CPU registers on an AMD64 processor.
@ -34,10 +36,10 @@ type Regs struct {
fs uint64 fs uint64
gs uint64 gs uint64
gsBase uint64 gsBase uint64
fpregs []Register fpregs []proc.Register
} }
func (r *Regs) Slice() []Register { func (r *Regs) Slice() []proc.Register {
var regs = []struct { var regs = []struct {
k string k string
v uint64 v uint64
@ -65,12 +67,12 @@ func (r *Regs) Slice() []Register {
{"Gs", r.gs}, {"Gs", r.gs},
{"Gs_base", r.gsBase}, {"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 { for _, reg := range regs {
if reg.k == "Rflags" { if reg.k == "Rflags" {
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64) out = proc.AppendEflagReg(out, reg.k, reg.v)
} else { } else {
out = appendQwordReg(out, reg.k, reg.v) out = proc.AppendQwordReg(out, reg.k, reg.v)
} }
} }
out = append(out, r.fpregs...) 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`. // 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) thread := t.(*Thread)
kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc)) kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc))
if kret != C.KERN_SUCCESS { if kret != C.KERN_SUCCESS {
@ -273,10 +275,10 @@ func (r *Regs) Get(n int) (uint64, error) {
return r.r15, nil 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 state C.x86_thread_state64_t
var identity C.thread_identifier_info_data_t var identity C.thread_identifier_info_data_t
kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &state) 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") return nil, fmt.Errorf("could not get floating point registers")
} }
regs.fpregs = appendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw)))) regs.fpregs = proc.AppendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))
regs.fpregs = appendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw)))) regs.fpregs = proc.AppendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))
regs.fpregs = appendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw)) regs.fpregs = proc.AppendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw))
regs.fpregs = appendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop)) regs.fpregs = proc.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 = proc.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.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]} { 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) stb := C.GoBytes(unsafe.Pointer(st), 10)
mantissa := binary.LittleEndian.Uint64(stb[:8]) mantissa := binary.LittleEndian.Uint64(stb[:8])
exponent := binary.LittleEndian.Uint16(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 = proc.AppendMxcsrReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr))
regs.fpregs = appendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask)) 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]} { 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 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) kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &thread.os.registers)
if kret != C.KERN_SUCCESS { if kret != C.KERN_SUCCESS {
return nil, fmt.Errorf("could not save register contents") return nil, fmt.Errorf("could not save register contents")

@ -1,19 +1,21 @@
package proc package native
import ( import (
"fmt" "fmt"
"golang.org/x/arch/x86/x86asm" "golang.org/x/arch/x86/x86asm"
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/pkg/proc"
) )
// Regs is a wrapper for sys.PtraceRegs. // Regs is a wrapper for sys.PtraceRegs.
type Regs struct { type Regs struct {
regs *sys.PtraceRegs regs *sys.PtraceRegs
fpregs []Register fpregs []proc.Register
} }
func (r *Regs) Slice() []Register { func (r *Regs) Slice() []proc.Register {
var regs = []struct { var regs = []struct {
k string k string
v uint64 v uint64
@ -46,12 +48,12 @@ func (r *Regs) Slice() []Register {
{"Fs", r.regs.Fs}, {"Fs", r.regs.Fs},
{"Gs", r.regs.Gs}, {"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 { for _, reg := range regs {
if reg.k == "Eflags" { if reg.k == "Eflags" {
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64) out = proc.AppendEflagReg(out, reg.k, reg.v)
} else { } else {
out = appendQwordReg(out, reg.k, reg.v) out = proc.AppendQwordReg(out, reg.k, reg.v)
} }
} }
out = append(out, r.fpregs...) out = append(out, r.fpregs...)
@ -88,7 +90,7 @@ func (r *Regs) GAddr() (uint64, bool) {
} }
// SetPC sets RIP to the value specified by 'pc'. // 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) thread := t.(*Thread)
r.regs.SetPC(pc) r.regs.SetPC(pc)
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) }) 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 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 ( var (
regs sys.PtraceRegs regs sys.PtraceRegs
err error err error
@ -302,30 +304,30 @@ const (
_XSAVE_SSE_REGION_LEN = 416 _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 var fpregs PtraceXsave
thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) }) thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
// x87 registers // x87 registers
regs = appendWordReg(regs, "CW", fpregs.Cwd) regs = proc.AppendWordReg(regs, "CW", fpregs.Cwd)
regs = appendWordReg(regs, "SW", fpregs.Swd) regs = proc.AppendWordReg(regs, "SW", fpregs.Swd)
regs = appendWordReg(regs, "TW", fpregs.Ftw) regs = proc.AppendWordReg(regs, "TW", fpregs.Ftw)
regs = appendWordReg(regs, "FOP", fpregs.Fop) regs = proc.AppendWordReg(regs, "FOP", fpregs.Fop)
regs = appendQwordReg(regs, "FIP", fpregs.Rip) regs = proc.AppendQwordReg(regs, "FIP", fpregs.Rip)
regs = appendQwordReg(regs, "FDP", fpregs.Rdp) regs = proc.AppendQwordReg(regs, "FDP", fpregs.Rdp)
for i := 0; i < len(fpregs.StSpace); i += 4 { 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 // SSE registers
regs = appendFlagReg(regs, "MXCSR", uint64(fpregs.Mxcsr), mxcsrDescription, 32) regs = proc.AppendMxcsrReg(regs, "MXCSR", uint64(fpregs.Mxcsr))
regs = appendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask) regs = proc.AppendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask)
for i := 0; i < len(fpregs.XmmSpace); i += 16 { 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 { 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])
} }
} }

@ -1,10 +1,12 @@
package proc package native
import ( import (
"fmt" "fmt"
"unsafe" "unsafe"
"golang.org/x/arch/x86/x86asm" "golang.org/x/arch/x86/x86asm"
"github.com/derekparker/delve/pkg/proc"
) )
// Regs represents CPU registers on an AMD64 processor. // Regs represents CPU registers on an AMD64 processor.
@ -34,7 +36,7 @@ type Regs struct {
fltSave *_XMM_SAVE_AREA32 fltSave *_XMM_SAVE_AREA32
} }
func (r *Regs) Slice() []Register { func (r *Regs) Slice() []proc.Register {
var regs = []struct { var regs = []struct {
k string k string
v uint64 v uint64
@ -66,31 +68,31 @@ func (r *Regs) Slice() []Register {
if r.fltSave != nil { if r.fltSave != nil {
outlen += 6 + 8 + 2 + 16 outlen += 6 + 8 + 2 + 16
} }
out := make([]Register, 0, outlen) out := make([]proc.Register, 0, outlen)
for _, reg := range regs { for _, reg := range regs {
if reg.k == "Eflags" { if reg.k == "Eflags" {
out = append(out, Register{reg.k, eflagsDescription.Describe(reg.v, 64)}) out = proc.AppendEflagReg(out, reg.k, reg.v)
} else { } else {
out = appendQwordReg(out, reg.k, reg.v) out = proc.AppendQwordReg(out, reg.k, reg.v)
} }
} }
if r.fltSave != nil { if r.fltSave != nil {
out = appendWordReg(out, "CW", r.fltSave.ControlWord) out = proc.AppendWordReg(out, "CW", r.fltSave.ControlWord)
out = appendWordReg(out, "SW", r.fltSave.StatusWord) out = proc.AppendWordReg(out, "SW", r.fltSave.StatusWord)
out = appendWordReg(out, "TW", uint16(r.fltSave.TagWord)) out = proc.AppendWordReg(out, "TW", uint16(r.fltSave.TagWord))
out = appendWordReg(out, "FOP", r.fltSave.ErrorOpcode) out = proc.AppendWordReg(out, "FOP", r.fltSave.ErrorOpcode)
out = appendQwordReg(out, "FIP", uint64(r.fltSave.ErrorSelector)<<32|uint64(r.fltSave.ErrorOffset)) out = proc.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.AppendQwordReg(out, "FDP", uint64(r.fltSave.DataSelector)<<32|uint64(r.fltSave.DataOffset))
for i := range r.fltSave.FloatRegisters { 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 = proc.AppendMxcsrReg(out, "MXCSR", uint64(r.fltSave.MxCsr))
out = appendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask) out = proc.AppendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask)
for i := 0; i < len(r.fltSave.XmmRegisters); i += 16 { 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 return out
@ -129,7 +131,7 @@ func (r *Regs) GAddr() (uint64, bool) {
} }
// SetPC sets the RIP register to the value specified by `pc`. // 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) thread := t.(*Thread)
context := newCONTEXT() context := newCONTEXT()
context.ContextFlags = _CONTEXT_ALL context.ContextFlags = _CONTEXT_ALL
@ -298,10 +300,10 @@ func (r *Regs) Get(n int) (uint64, error) {
return r.r15, nil 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 := newCONTEXT()
context.ContextFlags = _CONTEXT_ALL context.ContextFlags = _CONTEXT_ALL
@ -348,7 +350,7 @@ func registers(thread *Thread, floatingPoint bool) (Registers, error) {
return regs, nil return regs, nil
} }
func (thread *Thread) saveRegisters() (Registers, error) { func (thread *Thread) saveRegisters() (proc.Registers, error) {
return nil, fmt.Errorf("not implemented: saveRegisters") return nil, fmt.Errorf("not implemented: saveRegisters")
} }

@ -1,6 +1,6 @@
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
package proc package native
import ( import (
"syscall" "syscall"

@ -1,4 +1,4 @@
package proc package native
import "unsafe" import "unsafe"

199
pkg/proc/native/threads.go Normal file

@ -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
}

@ -1,4 +1,4 @@
package proc package native
// #include "threads_darwin.h" // #include "threads_darwin.h"
// #include "proc_darwin.h" // #include "proc_darwin.h"
@ -81,14 +81,14 @@ func (t *Thread) resume() error {
return nil return nil
} }
func threadBlocked(t IThread) bool { func (t *Thread) Blocked() bool {
// TODO(dp) cache the func pc to remove this lookup // TODO(dp) cache the func pc to remove this lookup
regs, err := t.Registers(false) regs, err := t.Registers(false)
if err != nil { if err != nil {
return false return false
} }
pc := regs.PC() pc := regs.PC()
fn := t.BinInfo().goSymTable.PCToFunc(pc) fn := t.BinInfo().PCToFunc(pc)
if fn == nil { if fn == nil {
return false return false
} }
@ -104,7 +104,7 @@ func (t *Thread) stopped() bool {
return C.thread_blocked(t.os.threadAct) > C.int(0) 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 { if len(data) == 0 {
return 0, nil return 0, nil
} }

@ -1,9 +1,11 @@
package proc package native
import ( import (
"fmt" "fmt"
sys "golang.org/x/sys/unix" sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/pkg/proc"
) )
type WaitStatus sys.WaitStatus type WaitStatus sys.WaitStatus
@ -59,7 +61,7 @@ func (t *Thread) singleStep() (err error) {
if status != nil { if status != nil {
rs = status.ExitStatus() 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 { if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
return nil 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) regs, err := t.Registers(false)
if err != nil { if err != nil {
return false return false
} }
pc := regs.PC() 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")) { if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
return true return true
} }
return false return false
} }
func (t *Thread) saveRegisters() (Registers, error) { func (t *Thread) saveRegisters() (proc.Registers, error) {
var err error var err error
t.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(t.ID, &t.os.registers) }) t.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(t.ID, &t.os.registers) })
if err != nil { if err != nil {
@ -94,7 +96,7 @@ func (t *Thread) restoreRegisters() (err error) {
return 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 { if len(data) == 0 {
return return
} }

@ -1,9 +1,12 @@
package proc package native
import ( import (
"errors"
"syscall" "syscall"
sys "golang.org/x/sys/windows" sys "golang.org/x/sys/windows"
"github.com/derekparker/delve/pkg/proc"
) )
// WaitStatus is a synonym for the platform-specific WaitStatus // WaitStatus is a synonym for the platform-specific WaitStatus
@ -57,7 +60,7 @@ func (t *Thread) singleStep() error {
} }
if tid == 0 { if tid == 0 {
t.dbp.postExit() 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 { if t.dbp.os.breakThread == t.ID {
@ -103,7 +106,7 @@ func (t *Thread) resume() error {
return err return err
} }
func threadBlocked(t IThread) bool { func (t *Thread) Blocked() bool {
// TODO: Probably incorrect - what are the runtime functions that // TODO: Probably incorrect - what are the runtime functions that
// indicate blocking on Windows? // indicate blocking on Windows?
regs, err := t.Registers(false) regs, err := t.Registers(false)
@ -111,7 +114,7 @@ func threadBlocked(t IThread) bool {
return false return false
} }
pc := regs.PC() pc := regs.PC()
fn := t.BinInfo().goSymTable.PCToFunc(pc) fn := t.BinInfo().PCToFunc(pc)
if fn == nil { if fn == nil {
return false return false
} }
@ -129,7 +132,7 @@ func (t *Thread) stopped() bool {
return true 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 var count uintptr
err := _WriteProcessMemory(t.dbp.os.hProcess, addr, &data[0], uintptr(len(data)), &count) err := _WriteProcessMemory(t.dbp.os.hProcess, addr, &data[0], uintptr(len(data)), &count)
if err != nil { if err != nil {
@ -138,6 +141,8 @@ func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
return int(count), nil return int(count), nil
} }
var ErrShortRead = errors.New("short read")
func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) { func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
if len(buf) == 0 { if len(buf) == 0 {
return 0, nil return 0, nil

@ -1,6 +1,6 @@
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT // MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
package proc package native
import ( import (
"syscall" "syscall"

@ -8,48 +8,13 @@ import (
"go/ast" "go/ast"
"go/constant" "go/constant"
"go/token" "go/token"
"os"
"path/filepath" "path/filepath"
"runtime"
"strconv" "strconv"
"strings" "strings"
"sync"
"golang.org/x/debug/dwarf" "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 { type functionDebugInfo struct {
lowpc, highpc uint64 lowpc, highpc uint64
offset dwarf.Offset offset dwarf.Offset
@ -57,25 +22,6 @@ type functionDebugInfo struct {
var NotExecutableErr = errors.New("not an executable file") 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 // ProcessExitedError indicates that the process has exited and contains both
// process id and exit status. // process id and exit status.
type ProcessExitedError struct { 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) 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. // FindFileLocation returns the PC for a given file:line.
// Assumes that `file` is normailzed to lower case and '/' on Windows. // 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) pc, fn, err := bi.goSymTable.LineToPC(fileName, lineno)
if err != nil { if err != nil {
return 0, err return 0, err
@ -206,17 +46,13 @@ func FindFileLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint,
return pc, nil 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 // 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 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 // 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 // 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: // Note that setting breakpoints at that address will cause surprising behavior:
// https://github.com/derekparker/delve/issues/170 // 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) origfn := bi.goSymTable.LookupFunc(funcName)
if origfn == nil { if origfn == nil {
return 0, fmt.Errorf("Could not find function %s\n", funcName) 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 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. // Next continues execution until the next source line.
func Next(dbp Continuable) (err error) { func Next(dbp Continuable) (err error) {
if dbp.Exited() { if dbp.Exited() {
@ -343,33 +92,6 @@ func Next(dbp Continuable) (err error) {
return Continue(dbp) 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 // Continuable is the subinterface of target.Interface used to implement
// Continue/Next/etc. // Continue/Next/etc.
type Continuable interface { type Continuable interface {
@ -442,7 +164,7 @@ func Continue(dbp Continuable) error {
// here we either set a breakpoint into the destination of the CALL // here we either set a breakpoint into the destination of the CALL
// instruction or we determined that the called function is hidden, // instruction or we determined that the called function is hidden,
// either way we need to resume execution // 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 return err
} }
} else { } else {
@ -528,8 +250,9 @@ func Step(dbp Continuable) (err error) {
return Continue(dbp) return Continue(dbp)
} }
// Returns an expression that evaluates to true when the current goroutine is g // SameGoroutineCondition returns an expression that evaluates to true when
func sameGoroutineCondition(g *G) ast.Expr { // the current goroutine is g.
func SameGoroutineCondition(g *G) ast.Expr {
if g == nil { if g == nil {
return 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 // StepOut will continue until the current goroutine exits the
// function currently being executed or a deferred function is executed // function currently being executed or a deferred function is executed
func StepOut(dbp Continuable) error { func StepOut(dbp Continuable) error {
selg := dbp.SelectedGoroutine() selg := dbp.SelectedGoroutine()
curthread := dbp.CurrentThread() curthread := dbp.CurrentThread()
cond := sameGoroutineCondition(selg) cond := SameGoroutineCondition(selg)
topframe, err := topframe(selg, curthread) topframe, err := topframe(selg, curthread)
if err != nil { if err != nil {
@ -631,50 +327,12 @@ func StepOut(dbp Continuable) error {
return Continue(dbp) 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 // If the argument of GoroutinesInfo implements AllGCache GoroutinesInfo
// will use the pointer returned by AllGCache as a cache. // will use the pointer returned by AllGCache as a cache.
type AllGCache interface { type AllGCache interface {
AllGCache() *[]*G AllGCache() *[]*G
} }
func (dbp *Process) AllGCache() *[]*G {
return &dbp.allGCache
}
// GoroutinesInfo returns an array of G structures representing the information // GoroutinesInfo returns an array of G structures representing the information
// Delve cares about from the internal runtime G structure. // Delve cares about from the internal runtime G structure.
func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) { func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
@ -695,7 +353,7 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
threads := dbp.ThreadList() threads := dbp.ThreadList()
for _, th := range threads { for _, th := range threads {
if threadBlocked(th) { if th.Blocked() {
continue continue
} }
g, _ := GetG(th) g, _ := GetG(th)
@ -724,12 +382,12 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
return nil, err return nil, err
} }
} }
faddr := make([]byte, dbp.BinInfo().arch.PtrSize()) faddr := make([]byte, dbp.BinInfo().Arch.PtrSize())
_, err = dbp.CurrentThread().ReadMemory(faddr, uintptr(allgentryaddr)) _, err = dbp.CurrentThread().ReadMemory(faddr, uintptr(allgentryaddr))
allgptr := binary.LittleEndian.Uint64(faddr) allgptr := binary.LittleEndian.Uint64(faddr)
for i := uint64(0); i < allglen; i++ { 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 { if err != nil {
return nil, err return nil, err
} }
@ -742,7 +400,7 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
g.thread = thread g.Thread = thread
// Prefer actual thread location information. // Prefer actual thread location information.
g.CurrentLoc = *loc g.CurrentLoc = *loc
} }
@ -758,153 +416,8 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
return allg, nil return allg, nil
} }
func (g *G) Thread() IThread { func GetGoInformation(p Continuable) (ver GoVersion, isextld bool, err error) {
return g.thread scope := &EvalScope{0, 0, p.CurrentThread(), nil, p.BinInfo()}
}
// 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) {
vv, err := scope.packageVarAddr("runtime.buildVersion") vv, err := scope.packageVarAddr("runtime.buildVersion")
if err != nil { if err != nil {
return ver, false, fmt.Errorf("Could not determine version number: %v", err) 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 return
} }
rdr := scope.bi.DwarfReader() rdr := scope.BinInfo.DwarfReader()
rdr.Seek(0) rdr.Seek(0)
for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() { for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() {
if err != nil { if err != nil {
@ -976,11 +489,11 @@ func ConvertEvalScope(dbp EvalScopeConvertible, gid, frame int) (*EvalScope, err
return ThreadScope(ct) return ThreadScope(ct)
} }
var thread memoryReadWriter var thread MemoryReadWriter
if g.thread == nil { if g.Thread == nil {
thread = ct thread = ct
} else { } else {
thread = g.thread thread = g.Thread
} }
locs, err := g.Stacktrace(frame) 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 { func FrameToScope(p EvalScopeConvertible, frame Stackframe) *EvalScope {
return &EvalScope{frame.Current.PC, frame.CFA, p.CurrentThread(), nil, p.BinInfo()} 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)
}

@ -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")
}
}

File diff suppressed because it is too large Load Diff

@ -1,14 +1,16 @@
// +build linux darwin // +build linux darwin
package proc package proc_test
import ( import (
"runtime"
"syscall" "syscall"
"testing" "testing"
"time" "time"
"runtime"
"github.com/derekparker/delve/pkg/proc"
protest "github.com/derekparker/delve/pkg/proc/test" protest "github.com/derekparker/delve/pkg/proc/test"
"github.com/derekparker/delve/pkg/target"
) )
func TestIssue419(t *testing.T) { func TestIssue419(t *testing.T) {
@ -17,10 +19,10 @@ func TestIssue419(t *testing.T) {
return return
} }
// SIGINT directed at the inferior should be passed along not swallowed by delve // 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") _, err := setFunctionBreakpoint(p, "main.main")
assertNoError(err, t, "SetBreakpoint()") assertNoError(err, t, "SetBreakpoint()")
assertNoError(Continue(p), t, "Continue()") assertNoError(proc.Continue(p), t, "Continue()")
go func() { go func() {
for { for {
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
@ -39,8 +41,8 @@ func TestIssue419(t *testing.T) {
} }
} }
}() }()
err = Continue(p) err = proc.Continue(p)
if _, exited := err.(ProcessExitedError); !exited { if _, exited := err.(proc.ProcessExitedError); !exited {
t.Fatalf("Unexpected error after Continue(): %v\n", err) t.Fatalf("Unexpected error after Continue(): %v\n", err)
} }
}) })

@ -32,15 +32,18 @@ type Register struct {
Value string 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)}) 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)}) 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)}) 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)}) 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 var f float64
fset := false 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)}) 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) buf := bytes.NewReader(xmm)
var out bytes.Buffer var out bytes.Buffer
@ -141,20 +156,6 @@ func appendSSEReg(regs []Register, name string, xmm []byte) []Register {
var UnknownRegisterError = errors.New("unknown 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 flagRegisterDescr []flagDescr
type flagDescr struct { type flagDescr struct {
name string name string

@ -15,11 +15,11 @@ const runtimeStackBarrier = "runtime.stackBarrier"
// NoReturnAddr is returned when return address // NoReturnAddr is returned when return address
// could not be found during stack trace. // could not be found during stack trace.
type NoReturnAddr struct { type NoReturnAddr struct {
fn string Fn string
} }
func (nra NoReturnAddr) Error() 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. // Stackframe represents a frame in a system stack.
@ -54,12 +54,12 @@ func (g *G) stackIterator() (*stackIterator, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if g.thread != nil { if g.Thread != nil {
regs, err := g.thread.Registers(false) regs, err := g.Thread.Registers(false)
if err != nil { if err != nil {
return nil, err 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 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 atend bool
frame Stackframe frame Stackframe
bi *BinaryInfo bi *BinaryInfo
mem memoryReadWriter mem MemoryReadWriter
err error err error
stackBarrierPC uint64 stackBarrierPC uint64
@ -102,7 +102,7 @@ type savedLR struct {
val uint64 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 stackBarrierFunc := bi.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9
var stackBarrierPC uint64 var stackBarrierPC uint64
if stackBarrierFunc != nil && stkbar != nil { if stackBarrierFunc != nil && stkbar != nil {
@ -161,7 +161,7 @@ func (it *stackIterator) Next() bool {
it.top = false it.top = false
it.pc = it.frame.Ret it.pc = it.frame.Ret
it.sp = uint64(it.frame.CFA) 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 return true
} }
@ -185,8 +185,8 @@ func (it *stackIterator) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, err
return Stackframe{}, err return Stackframe{}, err
} }
// When no FDE is available attempt to use BP instead // When no FDE is available attempt to use BP instead
retaddr := uintptr(int(bp) + it.bi.arch.PtrSize()) retaddr := uintptr(int(bp) + it.bi.Arch.PtrSize())
cfa := int64(retaddr) + int64(it.bi.arch.PtrSize()) cfa := int64(retaddr) + int64(it.bi.Arch.PtrSize())
return it.newStackframe(pc, cfa, retaddr, nil, top) 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{} return Stackframe{}, NullAddrError{}
} }
f, l, fn := it.bi.PCToLine(pc) 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 { if err != nil {
return Stackframe{}, err return Stackframe{}, err
} }

@ -13,27 +13,9 @@ import (
"golang.org/x/debug/dwarf" "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. // IThread represents a thread.
type IThread interface { type IThread interface {
memoryReadWriter MemoryReadWriter
Location() (*Location, error) Location() (*Location, error)
// Breakpoint will return the breakpoint that this thread is stopped at or // Breakpoint will return the breakpoint that this thread is stopped at or
// nil if the thread is not stopped at any breakpoint. // nil if the thread is not stopped at any breakpoint.
@ -47,6 +29,8 @@ type IThread interface {
Arch() Arch Arch() Arch
BinInfo() *BinaryInfo BinInfo() *BinaryInfo
StepInstruction() error StepInstruction() error
// Blocked returns true if the thread is blocked
Blocked() bool
} }
// Location represents the location of a thread. // Location represents the location of a thread.
@ -59,88 +43,6 @@ type Location struct {
Fn *gosym.Func 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 // ThreadBlockedError is returned when the thread
// is blocked in the scheduler. // is blocked in the scheduler.
type ThreadBlockedError struct{} type ThreadBlockedError struct{}
@ -155,7 +57,7 @@ func topframe(g *G, thread IThread) (Stackframe, error) {
var err error var err error
if g == nil { if g == nil {
if threadBlocked(thread) { if thread.Blocked() {
return Stackframe{}, ThreadBlockedError{} return Stackframe{}, ThreadBlockedError{}
} }
frames, err = ThreadStacktrace(thread, 0) frames, err = ThreadStacktrace(thread, 0)
@ -194,11 +96,11 @@ func next(dbp Continuable, stepInto bool) error {
}() }()
csource := filepath.Ext(topframe.Current.File) != ".go" csource := filepath.Ext(topframe.Current.File) != ".go"
var thread memoryReadWriter = curthread var thread MemoryReadWriter = curthread
var regs Registers var regs Registers
if selg != nil && selg.Thread() != nil { if selg != nil && selg.Thread != nil {
thread = selg.Thread() thread = selg.Thread
regs, err = selg.Thread().Registers(false) regs, err = selg.Thread.Registers(false)
if err != nil { if err != nil {
return err return err
} }
@ -215,7 +117,7 @@ func next(dbp Continuable, stepInto bool) error {
} }
} }
cond := sameGoroutineCondition(selg) cond := SameGoroutineCondition(selg)
if stepInto { if stepInto {
for _, instr := range text { for _, instr := range text {
@ -361,15 +263,6 @@ func setInternalBreakpoints(dbp Continuable, curpc uint64, pcs []uint64, kind Br
return nil 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) { func getGVariable(thread IThread) (*Variable, error) {
arch := thread.Arch() arch := thread.Arch()
regs, err := thread.Registers(false) regs, err := thread.Registers(false)
@ -435,7 +328,7 @@ func GetG(thread IThread) (g *G, err error) {
g, err = gaddr.parseG() g, err = gaddr.parseG()
if err == nil { if err == nil {
g.thread = thread g.Thread = thread
if loc, err := thread.Location(); err == nil { if loc, err := thread.Location(); err == nil {
g.CurrentLoc = *loc g.CurrentLoc = *loc
} }
@ -443,29 +336,6 @@ func GetG(thread IThread) (g *G, err error) {
return 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. // ThreadScope returns an EvalScope for this thread.
func ThreadScope(thread IThread) (*EvalScope, error) { func ThreadScope(thread IThread) (*EvalScope, error) {
locations, err := ThreadStacktrace(thread, 0) 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 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 { func onRuntimeBreakpoint(thread IThread) bool {
loc, err := thread.Location() loc, err := thread.Location()
if err != nil { if err != nil {
@ -551,13 +391,5 @@ func onNextGoroutine(thread IThread, breakpoints map[uint64]*Breakpoint) (bool,
bp.Kind = NextDeferBreakpoint bp.Kind = NextDeferBreakpoint
}() }()
} }
return bp.checkCondition(thread) 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
} }

@ -69,7 +69,7 @@ func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return pointerTo(ptyp, bi.arch), nil return pointerTo(ptyp, bi.Arch), nil
} }
return bi.findType(exprToString(expr)) return bi.findType(exprToString(expr))
} }
@ -380,7 +380,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
if err != nil { if err != nil {
return "", err return "", err
} }
prtyp := pointerTo(rtyp, _type.bi.arch) prtyp := pointerTo(rtyp, _type.bi.Arch)
uadd := _type.RealType.Common().ByteSize uadd := _type.RealType.Common().ByteSize
if ut := uncommon(_type, tflag); ut != nil { 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++ { for i := int64(0); i < inCount; i++ {
argtype := cursortyp.maybeDereference() argtype := cursortyp.maybeDereference()
cursortyp.Addr += uintptr(_type.bi.arch.PtrSize()) cursortyp.Addr += uintptr(_type.bi.Arch.PtrSize())
argtypename, _, err := nameOfRuntimeType(argtype) argtypename, _, err := nameOfRuntimeType(argtype)
if err != nil { if err != nil {
return "", err return "", err
@ -434,7 +434,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
buf.WriteString(" (") buf.WriteString(" (")
for i := int64(0); i < outCount; i++ { for i := int64(0); i < outCount; i++ {
argtype := cursortyp.maybeDereference() argtype := cursortyp.maybeDereference()
cursortyp.Addr += uintptr(_type.bi.arch.PtrSize()) cursortyp.Addr += uintptr(_type.bi.Arch.PtrSize())
argtypename, _, err := nameOfRuntimeType(argtype) argtypename, _, err := nameOfRuntimeType(argtype)
if err != nil { if err != nil {
return "", err return "", err
@ -582,7 +582,7 @@ func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
prtyp := pointerTo(rtyp, _type.bi.arch) prtyp := pointerTo(rtyp, _type.bi.Arch)
uintptrtyp, err := _type.bi.findType("uintptr") uintptrtyp, err := _type.bi.findType("uintptr")
if err != nil { if err != nil {
@ -602,7 +602,7 @@ func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType { newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType {
r := newStructType("[]"+elemtype.Common().Name, uintptr(3*uintptrtyp.Size())) 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, "len", uintptrtyp, uintptr(uintptrtyp.Size()))
appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size())) appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size()))
return &dwarf.SliceType{StructType: *r, ElemType: elemtype} return &dwarf.SliceType{StructType: *r, ElemType: elemtype}

@ -50,7 +50,7 @@ type Variable struct {
DwarfType dwarf.Type DwarfType dwarf.Type
RealType dwarf.Type RealType dwarf.Type
Kind reflect.Kind Kind reflect.Kind
mem memoryReadWriter mem MemoryReadWriter
bi *BinaryInfo bi *BinaryInfo
Value constant.Value Value constant.Value
@ -129,7 +129,7 @@ type G struct {
CurrentLoc Location CurrentLoc Location
// Thread that this goroutine is currently allocated to // Thread that this goroutine is currently allocated to
thread IThread Thread IThread
variable *Variable variable *Variable
} }
@ -137,11 +137,11 @@ type G struct {
// EvalScope is the scope for variable evaluation. Contains the thread, // EvalScope is the scope for variable evaluation. Contains the thread,
// current location (PC), and canonical frame address. // current location (PC), and canonical frame address.
type EvalScope struct { type EvalScope struct {
PC uint64 // Current instruction of the evaluation frame PC uint64 // Current instruction of the evaluation frame
CFA int64 // Stack address of the evaluation frame CFA int64 // Stack address of the evaluation frame
Mem memoryReadWriter // Target's memory Mem MemoryReadWriter // Target's memory
gvar *Variable Gvar *Variable
bi *BinaryInfo BinInfo *BinaryInfo
} }
// IsNilErr is returned when a variable is nil. // 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 { 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 { 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) 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{ v := &Variable{
Name: name, Name: name,
Addr: addr, Addr: addr,
@ -191,7 +191,7 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, bi *BinaryInfo
v.stride = 1 v.stride = 1
v.fieldType = &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}} v.fieldType = &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}}
if v.Addr != 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: case *dwarf.SliceType:
v.Kind = reflect.Slice 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} v := &Variable{Value: val, mem: mem, loaded: true}
switch val.Kind() { switch val.Kind() {
case constant.Int: case constant.Int:
@ -322,33 +322,23 @@ func (v *Variable) toField(field *dwarf.StructField) (*Variable, error) {
// DwarfReader returns the DwarfReader containing the // DwarfReader returns the DwarfReader containing the
// Dwarf information for the target process. // Dwarf information for the target process.
func (scope *EvalScope) DwarfReader() *reader.Reader { func (scope *EvalScope) DwarfReader() *reader.Reader {
return scope.bi.DwarfReader() return scope.BinInfo.DwarfReader()
} }
// Type returns the Dwarf type entry at `offset`. // Type returns the Dwarf type entry at `offset`.
func (scope *EvalScope) Type(offset dwarf.Offset) (dwarf.Type, error) { 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. // PtrSize returns the size of a pointer.
func (scope *EvalScope) PtrSize() int { func (scope *EvalScope) PtrSize() int {
return scope.bi.arch.PtrSize() return scope.BinInfo.Arch.PtrSize()
} }
// ChanRecvBlocked returns whether the goroutine is blocked on // ChanRecvBlocked returns whether the goroutine is blocked on
// a channel read operation. // a channel read operation.
func (g *G) ChanRecvBlocked() bool { func (g *G) ChanRecvBlocked() bool {
return (g.thread == nil) && (g.WaitReason == chanRecv) 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
} }
// NoGError returned when a G could not be found // NoGError returned when a G could not be found
@ -367,7 +357,7 @@ func (gvar *Variable) parseG() (*G, error) {
_, deref := gvar.RealType.(*dwarf.PtrType) _, deref := gvar.RealType.(*dwarf.PtrType)
if deref { if deref {
gaddrbytes := make([]byte, gvar.bi.arch.PtrSize()) gaddrbytes := make([]byte, gvar.bi.Arch.PtrSize())
_, err := mem.ReadMemory(gaddrbytes, uintptr(gaddr)) _, err := mem.ReadMemory(gaddrbytes, uintptr(gaddr))
if err != nil { if err != nil {
return nil, fmt.Errorf("error derefing *G %s", err) return nil, fmt.Errorf("error derefing *G %s", err)
@ -376,8 +366,8 @@ func (gvar *Variable) parseG() (*G, error) {
} }
if gaddr == 0 { if gaddr == 0 {
id := 0 id := 0
if thread, ok := mem.(*Thread); ok { if thread, ok := mem.(IThread); ok {
id = thread.ID id = thread.ThreadID()
} }
return nil, NoGError{tid: id} 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) { func (scope *EvalScope) extractVarInfo(varName string) (*Variable, error) {
reader := scope.DwarfReader() reader := scope.DwarfReader()
off, err := scope.bi.findFunctionDebugInfo(scope.PC) off, err := scope.BinInfo.findFunctionDebugInfo(scope.PC)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -655,19 +645,6 @@ func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
return vars, nil 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) { func (scope *EvalScope) packageVarAddr(name string) (*Variable, error) {
reader := scope.DwarfReader() reader := scope.DwarfReader()
for entry, err := reader.NextPackageVariable(); entry != nil; entry, err = reader.NextPackageVariable() { for entry, err := reader.NextPackageVariable(); entry != nil; entry, err = reader.NextPackageVariable() {
@ -941,7 +918,7 @@ func (v *Variable) setValue(y *Variable) error {
return err 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 // string data structure is always two ptrs in size. Addr, followed by len
// http://research.swtch.com/godata // http://research.swtch.com/godata
@ -971,7 +948,7 @@ func readStringInfo(mem memoryReadWriter, arch Arch, addr uintptr) (uintptr, int
return addr, strlen, nil 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 count := strlen
if count > int64(cfg.MaxStringLen) { if count > int64(cfg.MaxStringLen) {
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)) 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 var n int64
val := make([]byte, int(size)) val := make([]byte, int(size))
@ -1140,11 +1117,11 @@ func (v *Variable) writeUint(value uint64, size int64) error {
binary.LittleEndian.PutUint64(val, uint64(value)) binary.LittleEndian.PutUint64(val, uint64(value))
} }
_, err := v.mem.writeMemory(v.Addr, val) _, err := v.mem.WriteMemory(v.Addr, val)
return err 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 var n uint64
val := make([]byte, int(size)) val := make([]byte, int(size))
@ -1201,19 +1178,19 @@ func (v *Variable) writeFloatRaw(f float64, size int64) error {
binary.Write(buf, binary.LittleEndian, n) binary.Write(buf, binary.LittleEndian, n)
} }
_, err := v.mem.writeMemory(v.Addr, buf.Bytes()) _, err := v.mem.WriteMemory(v.Addr, buf.Bytes())
return err return err
} }
func (v *Variable) writeBool(value bool) error { func (v *Variable) writeBool(value bool) error {
val := []byte{0} val := []byte{0}
val[0] = *(*byte)(unsafe.Pointer(&value)) val[0] = *(*byte)(unsafe.Pointer(&value))
_, err := v.mem.writeMemory(v.Addr, val) _, err := v.mem.WriteMemory(v.Addr, val)
return err return err
} }
func (v *Variable) readFunctionPtr() { 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) _, err := v.mem.ReadMemory(val, v.Addr)
if err != nil { if err != nil {
v.Unreadable = err v.Unreadable = err
@ -1644,7 +1621,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
if kind&kindDirectIface == 0 { if kind&kindDirectIface == 0 {
realtyp := resolveTypedef(typ) realtyp := resolveTypedef(typ)
if _, isptr := realtyp.(*dwarf.PtrType); !isptr { 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 // Fetches all variables of a specific type in the current function scope
func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) { func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) {
reader := scope.DwarfReader() reader := scope.DwarfReader()
off, err := scope.bi.findFunctionDebugInfo(scope.PC) off, err := scope.BinInfo.findFunctionDebugInfo(scope.PC)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -5,6 +5,9 @@ import (
"go/ast" "go/ast"
"github.com/derekparker/delve/pkg/proc" "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 // Target represents the target of the debugger. This
@ -89,6 +92,6 @@ type VariableEval interface {
ConvertEvalScope(gid, frame int) (*proc.EvalScope, error) ConvertEvalScope(gid, frame int) (*proc.EvalScope, error)
} }
var _ Interface = &proc.Process{} var _ Interface = &native.Process{}
var _ Interface = &proc.CoreProcess{} var _ Interface = &core.CoreProcess{}
var _ Interface = &proc.GdbserverProcess{} var _ Interface = &gdbserial.GdbserverProcess{}

@ -201,7 +201,7 @@ func ConvertFunction(fn *gosym.Func) *Function {
// ConvertGoroutine converts from proc.G to api.Goroutine. // ConvertGoroutine converts from proc.G to api.Goroutine.
func ConvertGoroutine(g *proc.G) *Goroutine { func ConvertGoroutine(g *proc.G) *Goroutine {
th := g.Thread() th := g.Thread
tid := 0 tid := 0
if th != nil { if th != nil {
tid = th.ThreadID() tid = th.ThreadID()

@ -14,6 +14,9 @@ import (
"time" "time"
"github.com/derekparker/delve/pkg/proc" "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/pkg/target"
"github.com/derekparker/delve/service/api" "github.com/derekparker/delve/service/api"
) )
@ -77,7 +80,7 @@ func New(config *Config) (*Debugger, error) {
case d.config.CoreFile != "": case d.config.CoreFile != "":
log.Printf("opening core file %s (executable %s)", d.config.CoreFile, d.config.ProcessArgs[0]) 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 { if err != nil {
return nil, err return nil, err
} }
@ -100,14 +103,14 @@ func New(config *Config) (*Debugger, error) {
func (d *Debugger) Launch(processArgs []string, wd string) (target.Interface, error) { func (d *Debugger) Launch(processArgs []string, wd string) (target.Interface, error) {
switch d.config.Backend { switch d.config.Backend {
case "native": case "native":
return proc.Launch(processArgs, wd) return native.Launch(processArgs, wd)
case "lldb": case "lldb":
return proc.LLDBLaunch(processArgs, wd) return gdbserial.LLDBLaunch(processArgs, wd)
case "default": case "default":
if runtime.GOOS == "darwin" { 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: default:
return nil, fmt.Errorf("unknown backend %q", d.config.Backend) 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) { func (d *Debugger) Attach(pid int, path string) (target.Interface, error) {
switch d.config.Backend { switch d.config.Backend {
case "native": case "native":
return proc.Attach(pid) return native.Attach(pid)
case "lldb": case "lldb":
if runtime.GOOS == "darwin" && path == "" { if runtime.GOOS == "darwin" && path == "" {
return nil, ErrNoAttachPath return nil, ErrNoAttachPath
} }
return proc.LLDBAttach(pid, path) return gdbserial.LLDBAttach(pid, path)
case "default": case "default":
if runtime.GOOS == "darwin" { if runtime.GOOS == "darwin" {
if path == "" { if path == "" {
return nil, ErrNoAttachPath return nil, ErrNoAttachPath
} }
return proc.LLDBAttach(pid, path) return gdbserial.LLDBAttach(pid, path)
} }
return proc.Attach(pid) return native.Attach(pid)
default: default:
return nil, fmt.Errorf("unknown backend %q", d.config.Backend) return nil, fmt.Errorf("unknown backend %q", d.config.Backend)
} }

@ -8,6 +8,8 @@ import (
"testing" "testing"
"github.com/derekparker/delve/pkg/proc" "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/pkg/target"
"github.com/derekparker/delve/service/api" "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 var err error
switch testBackend { switch testBackend {
case "native": case "native":
p, err = proc.Launch([]string{fixture.Path}, ".") p, err = native.Launch([]string{fixture.Path}, ".")
case "lldb": case "lldb":
p, err = proc.LLDBLaunch([]string{fixture.Path}, ".") p, err = gdbserial.LLDBLaunch([]string{fixture.Path}, ".")
default: default:
t.Fatalf("unknown backend %q", testBackend) t.Fatalf("unknown backend %q", testBackend)
} }