提交 15bac719 编写于 作者: A aarzilli 提交者: Derek Parker

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
上级 92dad944
......@@ -22,13 +22,13 @@ import (
type BinaryInfo struct {
lastModified time.Time // Time the executable of this process was last modified
goos string
GOOS string
closer io.Closer
// Maps package names to package paths, needed to lookup types inside DWARF info
packageMap map[string]string
arch Arch
Arch Arch
dwarf *dwarf.Data
frameEntries frame.FrameDescriptionEntries
lineInfo line.DebugLines
......@@ -46,12 +46,12 @@ var UnsupportedWindowsArchErr = errors.New("unsupported architecture of windows/
var UnsupportedDarwinArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported")
func NewBinaryInfo(goos, goarch string) BinaryInfo {
r := BinaryInfo{goos: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry)}
r := BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry)}
// TODO: find better way to determine proc arch (perhaps use executable file info)
switch goarch {
case "amd64":
r.arch = AMD64Arch(goos)
r.Arch = AMD64Arch(goos)
}
return r
......@@ -63,7 +63,7 @@ func (bininfo *BinaryInfo) LoadBinaryInfo(path string, wg *sync.WaitGroup) error
bininfo.lastModified = fi.ModTime()
}
switch bininfo.goos {
switch bininfo.GOOS {
case "linux":
return bininfo.LoadBinaryInfoElf(path, wg)
case "windows":
......@@ -112,6 +112,11 @@ func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *gosy
return bi.goSymTable.LineToPC(filename, lineno)
}
// PCToFunc returns the function containing the given PC address
func (bi *BinaryInfo) PCToFunc(pc uint64) *gosym.Func {
return bi.goSymTable.PCToFunc(pc)
}
func (bi *BinaryInfo) Close() error {
return bi.closer.Close()
}
......
......@@ -72,42 +72,30 @@ func (bp *Breakpoint) String() string {
return fmt.Sprintf("Breakpoint %d at %#v %s:%d (%d)", bp.ID, bp.Addr, bp.File, bp.Line, bp.TotalHitCount)
}
// ClearBreakpoint clears the specified breakpoint.
func (thread *Thread) ClearBreakpoint(bp *Breakpoint) (*Breakpoint, error) {
if _, err := thread.writeMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
return nil, fmt.Errorf("could not clear breakpoint %s", err)
}
return bp, nil
}
// BreakpointExistsError is returned when trying to set a breakpoint at
// an address that already has a breakpoint set for it.
type BreakpointExistsError struct {
file string
line int
addr uint64
File string
Line int
Addr uint64
}
func (bpe BreakpointExistsError) Error() string {
return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.File, bpe.Line, bpe.Addr)
}
// InvalidAddressError represents the result of
// attempting to set a breakpoint at an invalid address.
type InvalidAddressError struct {
address uint64
Address uint64
}
func (iae InvalidAddressError) Error() string {
return fmt.Sprintf("Invalid address %#v\n", iae.address)
}
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
_, err := thread.writeMemory(uintptr(addr), dbp.bi.arch.BreakpointInstruction())
return err
return fmt.Sprintf("Invalid address %#v\n", iae.Address)
}
func (bp *Breakpoint) checkCondition(thread IThread) (bool, error) {
// CheckCondition evaluates bp's condition on thread.
func (bp *Breakpoint) CheckCondition(thread IThread) (bool, error) {
if bp.Cond == nil {
return true, nil
}
......@@ -154,9 +142,9 @@ func (bp *Breakpoint) Internal() bool {
// NoBreakpointError is returned when trying to
// clear a breakpoint that does not exist.
type NoBreakpointError struct {
addr uint64
Addr uint64
}
func (nbp NoBreakpointError) Error() string {
return fmt.Sprintf("no breakpoint at %#v", nbp.addr)
return fmt.Sprintf("no breakpoint at %#v", nbp.Addr)
}
package proc
package core
import (
"debug/gosym"
......@@ -7,6 +7,8 @@ import (
"go/ast"
"io"
"sync"
"github.com/derekparker/delve/pkg/proc"
)
// A SplicedMemory represents a memory space formed from multiple regions,
......@@ -29,11 +31,11 @@ type SplicedMemory struct {
type readerEntry struct {
offset uintptr
length uintptr
reader MemoryReader
reader proc.MemoryReader
}
// Add adds a new region to the SplicedMemory, which may override existing regions.
func (r *SplicedMemory) Add(reader MemoryReader, off, length uintptr) {
func (r *SplicedMemory) Add(reader proc.MemoryReader, off, length uintptr) {
if length == 0 {
return
}
......@@ -141,12 +143,12 @@ func (r *OffsetReaderAt) ReadMemory(buf []byte, addr uintptr) (n int, err error)
}
type CoreProcess struct {
bi BinaryInfo
bi proc.BinaryInfo
core *Core
breakpoints map[uint64]*Breakpoint
breakpoints map[uint64]*proc.Breakpoint
currentThread *LinuxPrStatus
selectedGoroutine *G
allGCache []*G
selectedGoroutine *proc.G
allGCache []*proc.G
}
type CoreThread struct {
......@@ -165,8 +167,8 @@ func OpenCore(corePath, exePath string) (*CoreProcess, error) {
}
p := &CoreProcess{
core: core,
breakpoints: make(map[uint64]*Breakpoint),
bi: NewBinaryInfo("linux", "amd64"),
breakpoints: make(map[uint64]*proc.Breakpoint),
bi: proc.NewBinaryInfo("linux", "amd64"),
}
var wg sync.WaitGroup
......@@ -178,19 +180,18 @@ func OpenCore(corePath, exePath string) (*CoreProcess, error) {
break
}
scope := &EvalScope{0, 0, p.CurrentThread(), nil, &p.bi}
ver, isextld, err := scope.getGoInformation()
ver, isextld, err := proc.GetGoInformation(p)
if err != nil {
return nil, err
}
p.bi.arch.SetGStructOffset(ver, isextld)
p.selectedGoroutine, _ = GetG(p.CurrentThread())
p.bi.Arch.SetGStructOffset(ver, isextld)
p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
return p, nil
}
func (p *CoreProcess) BinInfo() *BinaryInfo {
func (p *CoreProcess) BinInfo() *proc.BinaryInfo {
return &p.bi
}
......@@ -202,16 +203,16 @@ func (thread *CoreThread) ReadMemory(data []byte, addr uintptr) (n int, err erro
return n, err
}
func (thread *CoreThread) writeMemory(addr uintptr, data []byte) (int, error) {
func (thread *CoreThread) WriteMemory(addr uintptr, data []byte) (int, error) {
return 0, ErrWriteCore
}
func (t *CoreThread) Location() (*Location, error) {
func (t *CoreThread) Location() (*proc.Location, error) {
f, l, fn := t.p.bi.PCToLine(t.th.Reg.Rip)
return &Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil
return &proc.Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil
}
func (t *CoreThread) Breakpoint() (*Breakpoint, bool, error) {
func (t *CoreThread) Breakpoint() (*proc.Breakpoint, bool, error) {
return nil, false, nil
}
......@@ -219,16 +220,16 @@ func (t *CoreThread) ThreadID() int {
return int(t.th.Pid)
}
func (t *CoreThread) Registers(floatingPoint bool) (Registers, error) {
func (t *CoreThread) Registers(floatingPoint bool) (proc.Registers, error) {
//TODO(aarzilli): handle floating point registers
return &t.th.Reg, nil
}
func (t *CoreThread) Arch() Arch {
return t.p.bi.arch
func (t *CoreThread) Arch() proc.Arch {
return t.p.bi.Arch
}
func (t *CoreThread) BinInfo() *BinaryInfo {
func (t *CoreThread) BinInfo() *proc.BinaryInfo {
return &t.p.bi
}
......@@ -236,19 +237,23 @@ func (t *CoreThread) StepInstruction() error {
return ErrContinueCore
}
func (p *CoreProcess) Breakpoints() map[uint64]*Breakpoint {
func (t *CoreThread) Blocked() bool {
return false
}
func (p *CoreProcess) Breakpoints() map[uint64]*proc.Breakpoint {
return p.breakpoints
}
func (p *CoreProcess) ClearBreakpoint(addr uint64) (*Breakpoint, error) {
return nil, NoBreakpointError{addr: addr}
func (p *CoreProcess) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
return nil, proc.NoBreakpointError{Addr: addr}
}
func (p *CoreProcess) ClearInternalBreakpoints() error {
return nil
}
func (p *CoreProcess) ContinueOnce() (IThread, error) {
func (p *CoreProcess) ContinueOnce() (proc.IThread, error) {
return nil, ErrContinueCore
}
......@@ -260,7 +265,7 @@ func (p *CoreProcess) RequestManualStop() error {
return nil
}
func (p *CoreProcess) CurrentThread() IThread {
func (p *CoreProcess) CurrentThread() proc.IThread {
return &CoreThread{p.currentThread, p}
}
......@@ -273,18 +278,18 @@ func (p *CoreProcess) Exited() bool {
}
func (p *CoreProcess) FindFileLocation(fileName string, lineNumber int) (uint64, error) {
return FindFileLocation(p.CurrentThread(), p.breakpoints, &p.bi, fileName, lineNumber)
return proc.FindFileLocation(p.CurrentThread(), p.breakpoints, &p.bi, fileName, lineNumber)
}
func (p *CoreProcess) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
return FirstPCAfterPrologue(p.CurrentThread(), p.breakpoints, &p.bi, fn, sameline)
return proc.FirstPCAfterPrologue(p.CurrentThread(), p.breakpoints, &p.bi, fn, sameline)
}
func (p *CoreProcess) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
return FindFunctionLocation(p.CurrentThread(), p.breakpoints, &p.bi, funcName, firstLine, lineOffset)
return proc.FindFunctionLocation(p.CurrentThread(), p.breakpoints, &p.bi, funcName, firstLine, lineOffset)
}
func (p *CoreProcess) AllGCache() *[]*G {
func (p *CoreProcess) AllGCache() *[]*proc.G {
return &p.allGCache
}
......@@ -304,16 +309,16 @@ func (p *CoreProcess) Running() bool {
return false
}
func (p *CoreProcess) SelectedGoroutine() *G {
func (p *CoreProcess) SelectedGoroutine() *proc.G {
return p.selectedGoroutine
}
func (p *CoreProcess) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
func (p *CoreProcess) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
return nil, ErrWriteCore
}
func (p *CoreProcess) SwitchGoroutine(gid int) error {
g, err := FindGoroutine(p, gid)
g, err := proc.FindGoroutine(p, gid)
if err != nil {
return err
}
......@@ -321,8 +326,8 @@ func (p *CoreProcess) SwitchGoroutine(gid int) error {
// user specified -1 and selectedGoroutine is nil
return nil
}
if g.thread != nil {
return p.SwitchThread(g.thread.ThreadID())
if g.Thread != nil {
return p.SwitchThread(g.Thread.ThreadID())
}
p.selectedGoroutine = g
return nil
......@@ -331,21 +336,21 @@ func (p *CoreProcess) SwitchGoroutine(gid int) error {
func (p *CoreProcess) SwitchThread(tid int) error {
if th, ok := p.core.Threads[tid]; ok {
p.currentThread = th
p.selectedGoroutine, _ = GetG(p.CurrentThread())
p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
return nil
}
return fmt.Errorf("thread %d does not exist", tid)
}
func (p *CoreProcess) ThreadList() []IThread {
r := make([]IThread, 0, len(p.core.Threads))
func (p *CoreProcess) ThreadList() []proc.IThread {
r := make([]proc.IThread, 0, len(p.core.Threads))
for _, v := range p.core.Threads {
r = append(r, &CoreThread{v, p})
}
return r
}
func (p *CoreProcess) FindThread(threadID int) (IThread, bool) {
func (p *CoreProcess) FindThread(threadID int) (proc.IThread, bool) {
t, ok := p.core.Threads[threadID]
return &CoreThread{t, p}, ok
}
package proc
package core
import (
"bytes"
......@@ -13,6 +13,7 @@ import (
"strings"
"testing"
"github.com/derekparker/delve/pkg/proc"
"github.com/derekparker/delve/pkg/proc/test"
)
......@@ -155,13 +156,13 @@ func TestCore(t *testing.T) {
t.Errorf("read apport log: %q, %v", apport, err)
t.Fatalf("ReadCore() failed: %v", err)
}
gs, err := GoroutinesInfo(p)
gs, err := proc.GoroutinesInfo(p)
if err != nil || len(gs) == 0 {
t.Fatalf("GoroutinesInfo() = %v, %v; wanted at least one goroutine", gs, err)
}
var panicking *G
var panickingStack []Stackframe
var panicking *proc.G
var panickingStack []proc.Stackframe
for _, g := range gs {
stack, err := g.Stacktrace(10)
if err != nil {
......@@ -178,7 +179,7 @@ func TestCore(t *testing.T) {
t.Fatalf("Didn't find a call to panic in goroutine stacks: %v", gs)
}
var mainFrame *Stackframe
var mainFrame *proc.Stackframe
// Walk backward, because the current function seems to be main.main
// in the actual call to panic().
for i := len(panickingStack) - 1; i >= 0; i-- {
......@@ -189,7 +190,7 @@ func TestCore(t *testing.T) {
if mainFrame == nil {
t.Fatalf("Couldn't find main in stack %v", panickingStack)
}
msg, err := FrameToScope(p, *mainFrame).EvalVariable("msg", LoadConfig{MaxStringLen: 64})
msg, err := proc.FrameToScope(p, *mainFrame).EvalVariable("msg", proc.LoadConfig{MaxStringLen: 64})
if err != nil {
t.Fatalf("Couldn't EvalVariable(msg, ...): %v", err)
}
......
package proc
package core
import (
"bytes"
......@@ -11,6 +11,8 @@ import (
"golang.org/x/debug/elf"
"golang.org/x/arch/x86/x86asm"
"github.com/derekparker/delve/pkg/proc"
)
// Copied from golang.org/x/sys/unix.PtraceRegs since it's not available on
......@@ -232,14 +234,14 @@ func (r *LinuxCoreRegisters) Get(n int) (uint64, error) {
return r.R15, nil
}
return 0, UnknownRegisterError
return 0, proc.UnknownRegisterError
}
func (r *LinuxCoreRegisters) SetPC(IThread, uint64) error {
func (r *LinuxCoreRegisters) SetPC(proc.IThread, uint64) error {
return errors.New("not supported")
}
func (r *LinuxCoreRegisters) Slice() []Register {
func (r *LinuxCoreRegisters) Slice() []proc.Register {
var regs = []struct {
k string
v uint64
......@@ -272,12 +274,12 @@ func (r *LinuxCoreRegisters) Slice() []Register {
{"Fs", r.Fs},
{"Gs", r.Gs},
}
out := make([]Register, 0, len(regs))
out := make([]proc.Register, 0, len(regs))
for _, reg := range regs {
if reg.k == "Eflags" {
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
out = proc.AppendEflagReg(out, reg.k, reg.v)
} else {
out = appendQwordReg(out, reg.k, reg.v)
out = proc.AppendQwordReg(out, reg.k, reg.v)
}
}
return out
......@@ -328,7 +330,7 @@ func readCore(corePath, exePath string) (*Core, error) {
}
type Core struct {
MemoryReader
proc.MemoryReader
Threads map[int]*LinuxPrStatus
Pid int
}
......@@ -448,7 +450,7 @@ func skipPadding(r io.ReadSeeker, pad int64) error {
return nil
}
func buildMemory(core *elf.File, exe io.ReaderAt, notes []*Note) MemoryReader {
func buildMemory(core *elf.File, exe io.ReaderAt, notes []*Note) proc.MemoryReader {
memory := &SplicedMemory{}
// For now, assume all file mappings are to the exe.
......
......@@ -35,16 +35,16 @@ func Disassemble(dbp DisassembleInfo, g *G, startPC, endPC uint64) ([]AsmInstruc
}
var regs Registers
var mem memoryReadWriter = dbp.CurrentThread()
if g.thread != nil {
mem = g.thread
regs, _ = g.thread.Registers(false)
var mem MemoryReadWriter = dbp.CurrentThread()
if g.Thread != nil {
mem = g.Thread
regs, _ = g.Thread.Registers(false)
}
return disassemble(mem, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC)
}
func disassemble(memrw memoryReadWriter, regs Registers, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, startPC, endPC uint64) ([]AsmInstruction, error) {
func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, startPC, endPC uint64) ([]AsmInstruction, error) {
mem := make([]byte, int(endPC-startPC))
_, err := memrw.ReadMemory(mem, uintptr(startPC))
if err != nil {
......
......@@ -63,7 +63,7 @@ func (inst *AsmInstruction) IsCall() bool {
return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL
}
func resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers, mem memoryReadWriter, bininfo *BinaryInfo) *Location {
func resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
return nil
}
......@@ -140,13 +140,9 @@ func init() {
}
}
func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
return FirstPCAfterPrologue(dbp.currentThread, dbp.breakpoints, &dbp.bi, fn, sameline)
}
// FirstPCAfterPrologue returns the address of the first instruction after the prologue for function fn
// If sameline is set FirstPCAfterPrologue will always return an address associated with the same line as fn.Entry
func FirstPCAfterPrologue(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fn *gosym.Func, sameline bool) (uint64, error) {
func FirstPCAfterPrologue(mem MemoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fn *gosym.Func, sameline bool) (uint64, error) {
text, err := disassemble(mem, nil, breakpoints, bi, fn.Entry, fn.End)
if err != nil {
return fn.Entry, err
......
......@@ -67,10 +67,10 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
// try to interpret the selector as a package variable
if maybePkg, ok := node.X.(*ast.Ident); ok {
if maybePkg.Name == "runtime" && node.Sel.Name == "curg" {
if scope.gvar == nil {
if scope.Gvar == nil {
return nilVariable, nil
}
return scope.gvar.clone(), nil
return scope.Gvar.clone(), nil
} else if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil {
return v, nil
}
......@@ -144,7 +144,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
fnnode = p.X
}
styp, err := scope.bi.findTypeExpr(fnnode)
styp, err := scope.BinInfo.findTypeExpr(fnnode)
if err != nil {
return nil, err
}
......@@ -152,7 +152,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
converr := fmt.Errorf("can not convert %q to %s", exprToString(node.Args[0]), typ.String())
v := newVariable("", 0, styp, scope.bi, scope.Mem)
v := newVariable("", 0, styp, scope.BinInfo, scope.Mem)
v.loaded = true
switch ttyp := typ.(type) {
......@@ -457,7 +457,7 @@ func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
return v, nil
}
// if it's not a local variable then it could be a package variable w/o explicit package name
_, _, fn := scope.bi.PCToLine(scope.PC)
_, _, fn := scope.BinInfo.PCToLine(scope.PC)
if fn != nil {
if v, err = scope.packageVarAddr(fn.PackageName() + "." + node.Name); err == nil {
v.Name = node.Name
......@@ -495,7 +495,7 @@ func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, err
if xv.Children[0].Addr == 0 {
return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type))
}
typ, err := scope.bi.findTypeExpr(node.Type)
typ, err := scope.BinInfo.findTypeExpr(node.Type)
if err != nil {
return nil, err
}
......@@ -640,7 +640,7 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) {
xev.OnlyAddr = true
typename := "*" + xev.DwarfType.Common().Name
rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.bi.arch.PtrSize()), Name: typename}, Type: xev.DwarfType})
rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.BinInfo.Arch.PtrSize()), Name: typename}, Type: xev.DwarfType})
rv.Children = []Variable{*xev}
rv.loaded = true
......
......@@ -59,7 +59,7 @@
//
// Therefore the following code will assume lldb-server-like behavior.
package proc
package gdbserial
import (
"debug/gosym"
......@@ -77,6 +77,8 @@ import (
"time"
"golang.org/x/arch/x86/x86asm"
"github.com/derekparker/delve/pkg/proc"
)
const (
......@@ -94,17 +96,17 @@ const heartbeatInterval = 10 * time.Second
// GdbserverProcess implements target.Interface using a connection to a
// debugger stub that understands Gdb Remote Serial Protocol.
type GdbserverProcess struct {
bi BinaryInfo
bi proc.BinaryInfo
conn gdbConn
threads map[int]*GdbserverThread
currentThread *GdbserverThread
selectedGoroutine *G
selectedGoroutine *proc.G
exited bool
ctrlC bool // ctrl-c was sent to stop inferior
breakpoints map[uint64]*Breakpoint
breakpoints map[uint64]*proc.Breakpoint
breakpointIDCounter int
internalBreakpointIDCounter int
......@@ -115,7 +117,7 @@ type GdbserverProcess struct {
process *exec.Cmd
allGCache []*G
allGCache []*proc.G
}
// GdbserverThread is a thread of GdbserverProcess.
......@@ -123,7 +125,7 @@ type GdbserverThread struct {
ID int
strID string
regs gdbRegisters
CurrentBreakpoint *Breakpoint
CurrentBreakpoint *proc.Breakpoint
BreakpointConditionMet bool
BreakpointConditionError error
p *GdbserverProcess
......@@ -175,8 +177,8 @@ func GdbserverConnect(addr string, path string, pid int, attempts int) (*Gdbserv
},
threads: make(map[int]*GdbserverThread),
bi: NewBinaryInfo(runtime.GOOS, runtime.GOARCH),
breakpoints: make(map[uint64]*Breakpoint),
bi: proc.NewBinaryInfo(runtime.GOOS, runtime.GOARCH),
breakpoints: make(map[uint64]*proc.Breakpoint),
gcmdok: true,
threadStopInfo: true,
}
......@@ -247,20 +249,19 @@ func GdbserverConnect(addr string, path string, pid int, attempts int) (*Gdbserv
}
}
scope := &EvalScope{0, 0, p.CurrentThread(), nil, &p.bi}
ver, isextld, err := scope.getGoInformation()
ver, isextld, err := proc.GetGoInformation(p)
if err != nil {
conn.Close()
p.bi.Close()
return nil, err
}
p.bi.arch.SetGStructOffset(ver, isextld)
p.selectedGoroutine, _ = GetG(p.CurrentThread())
p.bi.Arch.SetGStructOffset(ver, isextld)
p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
panicpc, err := p.FindFunctionLocation("runtime.startpanic", true, 0)
if err == nil {
bp, err := p.SetBreakpoint(panicpc, UserBreakpoint, nil)
bp, err := p.SetBreakpoint(panicpc, proc.UserBreakpoint, nil)
if err == nil {
bp.Name = "unrecovered-panic"
bp.ID = -1
......@@ -298,7 +299,7 @@ const debugserverExecutable = "/Library/Developer/CommandLineTools/Library/Priva
func LLDBLaunch(cmd []string, wd string) (*GdbserverProcess, error) {
// check that the argument to Launch is an executable file
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
return nil, NotExecutableErr
return nil, proc.NotExecutableErr
}
port := unusedPort()
......@@ -400,7 +401,7 @@ func (p *GdbserverProcess) loadProcessInfo(pid int) (int, string, error) {
return pid, pi["name"], nil
}
func (p *GdbserverProcess) BinInfo() *BinaryInfo {
func (p *GdbserverProcess) BinInfo() *proc.BinaryInfo {
return &p.bi
}
......@@ -417,39 +418,39 @@ func (p *GdbserverProcess) Running() bool {
}
func (p *GdbserverProcess) FindFileLocation(fileName string, lineNumber int) (uint64, error) {
return FindFileLocation(p.CurrentThread(), p.breakpoints, &p.bi, fileName, lineNumber)
return proc.FindFileLocation(p.CurrentThread(), p.breakpoints, &p.bi, fileName, lineNumber)
}
func (p *GdbserverProcess) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
return FirstPCAfterPrologue(p.CurrentThread(), p.breakpoints, &p.bi, fn, sameline)
return proc.FirstPCAfterPrologue(p.CurrentThread(), p.breakpoints, &p.bi, fn, sameline)
}
func (p *GdbserverProcess) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
return FindFunctionLocation(p.CurrentThread(), p.breakpoints, &p.bi, funcName, firstLine, lineOffset)
return proc.FindFunctionLocation(p.CurrentThread(), p.breakpoints, &p.bi, funcName, firstLine, lineOffset)
}
func (p *GdbserverProcess) FindThread(threadID int) (IThread, bool) {
func (p *GdbserverProcess) FindThread(threadID int) (proc.IThread, bool) {
thread, ok := p.threads[threadID]
return thread, ok
}
func (p *GdbserverProcess) ThreadList() []IThread {
r := make([]IThread, 0, len(p.threads))
func (p *GdbserverProcess) ThreadList() []proc.IThread {
r := make([]proc.IThread, 0, len(p.threads))
for _, thread := range p.threads {
r = append(r, thread)
}
return r
}
func (p *GdbserverProcess) CurrentThread() IThread {
func (p *GdbserverProcess) CurrentThread() proc.IThread {
return p.currentThread
}
func (p *GdbserverProcess) AllGCache() *[]*G {
func (p *GdbserverProcess) AllGCache() *[]*proc.G {
return &p.allGCache
}
func (p *GdbserverProcess) SelectedGoroutine() *G {
func (p *GdbserverProcess) SelectedGoroutine() *proc.G {
return p.selectedGoroutine
}
......@@ -460,9 +461,9 @@ const (
stopSignal = 0x13
)
func (p *GdbserverProcess) ContinueOnce() (IThread, error) {
func (p *GdbserverProcess) ContinueOnce() (proc.IThread, error) {
if p.exited {
return nil, &ProcessExitedError{Pid: p.conn.pid}
return nil, &proc.ProcessExitedError{Pid: p.conn.pid}
}
// step threads stopped at any breakpoint over their breakpoint
......@@ -491,7 +492,7 @@ continueLoop:
tu.done = false
threadID, sig, err = p.conn.resume(sig, &tu)
if err != nil {
if _, exited := err.(ProcessExitedError); exited {
if _, exited := err.(proc.ProcessExitedError); exited {
p.exited = true
}
return nil, err
......@@ -547,35 +548,35 @@ func (p *GdbserverProcess) StepInstruction() error {
if p.selectedGoroutine == nil {
return errors.New("cannot single step: no selected goroutine")
}
if p.selectedGoroutine.thread == nil {
if _, err := p.SetBreakpoint(p.selectedGoroutine.PC, NextBreakpoint, sameGoroutineCondition(p.selectedGoroutine)); err != nil {
if p.selectedGoroutine.Thread == nil {
if _, err := p.SetBreakpoint(p.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(p.selectedGoroutine)); err != nil {
return err
}
return Continue(p)
return proc.Continue(p)
}
p.allGCache = nil
if p.exited {
return &ProcessExitedError{Pid: p.conn.pid}
return &proc.ProcessExitedError{Pid: p.conn.pid}
}
p.selectedGoroutine.thread.(*GdbserverThread).clearBreakpointState()
err := p.selectedGoroutine.thread.(*GdbserverThread).StepInstruction()
p.selectedGoroutine.Thread.(*GdbserverThread).clearBreakpointState()
err := p.selectedGoroutine.Thread.(*GdbserverThread).StepInstruction()
if err != nil {
return err
}
return p.selectedGoroutine.thread.(*GdbserverThread).SetCurrentBreakpoint()
return p.selectedGoroutine.Thread.(*GdbserverThread).SetCurrentBreakpoint()
}
func (p *GdbserverProcess) SwitchThread(tid int) error {
if th, ok := p.threads[tid]; ok {
p.currentThread = th
p.selectedGoroutine, _ = GetG(p.CurrentThread())
p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
return nil
}
return fmt.Errorf("thread %d does not exist", tid)
}
func (p *GdbserverProcess) SwitchGoroutine(gid int) error {
g, err := FindGoroutine(p, gid)
g, err := proc.FindGoroutine(p, gid)
if err != nil {
return err
}
......@@ -583,8 +584,8 @@ func (p *GdbserverProcess) SwitchGoroutine(gid int) error {
// user specified -1 and selectedGoroutine is nil
return nil
}
if g.thread != nil {
return p.SwitchThread(g.thread.ThreadID())
if g.Thread != nil {
return p.SwitchThread(g.Thread.ThreadID())
}
p.selectedGoroutine = g
return nil
......@@ -605,7 +606,7 @@ func (p *GdbserverProcess) Kill() error {
return nil
}
err := p.conn.kill()
if _, exited := err.(ProcessExitedError); exited {
if _, exited := err.(proc.ProcessExitedError); exited {
p.exited = true
return nil
}
......@@ -615,7 +616,7 @@ func (p *GdbserverProcess) Kill() error {
func (p *GdbserverProcess) Detach(kill bool) error {
if kill {
if err := p.Kill(); err != nil {
if _, exited := err.(ProcessExitedError); !exited {
if _, exited := err.(proc.ProcessExitedError); !exited {
return err
}
}
......@@ -632,13 +633,13 @@ func (p *GdbserverProcess) Detach(kill bool) error {
return p.bi.Close()
}
func (p *GdbserverProcess) Breakpoints() map[uint64]*Breakpoint {
func (p *GdbserverProcess) Breakpoints() map[uint64]*proc.Breakpoint {
return p.breakpoints
}
func (p *GdbserverProcess) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
func (p *GdbserverProcess) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
if bp, ok := p.breakpoints[pc-uint64(p.bi.arch.BreakpointSize())]; ok {
if bp, ok := p.breakpoints[pc-uint64(p.bi.Arch.BreakpointSize())]; ok {
return bp, true
}
// Directly use addr to lookup breakpoint.
......@@ -648,16 +649,16 @@ func (p *GdbserverProcess) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
return nil, false
}
func (p *GdbserverProcess) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
func (p *GdbserverProcess) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
if bp, ok := p.breakpoints[addr]; ok {
return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
return nil, proc.BreakpointExistsError{bp.File, bp.Line, bp.Addr}
}
f, l, fn := p.bi.PCToLine(uint64(addr))
if fn == nil {
return nil, InvalidAddressError{address: addr}
return nil, proc.InvalidAddressError{Address: addr}
}
newBreakpoint := &Breakpoint{
newBreakpoint := &proc.Breakpoint{
FunctionName: fn.Name,
File: f,
Line: l,
......@@ -667,7 +668,7 @@ func (p *GdbserverProcess) SetBreakpoint(addr uint64, kind BreakpointKind, cond
HitCount: map[int]uint64{},
}
if kind != UserBreakpoint {
if kind != proc.UserBreakpoint {
p.internalBreakpointIDCounter++
newBreakpoint.ID = p.internalBreakpointIDCounter
} else {
......@@ -682,13 +683,13 @@ func (p *GdbserverProcess) SetBreakpoint(addr uint64, kind BreakpointKind, cond
return newBreakpoint, nil
}
func (p *GdbserverProcess) ClearBreakpoint(addr uint64) (*Breakpoint, error) {
func (p *GdbserverProcess) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
if p.exited {
return nil, &ProcessExitedError{Pid: p.conn.pid}
return nil, &proc.ProcessExitedError{Pid: p.conn.pid}
}
bp := p.breakpoints[addr]
if bp == nil {
return nil, NoBreakpointError{addr: addr}
return nil, proc.NoBreakpointError{Addr: addr}
}
if err := p.conn.clearBreakpoint(addr); err != nil {
......@@ -849,21 +850,21 @@ func (t *GdbserverThread) ReadMemory(data []byte, addr uintptr) (n int, err erro
return len(data), nil
}
func (t *GdbserverThread) writeMemory(addr uintptr, data []byte) (written int, err error) {
func (t *GdbserverThread) WriteMemory(addr uintptr, data []byte) (written int, err error) {
return t.p.conn.writeMemory(addr, data)
}
func (t *GdbserverThread) Location() (*Location, error) {
func (t *GdbserverThread) Location() (*proc.Location, error) {
regs, err := t.Registers(false)
if err != nil {
return nil, err
}
pc := regs.PC()
f, l, fn := t.p.bi.PCToLine(pc)
return &Location{PC: pc, File: f, Line: l, Fn: fn}, nil
return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
}
func (t *GdbserverThread) Breakpoint() (breakpoint *Breakpoint, active bool, condErr error) {
func (t *GdbserverThread) Breakpoint() (breakpoint *proc.Breakpoint, active bool, condErr error) {
return t.CurrentBreakpoint, (t.CurrentBreakpoint != nil && t.BreakpointConditionMet), t.BreakpointConditionError
}
......@@ -871,15 +872,15 @@ func (t *GdbserverThread) ThreadID() int {
return t.ID
}
func (t *GdbserverThread) Registers(floatingPoint bool) (Registers, error) {
func (t *GdbserverThread) Registers(floatingPoint bool) (proc.Registers, error) {
return &t.regs, nil
}
func (t *GdbserverThread) Arch() Arch {
return t.p.bi.arch
func (t *GdbserverThread) Arch() proc.Arch {
return t.p.bi.Arch
}
func (t *GdbserverThread) BinInfo() *BinaryInfo {
func (t *GdbserverThread) BinInfo() *proc.BinaryInfo {
return &t.p.bi
}
......@@ -903,16 +904,37 @@ func (t *GdbserverThread) StepInstruction() error {
return t.reloadRegisters()
}
func (t *GdbserverThread) Blocked() bool {
regs, err := t.Registers(false)
if err != nil {
return false
}
pc := regs.PC()
fn := t.BinInfo().PCToFunc(pc)
if fn == nil {
return false
}
switch fn.Name {
case "runtime.futex", "runtime.usleep", "runtime.clone":
return true
case "runtime.kevent":
return true
case "runtime.mach_semaphore_wait", "runtime.mach_semaphore_timedwait":
return true
}
return false
}
// loadGInstr returns the correct MOV instruction for the current
// OS/architecture that can be executed to load the address of G from an
// inferior's thread.
func (p *GdbserverProcess) loadGInstr() []byte {
switch p.bi.goos {
switch p.bi.GOOS {
case "windows":
//TODO(aarzilli): implement
panic("not implemented")
case "linux":
switch p.bi.arch.GStructOffset() {
switch p.bi.Arch.GStructOffset() {
case 0xfffffffffffffff8, 0x0:
// mov rcx,QWORD PTR fs:0xfffffffffffffff8
return []byte{0x64, 0x48, 0x8B, 0x0C, 0x25, 0xF8, 0xFF, 0xFF, 0xFF}
......@@ -1005,7 +1027,7 @@ func (t *GdbserverThread) readSomeRegisters(regNames ...string) error {
func (t *GdbserverThread) reloadGAtPC() error {
movinstr := t.p.loadGInstr()
if gdbserverThreadBlocked(t) {
if t.Blocked() {
t.regs.tls = 0
t.regs.gaddr = 0
t.regs.hasgaddr = true
......@@ -1038,13 +1060,13 @@ func (t *GdbserverThread) reloadGAtPC() error {
return err
}
_, err = t.writeMemory(uintptr(pc), movinstr)
_, err = t.WriteMemory(uintptr(pc), movinstr)
if err != nil {
return err
}
defer func() {
_, err0 := t.writeMemory(uintptr(pc), savedcode)
_, err0 := t.WriteMemory(uintptr(pc), savedcode)
if err == nil {
err = err0
}
......@@ -1077,7 +1099,7 @@ func (t *GdbserverThread) reloadGAtPC() error {
// a MOV instruction that loads the address of the current G in the RCX
// register.
func (t *GdbserverThread) reloadGAlloc() error {
if gdbserverThreadBlocked(t) {
if t.Blocked() {
t.regs.tls = 0
t.regs.gaddr = 0
t.regs.hasgaddr = true
......@@ -1118,27 +1140,6 @@ func (t *GdbserverThread) reloadGAlloc() error {
return err
}
func gdbserverThreadBlocked(t *GdbserverThread) bool {
regs, err := t.Registers(false)
if err != nil {
return false
}
pc := regs.PC()
fn := t.BinInfo().goSymTable.PCToFunc(pc)
if fn == nil {
return false
}
switch fn.Name {
case "runtime.futex", "runtime.usleep", "runtime.clone":
return true
case "runtime.kevent":
return true
case "runtime.mach_semaphore_wait", "runtime.mach_semaphore_timedwait":
return true
}
return false
}
func (t *GdbserverThread) clearBreakpointState() {
t.setbp = false
t.CurrentBreakpoint = nil
......@@ -1160,9 +1161,9 @@ func (thread *GdbserverThread) SetCurrentBreakpoint() error {
}
}
thread.CurrentBreakpoint = bp
thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.checkCondition(thread)
thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.CheckCondition(thread)
if thread.CurrentBreakpoint != nil && thread.BreakpointConditionMet {
if g, err := GetG(thread); err == nil {
if g, err := proc.GetG(thread); err == nil {
thread.CurrentBreakpoint.HitCount[g.ID]++
}
thread.CurrentBreakpoint.TotalHitCount++
......@@ -1365,10 +1366,10 @@ func (regs *gdbRegisters) Get(n int) (uint64, error) {
return regs.byName("r15"), nil
}
return 0, UnknownRegisterError
return 0, proc.UnknownRegisterError
}
func (regs *gdbRegisters) SetPC(thread IThread, pc uint64) error {
func (regs *gdbRegisters) SetPC(thread proc.IThread, pc uint64) error {
regs.setPC(pc)
t := thread.(*GdbserverThread)
if t.p.gcmdok {
......@@ -1378,20 +1379,20 @@ func (regs *gdbRegisters) SetPC(thread IThread, pc uint64) error {
return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value)
}
func (regs *gdbRegisters) Slice() []Register {
r := make([]Register, 0, len(regs.regsInfo))
func (regs *gdbRegisters) Slice() []proc.Register {
r := make([]proc.Register, 0, len(regs.regsInfo))
for _, reginfo := range regs.regsInfo {
switch {
case reginfo.Name == "eflags":
r = appendFlagReg(r, reginfo.Name, uint64(binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value)), eflagsDescription, 32)
r = proc.AppendEflagReg(r, reginfo.Name, uint64(binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value)))
case reginfo.Name == "mxcsr":
r = appendFlagReg(r, reginfo.Name, uint64(binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value)), mxcsrDescription, 32)
r = proc.AppendMxcsrReg(r, reginfo.Name, uint64(binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value)))
case reginfo.Bitsize == 16:
r = appendWordReg(r, reginfo.Name, binary.LittleEndian.Uint16(regs.regs[reginfo.Name].value))
r = proc.AppendWordReg(r, reginfo.Name, binary.LittleEndian.Uint16(regs.regs[reginfo.Name].value))
case reginfo.Bitsize == 32:
r = appendDwordReg(r, reginfo.Name, binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value))
r = proc.AppendDwordReg(r, reginfo.Name, binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value))
case reginfo.Bitsize == 64:
r = appendQwordReg(r, reginfo.Name, binary.LittleEndian.Uint64(regs.regs[reginfo.Name].value))
r = proc.AppendQwordReg(r, reginfo.Name, binary.LittleEndian.Uint64(regs.regs[reginfo.Name].value))
case reginfo.Bitsize == 80:
idx := 0
for _, stprefix := range []string{"stmm", "st"} {
......@@ -1401,10 +1402,10 @@ func (regs *gdbRegisters) Slice() []Register {
}
}
value := regs.regs[reginfo.Name].value
r = appendX87Reg(r, idx, binary.LittleEndian.Uint16(value[8:]), binary.LittleEndian.Uint64(value[:8]))
r = proc.AppendX87Reg(r, idx, binary.LittleEndian.Uint16(value[8:]), binary.LittleEndian.Uint64(value[:8]))
case reginfo.Bitsize == 128:
r = appendSSEReg(r, strings.ToUpper(reginfo.Name), regs.regs[reginfo.Name].value)
r = proc.AppendSSEReg(r, strings.ToUpper(reginfo.Name), regs.regs[reginfo.Name].value)
case reginfo.Bitsize == 256:
if !strings.HasPrefix(strings.ToLower(reginfo.Name), "ymm") {
......@@ -1413,8 +1414,8 @@ func (regs *gdbRegisters) Slice() []Register {
value := regs.regs[reginfo.Name].value
xmmName := "x" + reginfo.Name[1:]
r = appendSSEReg(r, strings.ToUpper(xmmName), value[:16])
r = appendSSEReg(r, strings.ToUpper(reginfo.Name), value[16:])
r = proc.AppendSSEReg(r, strings.ToUpper(xmmName), value[:16])
r = proc.AppendSSEReg(r, strings.ToUpper(reginfo.Name), value[16:])
}
}
return r
......
package proc
package gdbserial
import (
"bufio"
......@@ -12,6 +12,8 @@ import (
"strconv"
"strings"
"time"
"github.com/derekparker/delve/pkg/proc"
)
type gdbConn struct {
......@@ -630,7 +632,7 @@ func (conn *gdbConn) parseStopPacket(resp []byte, threadID string, tu *threadUpd
semicolon = len(resp)
}
status, _ := strconv.ParseUint(string(resp[1:semicolon]), 16, 8)
return false, stopPacket{}, ProcessExitedError{Pid: conn.pid, Status: int(status)}
return false, stopPacket{}, proc.ProcessExitedError{Pid: conn.pid, Status: int(status)}
case 'N':
// we were singlestepping the thread and the thread exited
......
// +build linux darwin
package proc
package gdbserial
import "syscall"
......
......@@ -11,15 +11,15 @@ type MemoryReader interface {
ReadMemory(buf []byte, addr uintptr) (n int, err error)
}
type memoryReadWriter interface {
type MemoryReadWriter interface {
MemoryReader
writeMemory(addr uintptr, data []byte) (written int, err error)
WriteMemory(addr uintptr, data []byte) (written int, err error)
}
type memCache struct {
cacheAddr uintptr
cache []byte
mem memoryReadWriter
mem MemoryReadWriter
}
func (m *memCache) contains(addr uintptr, size int) bool {
......@@ -35,11 +35,11 @@ func (m *memCache) ReadMemory(data []byte, addr uintptr) (n int, err error) {
return m.mem.ReadMemory(data, addr)
}
func (m *memCache) writeMemory(addr uintptr, data []byte) (written int, err error) {
return m.mem.writeMemory(addr, data)
func (m *memCache) WriteMemory(addr uintptr, data []byte) (written int, err error) {
return m.mem.WriteMemory(addr, data)
}
func cacheMemory(mem memoryReadWriter, addr uintptr, size int) memoryReadWriter {
func cacheMemory(mem MemoryReadWriter, addr uintptr, size int) MemoryReadWriter {
if !cacheEnabled {
return mem
}
......
......@@ -11,7 +11,7 @@ type moduleData struct {
typemapVar *Variable
}
func loadModuleData(bi *BinaryInfo, mem memoryReadWriter) (err error) {
func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) (err error) {
bi.loadModuleDataOnce.Do(func() {
scope := &EvalScope{0, 0, mem, nil, bi}
var md *Variable
......@@ -56,7 +56,7 @@ func loadModuleData(bi *BinaryInfo, mem memoryReadWriter) (err error) {
return
}
func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryReadWriter) (*Variable, error) {
func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryReadWriter) (*Variable, error) {
// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
if err := loadModuleData(bi, mem); err != nil {
return nil, err
......@@ -93,7 +93,7 @@ func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryRea
return newVariable("", res, rtyp, bi, mem), nil
}
func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
if err = loadModuleData(bi, mem); err != nil {
return "", "", 0, err
......@@ -118,7 +118,7 @@ func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem memoryRea
return loadName(bi, resv.Addr, mem)
}
func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem memoryReadWriter) (*Variable, error) {
func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem MemoryReadWriter) (*Variable, error) {
scope := &EvalScope{0, 0, mem, nil, bi}
reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs")
if err != nil {
......@@ -140,7 +140,7 @@ const (
nameflagHasPkg = 1 << 2
)
func loadName(bi *BinaryInfo, addr uintptr, mem memoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
func loadName(bi *BinaryInfo, addr uintptr, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
off := addr
namedata := make([]byte, 3)
_, err = mem.ReadMemory(namedata, off)
......
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
}
*/
package proc
package native
// #include "proc_darwin.h"
// #include "threads_darwin.h"
......@@ -15,6 +15,8 @@ import (
"unsafe"
sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/pkg/proc"
)
// OSProcessDetails holds Darwin specific information.
......@@ -36,7 +38,7 @@ type OSProcessDetails struct {
func Launch(cmd []string, wd string) (*Process, error) {
// check that the argument to Launch is an executable file
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
return nil, NotExecutableErr
return nil, proc.NotExecutableErr
}
argv0Go, err := filepath.Abs(cmd[0])
if err != nil {
......@@ -285,7 +287,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
return nil, err
}
dbp.postExit()
return nil, ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
case C.MACH_RCV_INTERRUPTED:
if !dbp.halt {
......@@ -393,7 +395,7 @@ func (dbp *Process) exitGuard(err error) error {
_, status, werr := dbp.wait(dbp.pid, sys.WNOHANG)
if werr == nil && status.Exited() {
dbp.postExit()
return ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
return proc.ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
}
return err
}
......
package proc
package native
import (
"bytes"
......@@ -16,6 +16,8 @@ import (
"time"
sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/pkg/proc"
)
// Process statuses
......@@ -43,34 +45,34 @@ type OSProcessDetails struct {
// to be supplied to that process. `wd` is working directory of the program.
func Launch(cmd []string, wd string) (*Process, error) {
var (
proc *exec.Cmd
err error
process *exec.Cmd
err error
)
// check that the argument to Launch is an executable file
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
return nil, NotExecutableErr
return nil, proc.NotExecutableErr
}
dbp := New(0)
dbp.execPtraceFunc(func() {
proc = exec.Command(cmd[0])
proc.Args = cmd
proc.Stdout = os.Stdout
proc.Stderr = os.Stderr
proc.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true}
process = exec.Command(cmd[0])
process.Args = cmd
process.Stdout = os.Stdout
process.Stderr = os.Stderr
process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true}
if wd != "" {
proc.Dir = wd
process.Dir = wd
}
err = proc.Start()
err = process.Start()
})
if err != nil {
return nil, err
}
dbp.pid = proc.Process.Pid
_, _, err = dbp.wait(proc.Process.Pid, 0)
dbp.pid = process.Process.Pid
_, _, err = dbp.wait(process.Process.Pid, 0)
if err != nil {
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
}
return initializeDebugProcess(dbp, proc.Path, false)
return initializeDebugProcess(dbp, process.Path, false)
}
// Attach to an existing process with the given PID.
......@@ -189,7 +191,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
if status.Exited() {
if wpid == dbp.pid {
dbp.postExit()
return nil, ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
return nil, proc.ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
}
delete(dbp.threads, wpid)
continue
......@@ -246,7 +248,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
// TODO(dp) alert user about unexpected signals here.
if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
if err == sys.ESRCH {
return nil, ProcessExitedError{Pid: dbp.pid}
return nil, proc.ProcessExitedError{Pid: dbp.pid}
}
return nil, err
}
......
package proc
package native
import (
"debug/pe"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
......@@ -11,6 +13,8 @@ import (
"unsafe"
sys "golang.org/x/sys/windows"
"github.com/derekparker/delve/pkg/proc"
)
// OSProcessDetails holds Windows specific information.
......@@ -19,6 +23,19 @@ type OSProcessDetails struct {
breakThread int
}
func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
f, err := os.OpenFile(path, 0, os.ModePerm)
if err != nil {
return nil, nil, err
}
peFile, err := pe.NewFile(f)
if err != nil {
f.Close()
return nil, nil, err
}
return peFile, f, nil
}
// Launch creates and begins debugging a new process.
func Launch(cmd []string, wd string) (*Process, error) {
argv0Go, err := filepath.Abs(cmd[0])
......@@ -35,7 +52,7 @@ func Launch(cmd []string, wd string) (*Process, error) {
_, closer, err := openExecutablePathPE(argv0Go)
if err != nil {
return nil, NotExecutableErr
return nil, proc.NotExecutableErr
}
closer.Close()
......@@ -129,7 +146,7 @@ func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
}
if tid == 0 {
dbp.postExit()
return nil, ProcessExitedError{Pid: dbp.pid, Status: exitCode}
return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: exitCode}
}
// Suspend all threads so that the call to _ContinueDebugEvent will
// not resume the target.
......@@ -330,9 +347,9 @@ func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, e
// this exception anymore.
atbp := true
if thread, found := dbp.threads[tid]; found {
data := make([]byte, dbp.bi.arch.BreakpointSize())
data := make([]byte, dbp.bi.Arch.BreakpointSize())
if _, err := thread.ReadMemory(data, exception.ExceptionRecord.ExceptionAddress); err == nil {
instr := dbp.bi.arch.BreakpointInstruction()
instr := dbp.bi.Arch.BreakpointInstruction()
for i := range instr {
if data[i] != instr[i] {
atbp = false
......@@ -388,7 +405,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
}
if tid == 0 {
dbp.postExit()
return nil, ProcessExitedError{Pid: dbp.pid, Status: exitCode}
return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: exitCode}
}
th := dbp.threads[tid]
return th, nil
......
package proc
package native
import sys "golang.org/x/sys/unix"
......
package proc
package native
import (
"encoding/binary"
......
package proc
package native
// #include "threads_darwin.h"
import "C"
......@@ -8,6 +8,8 @@ import (
"unsafe"
"golang.org/x/arch/x86/x86asm"
"github.com/derekparker/delve/pkg/proc"
)
// Regs represents CPU registers on an AMD64 processor.
......@@ -34,10 +36,10 @@ type Regs struct {
fs uint64
gs uint64
gsBase uint64
fpregs []Register
fpregs []proc.Register
}
func (r *Regs) Slice() []Register {
func (r *Regs) Slice() []proc.Register {
var regs = []struct {
k string
v uint64
......@@ -65,12 +67,12 @@ func (r *Regs) Slice() []Register {
{"Gs", r.gs},
{"Gs_base", r.gsBase},
}
out := make([]Register, 0, len(regs)+len(r.fpregs))
out := make([]proc.Register, 0, len(regs)+len(r.fpregs))
for _, reg := range regs {
if reg.k == "Rflags" {
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
out = proc.AppendEflagReg(out, reg.k, reg.v)
} else {
out = appendQwordReg(out, reg.k, reg.v)
out = proc.AppendQwordReg(out, reg.k, reg.v)
}
}
out = append(out, r.fpregs...)
......@@ -110,7 +112,7 @@ func (r *Regs) GAddr() (uint64, bool) {
}
// SetPC sets the RIP register to the value specified by `pc`.
func (r *Regs) SetPC(t IThread, pc uint64) error {
func (r *Regs) SetPC(t proc.IThread, pc uint64) error {
thread := t.(*Thread)
kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc))
if kret != C.KERN_SUCCESS {
......@@ -273,10 +275,10 @@ func (r *Regs) Get(n int) (uint64, error) {
return r.r15, nil
}
return 0, UnknownRegisterError
return 0, proc.UnknownRegisterError
}
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
var state C.x86_thread_state64_t
var identity C.thread_identifier_info_data_t
kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &state)
......@@ -332,31 +334,31 @@ func registers(thread *Thread, floatingPoint bool) (Registers, error) {
return nil, fmt.Errorf("could not get floating point registers")
}
regs.fpregs = appendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))
regs.fpregs = appendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))
regs.fpregs = appendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw))
regs.fpregs = appendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop))
regs.fpregs = appendQwordReg(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip))
regs.fpregs = appendQwordReg(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp))
regs.fpregs = proc.AppendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))
regs.fpregs = proc.AppendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))
regs.fpregs = proc.AppendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw))
regs.fpregs = proc.AppendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop))
regs.fpregs = proc.AppendQwordReg(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip))
regs.fpregs = proc.AppendQwordReg(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp))
for i, st := range []*C.char{&fpstate.__fpu_stmm0.__mmst_reg[0], &fpstate.__fpu_stmm1.__mmst_reg[0], &fpstate.__fpu_stmm2.__mmst_reg[0], &fpstate.__fpu_stmm3.__mmst_reg[0], &fpstate.__fpu_stmm4.__mmst_reg[0], &fpstate.__fpu_stmm5.__mmst_reg[0], &fpstate.__fpu_stmm6.__mmst_reg[0], &fpstate.__fpu_stmm7.__mmst_reg[0]} {
stb := C.GoBytes(unsafe.Pointer(st), 10)
mantissa := binary.LittleEndian.Uint64(stb[:8])
exponent := binary.LittleEndian.Uint16(stb[8:])
regs.fpregs = appendX87Reg(regs.fpregs, i, exponent, mantissa)
regs.fpregs = proc.AppendX87Reg(regs.fpregs, i, exponent, mantissa)
}
regs.fpregs = appendFlagReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr), mxcsrDescription, 32)
regs.fpregs = appendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask))
regs.fpregs = proc.AppendMxcsrReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr))
regs.fpregs = proc.AppendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask))
for i, xmm := range []*C.char{&fpstate.__fpu_xmm0.__xmm_reg[0], &fpstate.__fpu_xmm1.__xmm_reg[0], &fpstate.__fpu_xmm2.__xmm_reg[0], &fpstate.__fpu_xmm3.__xmm_reg[0], &fpstate.__fpu_xmm4.__xmm_reg[0], &fpstate.__fpu_xmm5.__xmm_reg[0], &fpstate.__fpu_xmm6.__xmm_reg[0], &fpstate.__fpu_xmm7.__xmm_reg[0], &fpstate.__fpu_xmm8.__xmm_reg[0], &fpstate.__fpu_xmm9.__xmm_reg[0], &fpstate.__fpu_xmm10.__xmm_reg[0], &fpstate.__fpu_xmm11.__xmm_reg[0], &fpstate.__fpu_xmm12.__xmm_reg[0], &fpstate.__fpu_xmm13.__xmm_reg[0], &fpstate.__fpu_xmm14.__xmm_reg[0], &fpstate.__fpu_xmm15.__xmm_reg[0]} {
regs.fpregs = appendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16))
regs.fpregs = proc.AppendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16))
}
}
return regs, nil
}
func (thread *Thread) saveRegisters() (Registers, error) {
func (thread *Thread) saveRegisters() (proc.Registers, error) {
kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &thread.os.registers)
if kret != C.KERN_SUCCESS {
return nil, fmt.Errorf("could not save register contents")
......
package proc
package native
import (
"fmt"
"golang.org/x/arch/x86/x86asm"
sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/pkg/proc"
)
// Regs is a wrapper for sys.PtraceRegs.
type Regs struct {
regs *sys.PtraceRegs
fpregs []Register
fpregs []proc.Register
}
func (r *Regs) Slice() []Register {
func (r *Regs) Slice() []proc.Register {
var regs = []struct {
k string
v uint64
......@@ -46,12 +48,12 @@ func (r *Regs) Slice() []Register {
{"Fs", r.regs.Fs},
{"Gs", r.regs.Gs},
}
out := make([]Register, 0, len(regs)+len(r.fpregs))
out := make([]proc.Register, 0, len(regs)+len(r.fpregs))
for _, reg := range regs {
if reg.k == "Eflags" {
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
out = proc.AppendEflagReg(out, reg.k, reg.v)
} else {
out = appendQwordReg(out, reg.k, reg.v)
out = proc.AppendQwordReg(out, reg.k, reg.v)
}
}
out = append(out, r.fpregs...)
......@@ -88,7 +90,7 @@ func (r *Regs) GAddr() (uint64, bool) {
}
// SetPC sets RIP to the value specified by 'pc'.
func (r *Regs) SetPC(t IThread, pc uint64) (err error) {
func (r *Regs) SetPC(t proc.IThread, pc uint64) (err error) {
thread := t.(*Thread)
r.regs.SetPC(pc)
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) })
......@@ -249,10 +251,10 @@ func (r *Regs) Get(n int) (uint64, error) {
return r.regs.R15, nil
}
return 0, UnknownRegisterError
return 0, proc.UnknownRegisterError
}
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
var (
regs sys.PtraceRegs
err error
......@@ -302,30 +304,30 @@ const (
_XSAVE_SSE_REGION_LEN = 416
)
func (thread *Thread) fpRegisters() (regs []Register, err error) {
func (thread *Thread) fpRegisters() (regs []proc.Register, err error) {
var fpregs PtraceXsave
thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
// x87 registers
regs = appendWordReg(regs, "CW", fpregs.Cwd)
regs = appendWordReg(regs, "SW", fpregs.Swd)
regs = appendWordReg(regs, "TW", fpregs.Ftw)
regs = appendWordReg(regs, "FOP", fpregs.Fop)
regs = appendQwordReg(regs, "FIP", fpregs.Rip)
regs = appendQwordReg(regs, "FDP", fpregs.Rdp)
regs = proc.AppendWordReg(regs, "CW", fpregs.Cwd)
regs = proc.AppendWordReg(regs, "SW", fpregs.Swd)
regs = proc.AppendWordReg(regs, "TW", fpregs.Ftw)
regs = proc.AppendWordReg(regs, "FOP", fpregs.Fop)
regs = proc.AppendQwordReg(regs, "FIP", fpregs.Rip)
regs = proc.AppendQwordReg(regs, "FDP", fpregs.Rdp)
for i := 0; i < len(fpregs.StSpace); i += 4 {
regs = appendX87Reg(regs, i/4, uint16(fpregs.StSpace[i+2]), uint64(fpregs.StSpace[i+1])<<32|uint64(fpregs.StSpace[i]))
regs = proc.AppendX87Reg(regs, i/4, uint16(fpregs.StSpace[i+2]), uint64(fpregs.StSpace[i+1])<<32|uint64(fpregs.StSpace[i]))
}
// SSE registers
regs = appendFlagReg(regs, "MXCSR", uint64(fpregs.Mxcsr), mxcsrDescription, 32)
regs = appendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask)
regs = proc.AppendMxcsrReg(regs, "MXCSR", uint64(fpregs.Mxcsr))
regs = proc.AppendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask)
for i := 0; i < len(fpregs.XmmSpace); i += 16 {
regs = appendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), fpregs.XmmSpace[i:i+16])
regs = proc.AppendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), fpregs.XmmSpace[i:i+16])
if fpregs.AvxState {
regs = appendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), fpregs.YmmSpace[i:i+16])
regs = proc.AppendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), fpregs.YmmSpace[i:i+16])
}
}
......
package proc
package native
import (
"fmt"
"unsafe"
"golang.org/x/arch/x86/x86asm"
"github.com/derekparker/delve/pkg/proc"
)
// Regs represents CPU registers on an AMD64 processor.
......@@ -34,7 +36,7 @@ type Regs struct {
fltSave *_XMM_SAVE_AREA32
}
func (r *Regs) Slice() []Register {
func (r *Regs) Slice() []proc.Register {
var regs = []struct {
k string
v uint64
......@@ -66,31 +68,31 @@ func (r *Regs) Slice() []Register {
if r.fltSave != nil {
outlen += 6 + 8 + 2 + 16
}
out := make([]Register, 0, outlen)
out := make([]proc.Register, 0, outlen)
for _, reg := range regs {
if reg.k == "Eflags" {
out = append(out, Register{reg.k, eflagsDescription.Describe(reg.v, 64)})
out = proc.AppendEflagReg(out, reg.k, reg.v)
} else {
out = appendQwordReg(out, reg.k, reg.v)
out = proc.AppendQwordReg(out, reg.k, reg.v)
}
}
if r.fltSave != nil {
out = appendWordReg(out, "CW", r.fltSave.ControlWord)
out = appendWordReg(out, "SW", r.fltSave.StatusWord)
out = appendWordReg(out, "TW", uint16(r.fltSave.TagWord))
out = appendWordReg(out, "FOP", r.fltSave.ErrorOpcode)
out = appendQwordReg(out, "FIP", uint64(r.fltSave.ErrorSelector)<<32|uint64(r.fltSave.ErrorOffset))
out = appendQwordReg(out, "FDP", uint64(r.fltSave.DataSelector)<<32|uint64(r.fltSave.DataOffset))
out = proc.AppendWordReg(out, "CW", r.fltSave.ControlWord)
out = proc.AppendWordReg(out, "SW", r.fltSave.StatusWord)
out = proc.AppendWordReg(out, "TW", uint16(r.fltSave.TagWord))
out = proc.AppendWordReg(out, "FOP", r.fltSave.ErrorOpcode)
out = proc.AppendQwordReg(out, "FIP", uint64(r.fltSave.ErrorSelector)<<32|uint64(r.fltSave.ErrorOffset))
out = proc.AppendQwordReg(out, "FDP", uint64(r.fltSave.DataSelector)<<32|uint64(r.fltSave.DataOffset))
for i := range r.fltSave.FloatRegisters {
out = appendX87Reg(out, i, uint16(r.fltSave.FloatRegisters[i].High), r.fltSave.FloatRegisters[i].Low)
out = proc.AppendX87Reg(out, i, uint16(r.fltSave.FloatRegisters[i].High), r.fltSave.FloatRegisters[i].Low)
}
out = appendFlagReg(out, "MXCSR", uint64(r.fltSave.MxCsr), mxcsrDescription, 32)
out = appendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask)
out = proc.AppendMxcsrReg(out, "MXCSR", uint64(r.fltSave.MxCsr))
out = proc.AppendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask)
for i := 0; i < len(r.fltSave.XmmRegisters); i += 16 {
out = appendSSEReg(out, fmt.Sprintf("XMM%d", i/16), r.fltSave.XmmRegisters[i:i+16])
out = proc.AppendSSEReg(out, fmt.Sprintf("XMM%d", i/16), r.fltSave.XmmRegisters[i:i+16])
}
}
return out
......@@ -129,7 +131,7 @@ func (r *Regs) GAddr() (uint64, bool) {
}
// SetPC sets the RIP register to the value specified by `pc`.
func (r *Regs) SetPC(t IThread, pc uint64) error {
func (r *Regs) SetPC(t proc.IThread, pc uint64) error {
thread := t.(*Thread)
context := newCONTEXT()
context.ContextFlags = _CONTEXT_ALL
......@@ -298,10 +300,10 @@ func (r *Regs) Get(n int) (uint64, error) {
return r.r15, nil
}
return 0, UnknownRegisterError
return 0, proc.UnknownRegisterError
}
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
context := newCONTEXT()
context.ContextFlags = _CONTEXT_ALL
......@@ -348,7 +350,7 @@ func registers(thread *Thread, floatingPoint bool) (Registers, error) {
return regs, nil
}
func (thread *Thread) saveRegisters() (Registers, error) {
func (thread *Thread) saveRegisters() (proc.Registers, error) {
return nil, fmt.Errorf("not implemented: saveRegisters")
}
......
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
package proc
package native
import (
"syscall"
......
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
}
package proc
package native
// #include "threads_darwin.h"
// #include "proc_darwin.h"
......@@ -81,14 +81,14 @@ func (t *Thread) resume() error {
return nil
}
func threadBlocked(t IThread) bool {
func (t *Thread) Blocked() bool {
// TODO(dp) cache the func pc to remove this lookup
regs, err := t.Registers(false)
if err != nil {
return false
}
pc := regs.PC()
fn := t.BinInfo().goSymTable.PCToFunc(pc)
fn := t.BinInfo().PCToFunc(pc)
if fn == nil {
return false
}
......@@ -104,7 +104,7 @@ func (t *Thread) stopped() bool {
return C.thread_blocked(t.os.threadAct) > C.int(0)
}
func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
if len(data) == 0 {
return 0, nil
}
......
package proc
package native
import (
"fmt"
sys "golang.org/x/sys/unix"
"github.com/derekparker/delve/pkg/proc"
)
type WaitStatus sys.WaitStatus
......@@ -59,7 +61,7 @@ func (t *Thread) singleStep() (err error) {
if status != nil {
rs = status.ExitStatus()
}
return ProcessExitedError{Pid: t.dbp.pid, Status: rs}
return proc.ProcessExitedError{Pid: t.dbp.pid, Status: rs}
}
if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
return nil
......@@ -67,20 +69,20 @@ func (t *Thread) singleStep() (err error) {
}
}
func threadBlocked(t IThread) bool {
func (t *Thread) Blocked() bool {
regs, err := t.Registers(false)
if err != nil {
return false
}
pc := regs.PC()
fn := t.BinInfo().goSymTable.PCToFunc(pc)
fn := t.BinInfo().PCToFunc(pc)
if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
return true
}
return false
}
func (t *Thread) saveRegisters() (Registers, error) {
func (t *Thread) saveRegisters() (proc.Registers, error) {
var err error
t.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(t.ID, &t.os.registers) })
if err != nil {
......@@ -94,7 +96,7 @@ func (t *Thread) restoreRegisters() (err error) {
return
}
func (t *Thread) writeMemory(addr uintptr, data []byte) (written int, err error) {
func (t *Thread) WriteMemory(addr uintptr, data []byte) (written int, err error) {
if len(data) == 0 {
return
}
......
package proc
package native
import (
"errors"
"syscall"
sys "golang.org/x/sys/windows"
"github.com/derekparker/delve/pkg/proc"
)
// WaitStatus is a synonym for the platform-specific WaitStatus
......@@ -57,7 +60,7 @@ func (t *Thread) singleStep() error {
}
if tid == 0 {
t.dbp.postExit()
return ProcessExitedError{Pid: t.dbp.pid, Status: exitCode}
return proc.ProcessExitedError{Pid: t.dbp.pid, Status: exitCode}
}
if t.dbp.os.breakThread == t.ID {
......@@ -103,7 +106,7 @@ func (t *Thread) resume() error {
return err
}
func threadBlocked(t IThread) bool {
func (t *Thread) Blocked() bool {
// TODO: Probably incorrect - what are the runtime functions that
// indicate blocking on Windows?
regs, err := t.Registers(false)
......@@ -111,7 +114,7 @@ func threadBlocked(t IThread) bool {
return false
}
pc := regs.PC()
fn := t.BinInfo().goSymTable.PCToFunc(pc)
fn := t.BinInfo().PCToFunc(pc)
if fn == nil {
return false
}
......@@ -129,7 +132,7 @@ func (t *Thread) stopped() bool {
return true
}
func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
var count uintptr
err := _WriteProcessMemory(t.dbp.os.hProcess, addr, &data[0], uintptr(len(data)), &count)
if err != nil {
......@@ -138,6 +141,8 @@ func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
return int(count), nil
}
var ErrShortRead = errors.New("short read")
func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
if len(buf) == 0 {
return 0, nil
......
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
package proc
package native
import (
"syscall"
......
......@@ -8,48 +8,13 @@ import (
"go/ast"
"go/constant"
"go/token"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"golang.org/x/debug/dwarf"
)
// Process represents all of the information the debugger
// is holding onto regarding the process we are debugging.
type Process struct {
bi BinaryInfo
pid int // Process Pid
Process *os.Process // Pointer to process struct for the actual process we are debugging
// Breakpoint table, holds information on breakpoints.
// Maps instruction address to Breakpoint struct.
breakpoints map[uint64]*Breakpoint
// List of threads mapped as such: pid -> *Thread
threads map[int]*Thread
// Active thread
currentThread *Thread
// Goroutine that will be used by default to set breakpoint, eval variables, etc...
// Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
selectedGoroutine *G
allGCache []*G
os *OSProcessDetails
breakpointIDCounter int
internalBreakpointIDCounter int
firstStart bool
halt bool
exited bool
ptraceChan chan func()
ptraceDoneChan chan interface{}
}
type functionDebugInfo struct {
lowpc, highpc uint64
offset dwarf.Offset
......@@ -57,25 +22,6 @@ type functionDebugInfo struct {
var NotExecutableErr = errors.New("not an executable file")
// New returns an initialized Process struct. Before returning,
// it will also launch a goroutine in order to handle ptrace(2)
// functions. For more information, see the documentation on
// `handlePtraceFuncs`.
func New(pid int) *Process {
dbp := &Process{
pid: pid,
threads: make(map[int]*Thread),
breakpoints: make(map[uint64]*Breakpoint),
firstStart: true,
os: new(OSProcessDetails),
ptraceChan: make(chan func()),
ptraceDoneChan: make(chan interface{}),
bi: NewBinaryInfo(runtime.GOOS, runtime.GOARCH),
}
go dbp.handlePtraceFuncs()
return dbp
}
// ProcessExitedError indicates that the process has exited and contains both
// process id and exit status.
type ProcessExitedError struct {
......@@ -87,115 +33,9 @@ func (pe ProcessExitedError) Error() string {
return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status)
}
func (dbp *Process) BinInfo() *BinaryInfo {
return &dbp.bi
}
// Detach from the process being debugged, optionally killing it.
func (dbp *Process) Detach(kill bool) (err error) {
if dbp.exited {
return nil
}
if dbp.Running() {
if err = dbp.Halt(); err != nil {
return
}
}
if !kill {
// Clean up any breakpoints we've set.
for _, bp := range dbp.breakpoints {
if bp != nil {
_, err := dbp.ClearBreakpoint(bp.Addr)
if err != nil {
return err
}
}
}
}
dbp.execPtraceFunc(func() {
err = dbp.detach(kill)
if err != nil {
return
}
if kill {
err = killProcess(dbp.pid)
}
})
dbp.bi.Close()
return
}
// Exited returns whether the debugged
// process has exited.
func (dbp *Process) Exited() bool {
return dbp.exited
}
// Running returns whether the debugged
// process is currently executing.
func (dbp *Process) Running() bool {
for _, th := range dbp.threads {
if th.running {
return true
}
}
return false
}
func (dbp *Process) Pid() int {
return dbp.pid
}
func (dbp *Process) SelectedGoroutine() *G {
return dbp.selectedGoroutine
}
func (dbp *Process) ThreadList() []IThread {
r := make([]IThread, 0, len(dbp.threads))
for _, v := range dbp.threads {
r = append(r, v)
}
return r
}
func (dbp *Process) FindThread(threadID int) (IThread, bool) {
th, ok := dbp.threads[threadID]
return th, ok
}
func (dbp *Process) CurrentThread() IThread {
return dbp.currentThread
}
func (dbp *Process) Breakpoints() map[uint64]*Breakpoint {
return dbp.breakpoints
}
// LoadInformation finds the executable and then uses it
// to parse the following information:
// * Dwarf .debug_frame section
// * Dwarf .debug_line section
// * Go symbol table.
func (dbp *Process) LoadInformation(path string) error {
var wg sync.WaitGroup
path = findExecutable(path, dbp.pid)
wg.Add(1)
go dbp.loadProcessInformation(&wg)
dbp.bi.LoadBinaryInfo(path, &wg)
wg.Wait()
return nil
}
func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) {
return FindFileLocation(dbp.currentThread, dbp.breakpoints, &dbp.bi, fileName, lineno)
}
// FindFileLocation returns the PC for a given file:line.
// Assumes that `file` is normailzed to lower case and '/' on Windows.
func FindFileLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fileName string, lineno int) (uint64, error) {
func FindFileLocation(mem MemoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fileName string, lineno int) (uint64, error) {
pc, fn, err := bi.goSymTable.LineToPC(fileName, lineno)
if err != nil {
return 0, err
......@@ -206,17 +46,13 @@ func FindFileLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint,
return pc, nil
}
func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
return FindFunctionLocation(dbp.currentThread, dbp.breakpoints, &dbp.bi, funcName, firstLine, lineOffset)
}
// FindFunctionLocation finds address of a function's line
// If firstLine == true is passed FindFunctionLocation will attempt to find the first line of the function
// If lineOffset is passed FindFunctionLocation will return the address of that line
// Pass lineOffset == 0 and firstLine == false if you want the address for the function's entry point
// Note that setting breakpoints at that address will cause surprising behavior:
// https://github.com/derekparker/delve/issues/170
func FindFunctionLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, funcName string, firstLine bool, lineOffset int) (uint64, error) {
func FindFunctionLocation(mem MemoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, funcName string, firstLine bool, lineOffset int) (uint64, error) {
origfn := bi.goSymTable.LookupFunc(funcName)
if origfn == nil {
return 0, fmt.Errorf("Could not find function %s\n", funcName)
......@@ -233,93 +69,6 @@ func FindFunctionLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoi
return origfn.Entry, nil
}
// CurrentLocation returns the location of the current thread.
func (dbp *Process) CurrentLocation() (*Location, error) {
return dbp.currentThread.Location()
}
// RequestManualStop sets the `halt` flag and
// sends SIGSTOP to all threads.
func (dbp *Process) RequestManualStop() error {
if dbp.exited {
return &ProcessExitedError{}
}
dbp.halt = true
return dbp.requestManualStop()
}
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
// break point table. Setting a break point must be thread specific due to
// ptrace actions needing the thread to be in a signal-delivery-stop.
func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
tid := dbp.currentThread.ID
if bp, ok := dbp.FindBreakpoint(addr); ok {
return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
}
f, l, fn := dbp.bi.goSymTable.PCToLine(uint64(addr))
if fn == nil {
return nil, InvalidAddressError{address: addr}
}
newBreakpoint := &Breakpoint{
FunctionName: fn.Name,
File: f,
Line: l,
Addr: addr,
Kind: kind,
Cond: cond,
HitCount: map[int]uint64{},
}
if kind != UserBreakpoint {
dbp.internalBreakpointIDCounter++
newBreakpoint.ID = dbp.internalBreakpointIDCounter
} else {
dbp.breakpointIDCounter++
newBreakpoint.ID = dbp.breakpointIDCounter
}
thread := dbp.threads[tid]
originalData := make([]byte, dbp.bi.arch.BreakpointSize())
_, err := thread.ReadMemory(originalData, uintptr(addr))
if err != nil {
return nil, err
}
if err := dbp.writeSoftwareBreakpoint(thread, addr); err != nil {
return nil, err
}
newBreakpoint.OriginalData = originalData
dbp.breakpoints[addr] = newBreakpoint
return newBreakpoint, nil
}
// ClearBreakpoint clears the breakpoint at addr.
func (dbp *Process) ClearBreakpoint(addr uint64) (*Breakpoint, error) {
if dbp.exited {
return nil, &ProcessExitedError{}
}
bp, ok := dbp.FindBreakpoint(addr)
if !ok {
return nil, NoBreakpointError{addr: addr}
}
if _, err := dbp.currentThread.ClearBreakpoint(bp); err != nil {
return nil, err
}
delete(dbp.breakpoints, addr)
return bp, nil
}
// Status returns the status of the current main thread context.
func (dbp *Process) Status() *WaitStatus {
return dbp.currentThread.Status
}
// Next continues execution until the next source line.
func Next(dbp Continuable) (err error) {
if dbp.Exited() {
......@@ -343,33 +92,6 @@ func Next(dbp Continuable) (err error) {
return Continue(dbp)
}
func (dbp *Process) ContinueOnce() (IThread, error) {
if dbp.exited {
return nil, &ProcessExitedError{}
}
if err := dbp.resume(); err != nil {
return nil, err
}
dbp.allGCache = nil
for _, th := range dbp.threads {
th.clearBreakpointState()
}
trapthread, err := dbp.trapWait(-1)
if err != nil {
return nil, err
}
if err := dbp.Halt(); err != nil {
return nil, dbp.exitGuard(err)
}
if err := dbp.setCurrentBreakpoints(trapthread); err != nil {
return nil, err
}
return trapthread, err
}
// Continuable is the subinterface of target.Interface used to implement
// Continue/Next/etc.
type Continuable interface {
......@@ -442,7 +164,7 @@ func Continue(dbp Continuable) error {
// here we either set a breakpoint into the destination of the CALL
// instruction or we determined that the called function is hidden,
// either way we need to resume execution
if err = setStepIntoBreakpoint(dbp, text, sameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
if err = setStepIntoBreakpoint(dbp, text, SameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
return err
}
} else {
......@@ -528,8 +250,9 @@ func Step(dbp Continuable) (err error) {
return Continue(dbp)
}
// Returns an expression that evaluates to true when the current goroutine is g
func sameGoroutineCondition(g *G) ast.Expr {
// SameGoroutineCondition returns an expression that evaluates to true when
// the current goroutine is g.
func SameGoroutineCondition(g *G) ast.Expr {
if g == nil {
return nil
}
......@@ -546,39 +269,12 @@ func sameGoroutineCondition(g *G) ast.Expr {
}
}
// StepInstruction will continue the current thread for exactly
// one instruction. This method affects only the thread
// asssociated with the selected goroutine. All other
// threads will remain stopped.
func (dbp *Process) StepInstruction() (err error) {
if dbp.selectedGoroutine == nil {
return errors.New("cannot single step: no selected goroutine")
}
if dbp.selectedGoroutine.thread == nil {
// Step called on parked goroutine
if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, NextBreakpoint, sameGoroutineCondition(dbp.selectedGoroutine)); err != nil {
return err
}
return Continue(dbp)
}
dbp.allGCache = nil
if dbp.exited {
return &ProcessExitedError{}
}
dbp.selectedGoroutine.thread.(*Thread).clearBreakpointState()
err = dbp.selectedGoroutine.thread.(*Thread).StepInstruction()
if err != nil {
return err
}
return dbp.selectedGoroutine.thread.(*Thread).SetCurrentBreakpoint()
}
// StepOut will continue until the current goroutine exits the
// function currently being executed or a deferred function is executed
func StepOut(dbp Continuable) error {
selg := dbp.SelectedGoroutine()
curthread := dbp.CurrentThread()
cond := sameGoroutineCondition(selg)
cond := SameGoroutineCondition(selg)
topframe, err := topframe(selg, curthread)
if err != nil {
......@@ -631,50 +327,12 @@ func StepOut(dbp Continuable) error {
return Continue(dbp)
}
// SwitchThread changes from current thread to the thread specified by `tid`.
func (dbp *Process) SwitchThread(tid int) error {
if dbp.exited {
return &ProcessExitedError{}
}
if th, ok := dbp.threads[tid]; ok {
dbp.currentThread = th
dbp.selectedGoroutine, _ = GetG(dbp.currentThread)
return nil
}
return fmt.Errorf("thread %d does not exist", tid)
}
// SwitchGoroutine changes from current thread to the thread
// running the specified goroutine.
func (dbp *Process) SwitchGoroutine(gid int) error {
if dbp.exited {
return &ProcessExitedError{}
}
g, err := FindGoroutine(dbp, gid)
if err != nil {
return err
}
if g == nil {
// user specified -1 and selectedGoroutine is nil
return nil
}
if g.thread != nil {
return dbp.SwitchThread(g.thread.ThreadID())
}
dbp.selectedGoroutine = g
return nil
}
// If the argument of GoroutinesInfo implements AllGCache GoroutinesInfo
// will use the pointer returned by AllGCache as a cache.
type AllGCache interface {
AllGCache() *[]*G
}
func (dbp *Process) AllGCache() *[]*G {
return &dbp.allGCache
}
// GoroutinesInfo returns an array of G structures representing the information
// Delve cares about from the internal runtime G structure.
func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
......@@ -695,7 +353,7 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
threads := dbp.ThreadList()
for _, th := range threads {
if threadBlocked(th) {
if th.Blocked() {
continue
}
g, _ := GetG(th)
......@@ -724,12 +382,12 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
return nil, err
}
}
faddr := make([]byte, dbp.BinInfo().arch.PtrSize())
faddr := make([]byte, dbp.BinInfo().Arch.PtrSize())
_, err = dbp.CurrentThread().ReadMemory(faddr, uintptr(allgentryaddr))
allgptr := binary.LittleEndian.Uint64(faddr)
for i := uint64(0); i < allglen; i++ {
gvar, err := newGVariable(dbp.CurrentThread(), uintptr(allgptr+(i*uint64(dbp.BinInfo().arch.PtrSize()))), true)
gvar, err := newGVariable(dbp.CurrentThread(), uintptr(allgptr+(i*uint64(dbp.BinInfo().Arch.PtrSize()))), true)
if err != nil {
return nil, err
}
......@@ -742,7 +400,7 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
if err != nil {
return nil, err
}
g.thread = thread
g.Thread = thread
// Prefer actual thread location information.
g.CurrentLoc = *loc
}
......@@ -758,153 +416,8 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
return allg, nil
}
func (g *G) Thread() IThread {
return g.thread
}
// Halt stops all threads.
func (dbp *Process) Halt() (err error) {
if dbp.exited {
return &ProcessExitedError{}
}
for _, th := range dbp.threads {
if err := th.Halt(); err != nil {
return err
}
}
return nil
}
// Registers obtains register values from the
// "current" thread of the traced process.
func (dbp *Process) Registers() (Registers, error) {
return dbp.currentThread.Registers(false)
}
// PC returns the PC of the current thread.
func (dbp *Process) PC() (uint64, error) {
return dbp.currentThread.PC()
}
// CurrentBreakpoint returns the breakpoint the current thread
// is stopped at.
func (dbp *Process) CurrentBreakpoint() *Breakpoint {
return dbp.currentThread.CurrentBreakpoint
}
// FindBreakpointByID finds the breakpoint for the given ID.
func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
for _, bp := range dbp.breakpoints {
if bp.ID == id {
return bp, true
}
}
return nil, false
}
// FindBreakpoint finds the breakpoint for the given pc.
func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
if bp, ok := dbp.breakpoints[pc-uint64(dbp.bi.arch.BreakpointSize())]; ok {
return bp, true
}
// Directly use addr to lookup breakpoint.
if bp, ok := dbp.breakpoints[pc]; ok {
return bp, true
}
return nil, false
}
// Returns a new Process struct.
func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, error) {
if attach {
var err error
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
if err != nil {
return nil, err
}
_, _, err = dbp.wait(dbp.pid, 0)
if err != nil {
return nil, err
}
}
proc, err := os.FindProcess(dbp.pid)
if err != nil {
return nil, err
}
dbp.Process = proc
err = dbp.LoadInformation(path)
if err != nil {
return nil, err
}
if err := dbp.updateThreadList(); err != nil {
return nil, err
}
scope := &EvalScope{0, 0, dbp.currentThread, nil, dbp.BinInfo()}
ver, isextld, err := scope.getGoInformation()
if err != nil {
return nil, err
}
dbp.bi.arch.SetGStructOffset(ver, isextld)
// selectedGoroutine can not be set correctly by the call to updateThreadList
// because without calling SetGStructOffset we can not read the G struct of currentThread
// but without calling updateThreadList we can not examine memory to determine
// the offset of g struct inside TLS
dbp.selectedGoroutine, _ = GetG(dbp.currentThread)
panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0)
if err == nil {
bp, err := dbp.SetBreakpoint(panicpc, UserBreakpoint, nil)
if err == nil {
bp.Name = "unrecovered-panic"
bp.ID = -1
dbp.breakpointIDCounter--
}
}
return dbp, nil
}
func (dbp *Process) ClearInternalBreakpoints() error {
for _, bp := range dbp.breakpoints {
if !bp.Internal() {
continue
}
if _, err := dbp.ClearBreakpoint(bp.Addr); err != nil {
return err
}
}
for i := range dbp.threads {
if dbp.threads[i].CurrentBreakpoint != nil && dbp.threads[i].CurrentBreakpoint.Internal() {
dbp.threads[i].CurrentBreakpoint = nil
}
}
return nil
}
func (dbp *Process) handlePtraceFuncs() {
// We must ensure here that we are running on the same thread during
// while invoking the ptrace(2) syscall. This is due to the fact that ptrace(2) expects
// all commands after PTRACE_ATTACH to come from the same thread.
runtime.LockOSThread()
for fn := range dbp.ptraceChan {
fn()
dbp.ptraceDoneChan <- nil
}
}
func (dbp *Process) execPtraceFunc(fn func()) {
dbp.ptraceChan <- fn
<-dbp.ptraceDoneChan
}
func (scope *EvalScope) getGoInformation() (ver GoVersion, isextld bool, err error) {
func GetGoInformation(p Continuable) (ver GoVersion, isextld bool, err error) {
scope := &EvalScope{0, 0, p.CurrentThread(), nil, p.BinInfo()}
vv, err := scope.packageVarAddr("runtime.buildVersion")
if err != nil {
return ver, false, fmt.Errorf("Could not determine version number: %v", err)
......@@ -921,7 +434,7 @@ func (scope *EvalScope) getGoInformation() (ver GoVersion, isextld bool, err err
return
}
rdr := scope.bi.DwarfReader()
rdr := scope.BinInfo.DwarfReader()
rdr.Seek(0)
for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() {
if err != nil {
......@@ -976,11 +489,11 @@ func ConvertEvalScope(dbp EvalScopeConvertible, gid, frame int) (*EvalScope, err
return ThreadScope(ct)
}
var thread memoryReadWriter
if g.thread == nil {
var thread MemoryReadWriter
if g.Thread == nil {
thread = ct
} else {
thread = g.thread
thread = g.Thread
}
locs, err := g.Stacktrace(frame)
......@@ -1001,9 +514,3 @@ func ConvertEvalScope(dbp EvalScopeConvertible, gid, frame int) (*EvalScope, err
func FrameToScope(p EvalScopeConvertible, frame Stackframe) *EvalScope {
return &EvalScope{frame.Current.PC, frame.CFA, p.CurrentThread(), nil, p.BinInfo()}
}
func (dbp *Process) postExit() {
dbp.exited = true
close(dbp.ptraceChan)
close(dbp.ptraceDoneChan)
}
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")
}
}
此差异已折叠。
// +build linux darwin
package proc
package proc_test
import (
"runtime"
"syscall"
"testing"
"time"
"runtime"
"github.com/derekparker/delve/pkg/proc"
protest "github.com/derekparker/delve/pkg/proc/test"
"github.com/derekparker/delve/pkg/target"
)
func TestIssue419(t *testing.T) {
......@@ -17,10 +19,10 @@ func TestIssue419(t *testing.T) {
return
}
// SIGINT directed at the inferior should be passed along not swallowed by delve
withTestProcess("issue419", t, func(p IProcess, fixture protest.Fixture) {
withTestProcess("issue419", t, func(p target.Interface, fixture protest.Fixture) {
_, err := setFunctionBreakpoint(p, "main.main")
assertNoError(err, t, "SetBreakpoint()")
assertNoError(Continue(p), t, "Continue()")
assertNoError(proc.Continue(p), t, "Continue()")
go func() {
for {
time.Sleep(500 * time.Millisecond)
......@@ -39,8 +41,8 @@ func TestIssue419(t *testing.T) {
}
}
}()
err = Continue(p)
if _, exited := err.(ProcessExitedError); !exited {
err = proc.Continue(p)
if _, exited := err.(proc.ProcessExitedError); !exited {
t.Fatalf("Unexpected error after Continue(): %v\n", err)
}
})
......
......@@ -32,15 +32,18 @@ type Register struct {
Value string
}
func appendWordReg(regs []Register, name string, value uint16) []Register {
// AppendWordReg appends a word (16 bit) register to regs.
func AppendWordReg(regs []Register, name string, value uint16) []Register {
return append(regs, Register{name, fmt.Sprintf("%#04x", value)})
}
func appendDwordReg(regs []Register, name string, value uint32) []Register {
// AppendDwordReg appends a double word (32 bit) register to regs.
func AppendDwordReg(regs []Register, name string, value uint32) []Register {
return append(regs, Register{name, fmt.Sprintf("%#08x", value)})
}
func appendQwordReg(regs []Register, name string, value uint64) []Register {
// AppendQwordReg appends a quad word (64 bit) register to regs.
func AppendQwordReg(regs []Register, name string, value uint64) []Register {
return append(regs, Register{name, fmt.Sprintf("%#016x", value)})
}
......@@ -48,7 +51,18 @@ func appendFlagReg(regs []Register, name string, value uint64, descr flagRegiste
return append(regs, Register{name, descr.Describe(value, size)})
}
func appendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64) []Register {
// AppendEflagReg appends EFLAG register to regs.
func AppendEflagReg(regs []Register, name string, value uint64) []Register {
return appendFlagReg(regs, name, value, eflagsDescription, 64)
}
// AppendMxcsrReg appends MXCSR register to regs.
func AppendMxcsrReg(regs []Register, name string, value uint64) []Register {
return appendFlagReg(regs, name, value, mxcsrDescription, 32)
}
// AppendX87Reg appends a 80 bit float register to regs.
func AppendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64) []Register {
var f float64
fset := false
......@@ -103,7 +117,8 @@ func appendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64)
return append(regs, Register{fmt.Sprintf("ST(%d)", index), fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)})
}
func appendSSEReg(regs []Register, name string, xmm []byte) []Register {
// AppendSSEReg appends a 256 bit SSE register to regs.
func AppendSSEReg(regs []Register, name string, xmm []byte) []Register {
buf := bytes.NewReader(xmm)
var out bytes.Buffer
......@@ -141,20 +156,6 @@ func appendSSEReg(regs []Register, name string, xmm []byte) []Register {
var UnknownRegisterError = errors.New("unknown register")
// Registers obtains register values from the debugged process.
func (t *Thread) Registers(floatingPoint bool) (Registers, error) {
return registers(t, floatingPoint)
}
// PC returns the current PC for this thread.
func (t *Thread) PC() (uint64, error) {
regs, err := t.Registers(false)
if err != nil {
return 0, err
}
return regs.PC(), nil
}
type flagRegisterDescr []flagDescr
type flagDescr struct {
name string
......
......@@ -15,11 +15,11 @@ const runtimeStackBarrier = "runtime.stackBarrier"
// NoReturnAddr is returned when return address
// could not be found during stack trace.
type NoReturnAddr struct {
fn string
Fn string
}
func (nra NoReturnAddr) Error() string {
return fmt.Sprintf("could not find return address for %s", nra.fn)
return fmt.Sprintf("could not find return address for %s", nra.Fn)
}
// Stackframe represents a frame in a system stack.
......@@ -54,12 +54,12 @@ func (g *G) stackIterator() (*stackIterator, error) {
if err != nil {
return nil, err
}
if g.thread != nil {
regs, err := g.thread.Registers(false)
if g.Thread != nil {
regs, err := g.Thread.Registers(false)
if err != nil {
return nil, err
}
return newStackIterator(g.variable.bi, g.thread, regs.PC(), regs.SP(), regs.BP(), stkbar, g.stkbarPos), nil
return newStackIterator(g.variable.bi, g.Thread, regs.PC(), regs.SP(), regs.BP(), stkbar, g.stkbarPos), nil
}
return newStackIterator(g.variable.bi, g.variable.mem, g.PC, g.SP, 0, stkbar, g.stkbarPos), nil
}
......@@ -90,7 +90,7 @@ type stackIterator struct {
atend bool
frame Stackframe
bi *BinaryInfo
mem memoryReadWriter
mem MemoryReadWriter
err error
stackBarrierPC uint64
......@@ -102,7 +102,7 @@ type savedLR struct {
val uint64
}
func newStackIterator(bi *BinaryInfo, mem memoryReadWriter, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
stackBarrierFunc := bi.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9
var stackBarrierPC uint64
if stackBarrierFunc != nil && stkbar != nil {
......@@ -161,7 +161,7 @@ func (it *stackIterator) Next() bool {
it.top = false
it.pc = it.frame.Ret
it.sp = uint64(it.frame.CFA)
it.bp, _ = readUintRaw(it.mem, uintptr(it.bp), int64(it.bi.arch.PtrSize()))
it.bp, _ = readUintRaw(it.mem, uintptr(it.bp), int64(it.bi.Arch.PtrSize()))
return true
}
......@@ -185,8 +185,8 @@ func (it *stackIterator) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, err
return Stackframe{}, err
}
// When no FDE is available attempt to use BP instead
retaddr := uintptr(int(bp) + it.bi.arch.PtrSize())
cfa := int64(retaddr) + int64(it.bi.arch.PtrSize())
retaddr := uintptr(int(bp) + it.bi.Arch.PtrSize())
cfa := int64(retaddr) + int64(it.bi.Arch.PtrSize())
return it.newStackframe(pc, cfa, retaddr, nil, top)
}
......@@ -202,7 +202,7 @@ func (it *stackIterator) newStackframe(pc uint64, cfa int64, retaddr uintptr, fd
return Stackframe{}, NullAddrError{}
}
f, l, fn := it.bi.PCToLine(pc)
ret, err := readUintRaw(it.mem, retaddr, int64(it.bi.arch.PtrSize()))
ret, err := readUintRaw(it.mem, retaddr, int64(it.bi.Arch.PtrSize()))
if err != nil {
return Stackframe{}, err
}
......
......@@ -13,27 +13,9 @@ import (
"golang.org/x/debug/dwarf"
)
// Thread represents a single thread in the traced process
// ID represents the thread id or port, Process holds a reference to the
// Process struct that contains info on the process as
// a whole, and Status represents the last result of a `wait` call
// on this thread.
type Thread struct {
ID int // Thread ID or mach port
Status *WaitStatus // Status returned from last wait call
CurrentBreakpoint *Breakpoint // Breakpoint thread is currently stopped at
BreakpointConditionMet bool // Output of evaluating the breakpoint's condition
BreakpointConditionError error // Error evaluating the breakpoint's condition
dbp *Process
singleStepping bool
running bool
os *OSSpecificDetails
}
// IThread represents a thread.
type IThread interface {
memoryReadWriter
MemoryReadWriter
Location() (*Location, error)
// Breakpoint will return the breakpoint that this thread is stopped at or
// nil if the thread is not stopped at any breakpoint.
......@@ -47,6 +29,8 @@ type IThread interface {
Arch() Arch
BinInfo() *BinaryInfo
StepInstruction() error
// Blocked returns true if the thread is blocked
Blocked() bool
}
// Location represents the location of a thread.
......@@ -59,88 +43,6 @@ type Location struct {
Fn *gosym.Func
}
// Continue the execution of this thread.
//
// If we are currently at a breakpoint, we'll clear it
// first and then resume execution. Thread will continue until
// it hits a breakpoint or is signaled.
func (thread *Thread) Continue() error {
pc, err := thread.PC()
if err != nil {
return err
}
// Check whether we are stopped at a breakpoint, and
// if so, single step over it before continuing.
if _, ok := thread.dbp.FindBreakpoint(pc); ok {
if err := thread.StepInstruction(); err != nil {
return err
}
}
return thread.resume()
}
// StepInstruction steps a single instruction.
//
// Executes exactly one instruction and then returns.
// If the thread is at a breakpoint, we first clear it,
// execute the instruction, and then replace the breakpoint.
// Otherwise we simply execute the next instruction.
func (thread *Thread) StepInstruction() (err error) {
thread.running = true
thread.singleStepping = true
defer func() {
thread.singleStepping = false
thread.running = false
}()
pc, err := thread.PC()
if err != nil {
return err
}
bp, ok := thread.dbp.FindBreakpoint(pc)
if ok {
// Clear the breakpoint so that we can continue execution.
_, err = thread.ClearBreakpoint(bp)
if err != nil {
return err
}
// Restore breakpoint now that we have passed it.
defer func() {
err = thread.dbp.writeSoftwareBreakpoint(thread, bp.Addr)
}()
}
err = thread.singleStep()
if err != nil {
if _, exited := err.(ProcessExitedError); exited {
return err
}
return fmt.Errorf("step failed: %s", err.Error())
}
return nil
}
// Location returns the threads location, including the file:line
// of the corresponding source code, the function we're in
// and the current instruction address.
func (thread *Thread) Location() (*Location, error) {
pc, err := thread.PC()
if err != nil {
return nil, err
}
f, l, fn := thread.dbp.bi.PCToLine(pc)
return &Location{PC: pc, File: f, Line: l, Fn: fn}, nil
}
func (thread *Thread) Arch() Arch {
return thread.dbp.bi.arch
}
func (thread *Thread) BinInfo() *BinaryInfo {
return &thread.dbp.bi
}
// ThreadBlockedError is returned when the thread
// is blocked in the scheduler.
type ThreadBlockedError struct{}
......@@ -155,7 +57,7 @@ func topframe(g *G, thread IThread) (Stackframe, error) {
var err error
if g == nil {
if threadBlocked(thread) {
if thread.Blocked() {
return Stackframe{}, ThreadBlockedError{}
}
frames, err = ThreadStacktrace(thread, 0)
......@@ -194,11 +96,11 @@ func next(dbp Continuable, stepInto bool) error {
}()
csource := filepath.Ext(topframe.Current.File) != ".go"
var thread memoryReadWriter = curthread
var thread MemoryReadWriter = curthread
var regs Registers
if selg != nil && selg.Thread() != nil {
thread = selg.Thread()
regs, err = selg.Thread().Registers(false)
if selg != nil && selg.Thread != nil {
thread = selg.Thread
regs, err = selg.Thread.Registers(false)
if err != nil {
return err
}
......@@ -215,7 +117,7 @@ func next(dbp Continuable, stepInto bool) error {
}
}
cond := sameGoroutineCondition(selg)
cond := SameGoroutineCondition(selg)
if stepInto {
for _, instr := range text {
......@@ -361,15 +263,6 @@ func setInternalBreakpoints(dbp Continuable, curpc uint64, pcs []uint64, kind Br
return nil
}
// SetPC sets the PC for this thread.
func (thread *Thread) SetPC(pc uint64) error {
regs, err := thread.Registers(false)
if err != nil {
return err
}
return regs.SetPC(thread, pc)
}
func getGVariable(thread IThread) (*Variable, error) {
arch := thread.Arch()
regs, err := thread.Registers(false)
......@@ -435,7 +328,7 @@ func GetG(thread IThread) (g *G, err error) {
g, err = gaddr.parseG()
if err == nil {
g.thread = thread
g.Thread = thread
if loc, err := thread.Location(); err == nil {
g.CurrentLoc = *loc
}
......@@ -443,29 +336,6 @@ func GetG(thread IThread) (g *G, err error) {
return
}
// Stopped returns whether the thread is stopped at
// the operating system level. Actual implementation
// is OS dependant, look in OS thread file.
func (thread *Thread) Stopped() bool {
return thread.stopped()
}
// Halt stops this thread from executing. Actual
// implementation is OS dependant. Look in OS
// thread file.
func (thread *Thread) Halt() (err error) {
defer func() {
if err == nil {
thread.running = false
}
}()
if thread.Stopped() {
return
}
err = thread.halt()
return
}
// ThreadScope returns an EvalScope for this thread.
func ThreadScope(thread IThread) (*EvalScope, error) {
locations, err := ThreadStacktrace(thread, 0)
......@@ -494,36 +364,6 @@ func GoroutineScope(thread IThread) (*EvalScope, error) {
return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, gvar, thread.BinInfo()}, nil
}
// SetCurrentBreakpoint sets the current breakpoint that this
// thread is stopped at as CurrentBreakpoint on the thread struct.
func (thread *Thread) SetCurrentBreakpoint() error {
thread.CurrentBreakpoint = nil
pc, err := thread.PC()
if err != nil {
return err
}
if bp, ok := thread.dbp.FindBreakpoint(pc); ok {
thread.CurrentBreakpoint = bp
if err = thread.SetPC(bp.Addr); err != nil {
return err
}
thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.checkCondition(thread)
if thread.CurrentBreakpoint != nil && thread.BreakpointConditionMet {
if g, err := GetG(thread); err == nil {
thread.CurrentBreakpoint.HitCount[g.ID]++
}
thread.CurrentBreakpoint.TotalHitCount++
}
}
return nil
}
func (thread *Thread) clearBreakpointState() {
thread.CurrentBreakpoint = nil
thread.BreakpointConditionMet = false
thread.BreakpointConditionError = nil
}
func onRuntimeBreakpoint(thread IThread) bool {
loc, err := thread.Location()
if err != nil {
......@@ -551,13 +391,5 @@ func onNextGoroutine(thread IThread, breakpoints map[uint64]*Breakpoint) (bool,
bp.Kind = NextDeferBreakpoint
}()
}
return bp.checkCondition(thread)
}
func (th *Thread) Breakpoint() (*Breakpoint, bool, error) {
return th.CurrentBreakpoint, (th.CurrentBreakpoint != nil && th.BreakpointConditionMet), th.BreakpointConditionError
}
func (th *Thread) ThreadID() int {
return th.ID
return bp.CheckCondition(thread)
}
......@@ -69,7 +69,7 @@ func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
if err != nil {
return nil, err
}
return pointerTo(ptyp, bi.arch), nil
return pointerTo(ptyp, bi.Arch), nil
}
return bi.findType(exprToString(expr))
}
......@@ -380,7 +380,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
if err != nil {
return "", err
}
prtyp := pointerTo(rtyp, _type.bi.arch)
prtyp := pointerTo(rtyp, _type.bi.Arch)
uadd := _type.RealType.Common().ByteSize
if ut := uncommon(_type, tflag); ut != nil {
......@@ -407,7 +407,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
for i := int64(0); i < inCount; i++ {
argtype := cursortyp.maybeDereference()
cursortyp.Addr += uintptr(_type.bi.arch.PtrSize())
cursortyp.Addr += uintptr(_type.bi.Arch.PtrSize())
argtypename, _, err := nameOfRuntimeType(argtype)
if err != nil {
return "", err
......@@ -434,7 +434,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
buf.WriteString(" (")
for i := int64(0); i < outCount; i++ {
argtype := cursortyp.maybeDereference()
cursortyp.Addr += uintptr(_type.bi.arch.PtrSize())
cursortyp.Addr += uintptr(_type.bi.Arch.PtrSize())
argtypename, _, err := nameOfRuntimeType(argtype)
if err != nil {
return "", err
......@@ -582,7 +582,7 @@ func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
if err != nil {
return nil, err
}
prtyp := pointerTo(rtyp, _type.bi.arch)
prtyp := pointerTo(rtyp, _type.bi.Arch)
uintptrtyp, err := _type.bi.findType("uintptr")
if err != nil {
......@@ -602,7 +602,7 @@ func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType {
r := newStructType("[]"+elemtype.Common().Name, uintptr(3*uintptrtyp.Size()))
appendField(r, "array", pointerTo(elemtype, _type.bi.arch), 0)
appendField(r, "array", pointerTo(elemtype, _type.bi.Arch), 0)
appendField(r, "len", uintptrtyp, uintptr(uintptrtyp.Size()))
appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size()))
return &dwarf.SliceType{StructType: *r, ElemType: elemtype}
......
......@@ -50,7 +50,7 @@ type Variable struct {
DwarfType dwarf.Type
RealType dwarf.Type
Kind reflect.Kind
mem memoryReadWriter
mem MemoryReadWriter
bi *BinaryInfo
Value constant.Value
......@@ -129,7 +129,7 @@ type G struct {
CurrentLoc Location
// Thread that this goroutine is currently allocated to
thread IThread
Thread IThread
variable *Variable
}
......@@ -137,11 +137,11 @@ type G struct {
// EvalScope is the scope for variable evaluation. Contains the thread,
// current location (PC), and canonical frame address.
type EvalScope struct {
PC uint64 // Current instruction of the evaluation frame
CFA int64 // Stack address of the evaluation frame
Mem memoryReadWriter // Target's memory
gvar *Variable
bi *BinaryInfo
PC uint64 // Current instruction of the evaluation frame
CFA int64 // Stack address of the evaluation frame
Mem MemoryReadWriter // Target's memory
Gvar *Variable
BinInfo *BinaryInfo
}
// IsNilErr is returned when a variable is nil.
......@@ -154,7 +154,7 @@ func (err *IsNilErr) Error() string {
}
func (scope *EvalScope) newVariable(name string, addr uintptr, dwarfType dwarf.Type) *Variable {
return newVariable(name, addr, dwarfType, scope.bi, scope.Mem)
return newVariable(name, addr, dwarfType, scope.BinInfo, scope.Mem)
}
func newVariableFromThread(t IThread, name string, addr uintptr, dwarfType dwarf.Type) *Variable {
......@@ -165,7 +165,7 @@ func (v *Variable) newVariable(name string, addr uintptr, dwarfType dwarf.Type)
return newVariable(name, addr, dwarfType, v.bi, v.mem)
}
func newVariable(name string, addr uintptr, dwarfType dwarf.Type, bi *BinaryInfo, mem memoryReadWriter) *Variable {
func newVariable(name string, addr uintptr, dwarfType dwarf.Type, bi *BinaryInfo, mem MemoryReadWriter) *Variable {
v := &Variable{
Name: name,
Addr: addr,
......@@ -191,7 +191,7 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, bi *BinaryInfo
v.stride = 1
v.fieldType = &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}}
if v.Addr != 0 {
v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.bi.arch, v.Addr)
v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.bi.Arch, v.Addr)
}
case *dwarf.SliceType:
v.Kind = reflect.Slice
......@@ -256,7 +256,7 @@ func resolveTypedef(typ dwarf.Type) dwarf.Type {
}
}
func newConstant(val constant.Value, mem memoryReadWriter) *Variable {
func newConstant(val constant.Value, mem MemoryReadWriter) *Variable {
v := &Variable{Value: val, mem: mem, loaded: true}
switch val.Kind() {
case constant.Int:
......@@ -322,33 +322,23 @@ func (v *Variable) toField(field *dwarf.StructField) (*Variable, error) {
// DwarfReader returns the DwarfReader containing the
// Dwarf information for the target process.
func (scope *EvalScope) DwarfReader() *reader.Reader {
return scope.bi.DwarfReader()
return scope.BinInfo.DwarfReader()
}
// Type returns the Dwarf type entry at `offset`.
func (scope *EvalScope) Type(offset dwarf.Offset) (dwarf.Type, error) {
return scope.bi.dwarf.Type(offset)
return scope.BinInfo.dwarf.Type(offset)
}
// PtrSize returns the size of a pointer.
func (scope *EvalScope) PtrSize() int {
return scope.bi.arch.PtrSize()
return scope.BinInfo.Arch.PtrSize()
}
// ChanRecvBlocked returns whether the goroutine is blocked on
// a channel read operation.
func (g *G) ChanRecvBlocked() bool {
return (g.thread == nil) && (g.WaitReason == chanRecv)
}
// chanRecvReturnAddr returns the address of the return from a channel read.
func (g *G) chanRecvReturnAddr(dbp *Process) (uint64, error) {
locs, err := g.Stacktrace(4)
if err != nil {
return 0, err
}
topLoc := locs[len(locs)-1]
return topLoc.Current.PC, nil
return (g.Thread == nil) && (g.WaitReason == chanRecv)
}
// NoGError returned when a G could not be found
......@@ -367,7 +357,7 @@ func (gvar *Variable) parseG() (*G, error) {
_, deref := gvar.RealType.(*dwarf.PtrType)
if deref {
gaddrbytes := make([]byte, gvar.bi.arch.PtrSize())
gaddrbytes := make([]byte, gvar.bi.Arch.PtrSize())
_, err := mem.ReadMemory(gaddrbytes, uintptr(gaddr))
if err != nil {
return nil, fmt.Errorf("error derefing *G %s", err)
......@@ -376,8 +366,8 @@ func (gvar *Variable) parseG() (*G, error) {
}
if gaddr == 0 {
id := 0
if thread, ok := mem.(*Thread); ok {
id = thread.ID
if thread, ok := mem.(IThread); ok {
id = thread.ThreadID()
}
return nil, NoGError{tid: id}
}
......@@ -586,7 +576,7 @@ func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry, cfg LoadCon
func (scope *EvalScope) extractVarInfo(varName string) (*Variable, error) {
reader := scope.DwarfReader()
off, err := scope.bi.findFunctionDebugInfo(scope.PC)
off, err := scope.BinInfo.findFunctionDebugInfo(scope.PC)
if err != nil {
return nil, err
}
......@@ -655,19 +645,6 @@ func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
return vars, nil
}
// EvalPackageVariable will evaluate the package level variable
// specified by 'name'.
func (dbp *Process) EvalPackageVariable(name string, cfg LoadConfig) (*Variable, error) {
scope := &EvalScope{0, 0, dbp.currentThread, nil, dbp.BinInfo()}
v, err := scope.packageVarAddr(name)
if err != nil {
return nil, err
}
v.loadValue(cfg)
return v, nil
}
func (scope *EvalScope) packageVarAddr(name string) (*Variable, error) {
reader := scope.DwarfReader()
for entry, err := reader.NextPackageVariable(); entry != nil; entry, err = reader.NextPackageVariable() {
......@@ -941,7 +918,7 @@ func (v *Variable) setValue(y *Variable) error {
return err
}
func readStringInfo(mem memoryReadWriter, arch Arch, addr uintptr) (uintptr, int64, error) {
func readStringInfo(mem MemoryReadWriter, arch Arch, addr uintptr) (uintptr, int64, error) {
// string data structure is always two ptrs in size. Addr, followed by len
// http://research.swtch.com/godata
......@@ -971,7 +948,7 @@ func readStringInfo(mem memoryReadWriter, arch Arch, addr uintptr) (uintptr, int
return addr, strlen, nil
}
func readStringValue(mem memoryReadWriter, addr uintptr, strlen int64, cfg LoadConfig) (string, error) {
func readStringValue(mem MemoryReadWriter, addr uintptr, strlen int64, cfg LoadConfig) (string, error) {
count := strlen
if count > int64(cfg.MaxStringLen) {
count = int64(cfg.MaxStringLen)
......@@ -1103,7 +1080,7 @@ func (v *Variable) writeComplex(real, imag float64, size int64) error {
return imagaddr.writeFloatRaw(imag, int64(size/2))
}
func readIntRaw(mem memoryReadWriter, addr uintptr, size int64) (int64, error) {
func readIntRaw(mem MemoryReadWriter, addr uintptr, size int64) (int64, error) {
var n int64
val := make([]byte, int(size))
......@@ -1140,11 +1117,11 @@ func (v *Variable) writeUint(value uint64, size int64) error {
binary.LittleEndian.PutUint64(val, uint64(value))
}
_, err := v.mem.writeMemory(v.Addr, val)
_, err := v.mem.WriteMemory(v.Addr, val)
return err
}
func readUintRaw(mem memoryReadWriter, addr uintptr, size int64) (uint64, error) {
func readUintRaw(mem MemoryReadWriter, addr uintptr, size int64) (uint64, error) {
var n uint64
val := make([]byte, int(size))
......@@ -1201,19 +1178,19 @@ func (v *Variable) writeFloatRaw(f float64, size int64) error {
binary.Write(buf, binary.LittleEndian, n)
}
_, err := v.mem.writeMemory(v.Addr, buf.Bytes())
_, err := v.mem.WriteMemory(v.Addr, buf.Bytes())
return err
}
func (v *Variable) writeBool(value bool) error {
val := []byte{0}
val[0] = *(*byte)(unsafe.Pointer(&value))
_, err := v.mem.writeMemory(v.Addr, val)
_, err := v.mem.WriteMemory(v.Addr, val)
return err
}
func (v *Variable) readFunctionPtr() {
val := make([]byte, v.bi.arch.PtrSize())
val := make([]byte, v.bi.Arch.PtrSize())
_, err := v.mem.ReadMemory(val, v.Addr)
if err != nil {
v.Unreadable = err
......@@ -1644,7 +1621,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
if kind&kindDirectIface == 0 {
realtyp := resolveTypedef(typ)
if _, isptr := realtyp.(*dwarf.PtrType); !isptr {
typ = pointerTo(typ, v.bi.arch)
typ = pointerTo(typ, v.bi.Arch)
}
}
......@@ -1662,7 +1639,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
// Fetches all variables of a specific type in the current function scope
func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) {
reader := scope.DwarfReader()
off, err := scope.bi.findFunctionDebugInfo(scope.PC)
off, err := scope.BinInfo.findFunctionDebugInfo(scope.PC)
if err != nil {
return nil, err
}
......
......@@ -5,6 +5,9 @@ import (
"go/ast"
"github.com/derekparker/delve/pkg/proc"
"github.com/derekparker/delve/pkg/proc/core"
"github.com/derekparker/delve/pkg/proc/gdbserial"
"github.com/derekparker/delve/pkg/proc/native"
)
// Target represents the target of the debugger. This
......@@ -89,6 +92,6 @@ type VariableEval interface {
ConvertEvalScope(gid, frame int) (*proc.EvalScope, error)
}
var _ Interface = &proc.Process{}
var _ Interface = &proc.CoreProcess{}
var _ Interface = &proc.GdbserverProcess{}
var _ Interface = &native.Process{}
var _ Interface = &core.CoreProcess{}
var _ Interface = &gdbserial.GdbserverProcess{}
......@@ -201,7 +201,7 @@ func ConvertFunction(fn *gosym.Func) *Function {
// ConvertGoroutine converts from proc.G to api.Goroutine.
func ConvertGoroutine(g *proc.G) *Goroutine {
th := g.Thread()
th := g.Thread
tid := 0
if th != nil {
tid = th.ThreadID()
......
......@@ -14,6 +14,9 @@ import (
"time"
"github.com/derekparker/delve/pkg/proc"
"github.com/derekparker/delve/pkg/proc/core"
"github.com/derekparker/delve/pkg/proc/gdbserial"
"github.com/derekparker/delve/pkg/proc/native"
"github.com/derekparker/delve/pkg/target"
"github.com/derekparker/delve/service/api"
)
......@@ -77,7 +80,7 @@ func New(config *Config) (*Debugger, error) {
case d.config.CoreFile != "":
log.Printf("opening core file %s (executable %s)", d.config.CoreFile, d.config.ProcessArgs[0])
p, err := proc.OpenCore(d.config.CoreFile, d.config.ProcessArgs[0])
p, err := core.OpenCore(d.config.CoreFile, d.config.ProcessArgs[0])
if err != nil {
return nil, err
}
......@@ -100,14 +103,14 @@ func New(config *Config) (*Debugger, error) {
func (d *Debugger) Launch(processArgs []string, wd string) (target.Interface, error) {
switch d.config.Backend {
case "native":
return proc.Launch(processArgs, wd)
return native.Launch(processArgs, wd)
case "lldb":
return proc.LLDBLaunch(processArgs, wd)
return gdbserial.LLDBLaunch(processArgs, wd)
case "default":
if runtime.GOOS == "darwin" {
return proc.LLDBLaunch(processArgs, wd)
return gdbserial.LLDBLaunch(processArgs, wd)
}
return proc.Launch(processArgs, wd)
return native.Launch(processArgs, wd)
default:
return nil, fmt.Errorf("unknown backend %q", d.config.Backend)
}
......@@ -121,20 +124,20 @@ var ErrNoAttachPath = errors.New("must specify executable path on macOS")
func (d *Debugger) Attach(pid int, path string) (target.Interface, error) {
switch d.config.Backend {
case "native":
return proc.Attach(pid)
return native.Attach(pid)
case "lldb":
if runtime.GOOS == "darwin" && path == "" {
return nil, ErrNoAttachPath
}
return proc.LLDBAttach(pid, path)
return gdbserial.LLDBAttach(pid, path)
case "default":
if runtime.GOOS == "darwin" {
if path == "" {
return nil, ErrNoAttachPath
}
return proc.LLDBAttach(pid, path)
return gdbserial.LLDBAttach(pid, path)
}
return proc.Attach(pid)
return native.Attach(pid)
default:
return nil, fmt.Errorf("unknown backend %q", d.config.Backend)
}
......
......@@ -8,6 +8,8 @@ import (
"testing"
"github.com/derekparker/delve/pkg/proc"
"github.com/derekparker/delve/pkg/proc/gdbserial"
"github.com/derekparker/delve/pkg/proc/native"
"github.com/derekparker/delve/pkg/target"
"github.com/derekparker/delve/service/api"
......@@ -84,9 +86,9 @@ func withTestProcess(name string, t *testing.T, fn func(p target.Interface, fixt
var err error
switch testBackend {
case "native":
p, err = proc.Launch([]string{fixture.Path}, ".")
p, err = native.Launch([]string{fixture.Path}, ".")
case "lldb":
p, err = proc.LLDBLaunch([]string{fixture.Path}, ".")
p, err = gdbserial.LLDBLaunch([]string{fixture.Path}, ".")
default:
t.Fatalf("unknown backend %q", testBackend)
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册