proc: refactoring: split backends to separate packages
- move native backend to pkg/proc/native - move gdbserver backend to pkg/proc/gdbserial - move core dumps backend to pkg/proc/core
This commit is contained in:
parent
92dad944d7
commit
15bac71979
@ -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)
|
||||
return fmt.Sprintf("Invalid address %#v\n", iae.Address)
|
||||
}
|
||||
|
||||
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
|
||||
_, err := thread.writeMemory(uintptr(addr), dbp.bi.arch.BreakpointInstruction())
|
||||
return err
|
||||
}
|
||||
|
||||
func (bp *Breakpoint) checkCondition(thread IThread) (bool, error) {
|
||||
// CheckCondition evaluates bp's condition on thread.
|
||||
func (bp *Breakpoint) CheckCondition(thread IThread) (bool, error) {
|
||||
if bp.Cond == nil {
|
||||
return true, nil
|
||||
}
|
||||
@ -154,9 +142,9 @@ func (bp *Breakpoint) Internal() bool {
|
||||
// NoBreakpointError is returned when trying to
|
||||
// clear a breakpoint that does not exist.
|
||||
type NoBreakpointError struct {
|
||||
addr uint64
|
||||
Addr uint64
|
||||
}
|
||||
|
||||
func (nbp NoBreakpointError) Error() string {
|
||||
return fmt.Sprintf("no breakpoint at %#v", nbp.addr)
|
||||
return fmt.Sprintf("no breakpoint at %#v", nbp.Addr)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package proc
|
||||
package core
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
@ -7,6 +7,8 @@ import (
|
||||
"go/ast"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// A SplicedMemory represents a memory space formed from multiple regions,
|
||||
@ -29,11 +31,11 @@ type SplicedMemory struct {
|
||||
type readerEntry struct {
|
||||
offset uintptr
|
||||
length uintptr
|
||||
reader MemoryReader
|
||||
reader proc.MemoryReader
|
||||
}
|
||||
|
||||
// Add adds a new region to the SplicedMemory, which may override existing regions.
|
||||
func (r *SplicedMemory) Add(reader MemoryReader, off, length uintptr) {
|
||||
func (r *SplicedMemory) Add(reader proc.MemoryReader, off, length uintptr) {
|
||||
if length == 0 {
|
||||
return
|
||||
}
|
||||
@ -141,12 +143,12 @@ func (r *OffsetReaderAt) ReadMemory(buf []byte, addr uintptr) (n int, err error)
|
||||
}
|
||||
|
||||
type CoreProcess struct {
|
||||
bi BinaryInfo
|
||||
bi proc.BinaryInfo
|
||||
core *Core
|
||||
breakpoints map[uint64]*Breakpoint
|
||||
breakpoints map[uint64]*proc.Breakpoint
|
||||
currentThread *LinuxPrStatus
|
||||
selectedGoroutine *G
|
||||
allGCache []*G
|
||||
selectedGoroutine *proc.G
|
||||
allGCache []*proc.G
|
||||
}
|
||||
|
||||
type CoreThread struct {
|
||||
@ -165,8 +167,8 @@ func OpenCore(corePath, exePath string) (*CoreProcess, error) {
|
||||
}
|
||||
p := &CoreProcess{
|
||||
core: core,
|
||||
breakpoints: make(map[uint64]*Breakpoint),
|
||||
bi: NewBinaryInfo("linux", "amd64"),
|
||||
breakpoints: make(map[uint64]*proc.Breakpoint),
|
||||
bi: proc.NewBinaryInfo("linux", "amd64"),
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
@ -178,19 +180,18 @@ func OpenCore(corePath, exePath string) (*CoreProcess, error) {
|
||||
break
|
||||
}
|
||||
|
||||
scope := &EvalScope{0, 0, p.CurrentThread(), nil, &p.bi}
|
||||
ver, isextld, err := scope.getGoInformation()
|
||||
ver, isextld, err := proc.GetGoInformation(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p.bi.arch.SetGStructOffset(ver, isextld)
|
||||
p.selectedGoroutine, _ = GetG(p.CurrentThread())
|
||||
p.bi.Arch.SetGStructOffset(ver, isextld)
|
||||
p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (p *CoreProcess) BinInfo() *BinaryInfo {
|
||||
func (p *CoreProcess) BinInfo() *proc.BinaryInfo {
|
||||
return &p.bi
|
||||
}
|
||||
|
||||
@ -202,16 +203,16 @@ func (thread *CoreThread) ReadMemory(data []byte, addr uintptr) (n int, err erro
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (thread *CoreThread) writeMemory(addr uintptr, data []byte) (int, error) {
|
||||
func (thread *CoreThread) WriteMemory(addr uintptr, data []byte) (int, error) {
|
||||
return 0, ErrWriteCore
|
||||
}
|
||||
|
||||
func (t *CoreThread) Location() (*Location, error) {
|
||||
func (t *CoreThread) Location() (*proc.Location, error) {
|
||||
f, l, fn := t.p.bi.PCToLine(t.th.Reg.Rip)
|
||||
return &Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil
|
||||
return &proc.Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil
|
||||
}
|
||||
|
||||
func (t *CoreThread) Breakpoint() (*Breakpoint, bool, error) {
|
||||
func (t *CoreThread) Breakpoint() (*proc.Breakpoint, bool, error) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
@ -219,16 +220,16 @@ func (t *CoreThread) ThreadID() int {
|
||||
return int(t.th.Pid)
|
||||
}
|
||||
|
||||
func (t *CoreThread) Registers(floatingPoint bool) (Registers, error) {
|
||||
func (t *CoreThread) Registers(floatingPoint bool) (proc.Registers, error) {
|
||||
//TODO(aarzilli): handle floating point registers
|
||||
return &t.th.Reg, nil
|
||||
}
|
||||
|
||||
func (t *CoreThread) Arch() Arch {
|
||||
return t.p.bi.arch
|
||||
func (t *CoreThread) Arch() proc.Arch {
|
||||
return t.p.bi.Arch
|
||||
}
|
||||
|
||||
func (t *CoreThread) BinInfo() *BinaryInfo {
|
||||
func (t *CoreThread) BinInfo() *proc.BinaryInfo {
|
||||
return &t.p.bi
|
||||
}
|
||||
|
||||
@ -236,19 +237,23 @@ func (t *CoreThread) StepInstruction() error {
|
||||
return ErrContinueCore
|
||||
}
|
||||
|
||||
func (p *CoreProcess) Breakpoints() map[uint64]*Breakpoint {
|
||||
func (t *CoreThread) Blocked() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *CoreProcess) Breakpoints() map[uint64]*proc.Breakpoint {
|
||||
return p.breakpoints
|
||||
}
|
||||
|
||||
func (p *CoreProcess) ClearBreakpoint(addr uint64) (*Breakpoint, error) {
|
||||
return nil, NoBreakpointError{addr: addr}
|
||||
func (p *CoreProcess) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
|
||||
return nil, proc.NoBreakpointError{Addr: addr}
|
||||
}
|
||||
|
||||
func (p *CoreProcess) ClearInternalBreakpoints() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *CoreProcess) ContinueOnce() (IThread, error) {
|
||||
func (p *CoreProcess) ContinueOnce() (proc.IThread, error) {
|
||||
return nil, ErrContinueCore
|
||||
}
|
||||
|
||||
@ -260,7 +265,7 @@ func (p *CoreProcess) RequestManualStop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *CoreProcess) CurrentThread() IThread {
|
||||
func (p *CoreProcess) CurrentThread() proc.IThread {
|
||||
return &CoreThread{p.currentThread, p}
|
||||
}
|
||||
|
||||
@ -273,18 +278,18 @@ func (p *CoreProcess) Exited() bool {
|
||||
}
|
||||
|
||||
func (p *CoreProcess) FindFileLocation(fileName string, lineNumber int) (uint64, error) {
|
||||
return FindFileLocation(p.CurrentThread(), p.breakpoints, &p.bi, fileName, lineNumber)
|
||||
return proc.FindFileLocation(p.CurrentThread(), p.breakpoints, &p.bi, fileName, lineNumber)
|
||||
}
|
||||
|
||||
func (p *CoreProcess) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
|
||||
return FirstPCAfterPrologue(p.CurrentThread(), p.breakpoints, &p.bi, fn, sameline)
|
||||
return proc.FirstPCAfterPrologue(p.CurrentThread(), p.breakpoints, &p.bi, fn, sameline)
|
||||
}
|
||||
|
||||
func (p *CoreProcess) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||
return FindFunctionLocation(p.CurrentThread(), p.breakpoints, &p.bi, funcName, firstLine, lineOffset)
|
||||
return proc.FindFunctionLocation(p.CurrentThread(), p.breakpoints, &p.bi, funcName, firstLine, lineOffset)
|
||||
}
|
||||
|
||||
func (p *CoreProcess) AllGCache() *[]*G {
|
||||
func (p *CoreProcess) AllGCache() *[]*proc.G {
|
||||
return &p.allGCache
|
||||
}
|
||||
|
||||
@ -304,16 +309,16 @@ func (p *CoreProcess) Running() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *CoreProcess) SelectedGoroutine() *G {
|
||||
func (p *CoreProcess) SelectedGoroutine() *proc.G {
|
||||
return p.selectedGoroutine
|
||||
}
|
||||
|
||||
func (p *CoreProcess) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
|
||||
func (p *CoreProcess) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
|
||||
return nil, ErrWriteCore
|
||||
}
|
||||
|
||||
func (p *CoreProcess) SwitchGoroutine(gid int) error {
|
||||
g, err := FindGoroutine(p, gid)
|
||||
g, err := proc.FindGoroutine(p, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -321,8 +326,8 @@ func (p *CoreProcess) SwitchGoroutine(gid int) error {
|
||||
// user specified -1 and selectedGoroutine is nil
|
||||
return nil
|
||||
}
|
||||
if g.thread != nil {
|
||||
return p.SwitchThread(g.thread.ThreadID())
|
||||
if g.Thread != nil {
|
||||
return p.SwitchThread(g.Thread.ThreadID())
|
||||
}
|
||||
p.selectedGoroutine = g
|
||||
return nil
|
||||
@ -331,21 +336,21 @@ func (p *CoreProcess) SwitchGoroutine(gid int) error {
|
||||
func (p *CoreProcess) SwitchThread(tid int) error {
|
||||
if th, ok := p.core.Threads[tid]; ok {
|
||||
p.currentThread = th
|
||||
p.selectedGoroutine, _ = GetG(p.CurrentThread())
|
||||
p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("thread %d does not exist", tid)
|
||||
}
|
||||
|
||||
func (p *CoreProcess) ThreadList() []IThread {
|
||||
r := make([]IThread, 0, len(p.core.Threads))
|
||||
func (p *CoreProcess) ThreadList() []proc.IThread {
|
||||
r := make([]proc.IThread, 0, len(p.core.Threads))
|
||||
for _, v := range p.core.Threads {
|
||||
r = append(r, &CoreThread{v, p})
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (p *CoreProcess) FindThread(threadID int) (IThread, bool) {
|
||||
func (p *CoreProcess) FindThread(threadID int) (proc.IThread, bool) {
|
||||
t, ok := p.core.Threads[threadID]
|
||||
return &CoreThread{t, p}, ok
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package proc
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
"github.com/derekparker/delve/pkg/proc/test"
|
||||
)
|
||||
|
||||
@ -155,13 +156,13 @@ func TestCore(t *testing.T) {
|
||||
t.Errorf("read apport log: %q, %v", apport, err)
|
||||
t.Fatalf("ReadCore() failed: %v", err)
|
||||
}
|
||||
gs, err := GoroutinesInfo(p)
|
||||
gs, err := proc.GoroutinesInfo(p)
|
||||
if err != nil || len(gs) == 0 {
|
||||
t.Fatalf("GoroutinesInfo() = %v, %v; wanted at least one goroutine", gs, err)
|
||||
}
|
||||
|
||||
var panicking *G
|
||||
var panickingStack []Stackframe
|
||||
var panicking *proc.G
|
||||
var panickingStack []proc.Stackframe
|
||||
for _, g := range gs {
|
||||
stack, err := g.Stacktrace(10)
|
||||
if err != nil {
|
||||
@ -178,7 +179,7 @@ func TestCore(t *testing.T) {
|
||||
t.Fatalf("Didn't find a call to panic in goroutine stacks: %v", gs)
|
||||
}
|
||||
|
||||
var mainFrame *Stackframe
|
||||
var mainFrame *proc.Stackframe
|
||||
// Walk backward, because the current function seems to be main.main
|
||||
// in the actual call to panic().
|
||||
for i := len(panickingStack) - 1; i >= 0; i-- {
|
||||
@ -189,7 +190,7 @@ func TestCore(t *testing.T) {
|
||||
if mainFrame == nil {
|
||||
t.Fatalf("Couldn't find main in stack %v", panickingStack)
|
||||
}
|
||||
msg, err := FrameToScope(p, *mainFrame).EvalVariable("msg", LoadConfig{MaxStringLen: 64})
|
||||
msg, err := proc.FrameToScope(p, *mainFrame).EvalVariable("msg", proc.LoadConfig{MaxStringLen: 64})
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't EvalVariable(msg, ...): %v", err)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package proc
|
||||
package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -11,6 +11,8 @@ import (
|
||||
"golang.org/x/debug/elf"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Copied from golang.org/x/sys/unix.PtraceRegs since it's not available on
|
||||
@ -232,14 +234,14 @@ func (r *LinuxCoreRegisters) Get(n int) (uint64, error) {
|
||||
return r.R15, nil
|
||||
}
|
||||
|
||||
return 0, UnknownRegisterError
|
||||
return 0, proc.UnknownRegisterError
|
||||
}
|
||||
|
||||
func (r *LinuxCoreRegisters) SetPC(IThread, uint64) error {
|
||||
func (r *LinuxCoreRegisters) SetPC(proc.IThread, uint64) error {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
|
||||
func (r *LinuxCoreRegisters) Slice() []Register {
|
||||
func (r *LinuxCoreRegisters) Slice() []proc.Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
@ -272,12 +274,12 @@ func (r *LinuxCoreRegisters) Slice() []Register {
|
||||
{"Fs", r.Fs},
|
||||
{"Gs", r.Gs},
|
||||
}
|
||||
out := make([]Register, 0, len(regs))
|
||||
out := make([]proc.Register, 0, len(regs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
|
||||
out = proc.AppendEflagReg(out, reg.k, reg.v)
|
||||
} else {
|
||||
out = appendQwordReg(out, reg.k, reg.v)
|
||||
out = proc.AppendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
return out
|
||||
@ -328,7 +330,7 @@ func readCore(corePath, exePath string) (*Core, error) {
|
||||
}
|
||||
|
||||
type Core struct {
|
||||
MemoryReader
|
||||
proc.MemoryReader
|
||||
Threads map[int]*LinuxPrStatus
|
||||
Pid int
|
||||
}
|
||||
@ -448,7 +450,7 @@ func skipPadding(r io.ReadSeeker, pad int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildMemory(core *elf.File, exe io.ReaderAt, notes []*Note) MemoryReader {
|
||||
func buildMemory(core *elf.File, exe io.ReaderAt, notes []*Note) proc.MemoryReader {
|
||||
memory := &SplicedMemory{}
|
||||
|
||||
// For now, assume all file mappings are to the exe.
|
@ -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
|
@ -1,4 +1,4 @@
|
||||
package proc
|
||||
package gdbserial
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@ -12,6 +12,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
type gdbConn struct {
|
||||
@ -630,7 +632,7 @@ func (conn *gdbConn) parseStopPacket(resp []byte, threadID string, tu *threadUpd
|
||||
semicolon = len(resp)
|
||||
}
|
||||
status, _ := strconv.ParseUint(string(resp[1:semicolon]), 16, 8)
|
||||
return false, stopPacket{}, ProcessExitedError{Pid: conn.pid, Status: int(status)}
|
||||
return false, stopPacket{}, proc.ProcessExitedError{Pid: conn.pid, Status: int(status)}
|
||||
|
||||
case 'N':
|
||||
// we were singlestepping the thread and the thread exited
|
@ -1,6 +1,6 @@
|
||||
// +build linux darwin
|
||||
|
||||
package proc
|
||||
package gdbserial
|
||||
|
||||
import "syscall"
|
||||
|
@ -1,4 +1,4 @@
|
||||
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)
|
||||
|
525
pkg/proc/native/proc.go
Normal file
525
pkg/proc/native/proc.go
Normal file
@ -0,0 +1,525 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Process represents all of the information the debugger
|
||||
// is holding onto regarding the process we are debugging.
|
||||
type Process struct {
|
||||
bi proc.BinaryInfo
|
||||
pid int // Process Pid
|
||||
Process *os.Process // Pointer to process struct for the actual process we are debugging
|
||||
|
||||
// Breakpoint table, holds information on breakpoints.
|
||||
// Maps instruction address to Breakpoint struct.
|
||||
breakpoints map[uint64]*proc.Breakpoint
|
||||
|
||||
// List of threads mapped as such: pid -> *Thread
|
||||
threads map[int]*Thread
|
||||
|
||||
// Active thread
|
||||
currentThread *Thread
|
||||
|
||||
// Goroutine that will be used by default to set breakpoint, eval variables, etc...
|
||||
// Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
|
||||
selectedGoroutine *proc.G
|
||||
|
||||
allGCache []*proc.G
|
||||
os *OSProcessDetails
|
||||
breakpointIDCounter int
|
||||
internalBreakpointIDCounter int
|
||||
firstStart bool
|
||||
halt bool
|
||||
exited bool
|
||||
ptraceChan chan func()
|
||||
ptraceDoneChan chan interface{}
|
||||
}
|
||||
|
||||
// New returns an initialized Process struct. Before returning,
|
||||
// it will also launch a goroutine in order to handle ptrace(2)
|
||||
// functions. For more information, see the documentation on
|
||||
// `handlePtraceFuncs`.
|
||||
func New(pid int) *Process {
|
||||
dbp := &Process{
|
||||
pid: pid,
|
||||
threads: make(map[int]*Thread),
|
||||
breakpoints: make(map[uint64]*proc.Breakpoint),
|
||||
firstStart: true,
|
||||
os: new(OSProcessDetails),
|
||||
ptraceChan: make(chan func()),
|
||||
ptraceDoneChan: make(chan interface{}),
|
||||
bi: proc.NewBinaryInfo(runtime.GOOS, runtime.GOARCH),
|
||||
}
|
||||
go dbp.handlePtraceFuncs()
|
||||
return dbp
|
||||
}
|
||||
|
||||
func (dbp *Process) BinInfo() *proc.BinaryInfo {
|
||||
return &dbp.bi
|
||||
}
|
||||
|
||||
// Detach from the process being debugged, optionally killing it.
|
||||
func (dbp *Process) Detach(kill bool) (err error) {
|
||||
if dbp.exited {
|
||||
return nil
|
||||
}
|
||||
if dbp.Running() {
|
||||
if err = dbp.Halt(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !kill {
|
||||
// Clean up any breakpoints we've set.
|
||||
for _, bp := range dbp.breakpoints {
|
||||
if bp != nil {
|
||||
_, err := dbp.ClearBreakpoint(bp.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dbp.execPtraceFunc(func() {
|
||||
err = dbp.detach(kill)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if kill {
|
||||
err = killProcess(dbp.pid)
|
||||
}
|
||||
})
|
||||
dbp.bi.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// Exited returns whether the debugged
|
||||
// process has exited.
|
||||
func (dbp *Process) Exited() bool {
|
||||
return dbp.exited
|
||||
}
|
||||
|
||||
// Running returns whether the debugged
|
||||
// process is currently executing.
|
||||
func (dbp *Process) Running() bool {
|
||||
for _, th := range dbp.threads {
|
||||
if th.running {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (dbp *Process) Pid() int {
|
||||
return dbp.pid
|
||||
}
|
||||
|
||||
func (dbp *Process) SelectedGoroutine() *proc.G {
|
||||
return dbp.selectedGoroutine
|
||||
}
|
||||
|
||||
func (dbp *Process) ThreadList() []proc.IThread {
|
||||
r := make([]proc.IThread, 0, len(dbp.threads))
|
||||
for _, v := range dbp.threads {
|
||||
r = append(r, v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (dbp *Process) FindThread(threadID int) (proc.IThread, bool) {
|
||||
th, ok := dbp.threads[threadID]
|
||||
return th, ok
|
||||
}
|
||||
|
||||
func (dbp *Process) CurrentThread() proc.IThread {
|
||||
return dbp.currentThread
|
||||
}
|
||||
|
||||
func (dbp *Process) Breakpoints() map[uint64]*proc.Breakpoint {
|
||||
return dbp.breakpoints
|
||||
}
|
||||
|
||||
// LoadInformation finds the executable and then uses it
|
||||
// to parse the following information:
|
||||
// * Dwarf .debug_frame section
|
||||
// * Dwarf .debug_line section
|
||||
// * Go symbol table.
|
||||
func (dbp *Process) LoadInformation(path string) error {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
path = findExecutable(path, dbp.pid)
|
||||
|
||||
wg.Add(1)
|
||||
go dbp.loadProcessInformation(&wg)
|
||||
dbp.bi.LoadBinaryInfo(path, &wg)
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||
return proc.FindFunctionLocation(dbp.currentThread, dbp.breakpoints, &dbp.bi, funcName, firstLine, lineOffset)
|
||||
}
|
||||
|
||||
func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) {
|
||||
return proc.FindFileLocation(dbp.currentThread, dbp.breakpoints, &dbp.bi, fileName, lineno)
|
||||
}
|
||||
|
||||
func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
|
||||
return proc.FirstPCAfterPrologue(dbp.currentThread, dbp.breakpoints, &dbp.bi, fn, sameline)
|
||||
}
|
||||
|
||||
// CurrentLocation returns the location of the current thread.
|
||||
func (dbp *Process) CurrentLocation() (*proc.Location, error) {
|
||||
return dbp.currentThread.Location()
|
||||
}
|
||||
|
||||
// RequestManualStop sets the `halt` flag and
|
||||
// sends SIGSTOP to all threads.
|
||||
func (dbp *Process) RequestManualStop() error {
|
||||
if dbp.exited {
|
||||
return &proc.ProcessExitedError{}
|
||||
}
|
||||
dbp.halt = true
|
||||
return dbp.requestManualStop()
|
||||
}
|
||||
|
||||
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
|
||||
// break point table. Setting a break point must be thread specific due to
|
||||
// ptrace actions needing the thread to be in a signal-delivery-stop.
|
||||
func (dbp *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
|
||||
tid := dbp.currentThread.ID
|
||||
|
||||
if bp, ok := dbp.FindBreakpoint(addr); ok {
|
||||
return nil, proc.BreakpointExistsError{bp.File, bp.Line, bp.Addr}
|
||||
}
|
||||
|
||||
f, l, fn := dbp.bi.PCToLine(uint64(addr))
|
||||
if fn == nil {
|
||||
return nil, proc.InvalidAddressError{Address: addr}
|
||||
}
|
||||
|
||||
newBreakpoint := &proc.Breakpoint{
|
||||
FunctionName: fn.Name,
|
||||
File: f,
|
||||
Line: l,
|
||||
Addr: addr,
|
||||
Kind: kind,
|
||||
Cond: cond,
|
||||
HitCount: map[int]uint64{},
|
||||
}
|
||||
|
||||
if kind != proc.UserBreakpoint {
|
||||
dbp.internalBreakpointIDCounter++
|
||||
newBreakpoint.ID = dbp.internalBreakpointIDCounter
|
||||
} else {
|
||||
dbp.breakpointIDCounter++
|
||||
newBreakpoint.ID = dbp.breakpointIDCounter
|
||||
}
|
||||
|
||||
thread := dbp.threads[tid]
|
||||
originalData := make([]byte, dbp.bi.Arch.BreakpointSize())
|
||||
_, err := thread.ReadMemory(originalData, uintptr(addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := dbp.writeSoftwareBreakpoint(thread, addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newBreakpoint.OriginalData = originalData
|
||||
dbp.breakpoints[addr] = newBreakpoint
|
||||
|
||||
return newBreakpoint, nil
|
||||
}
|
||||
|
||||
// ClearBreakpoint clears the breakpoint at addr.
|
||||
func (dbp *Process) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
|
||||
if dbp.exited {
|
||||
return nil, &proc.ProcessExitedError{}
|
||||
}
|
||||
bp, ok := dbp.FindBreakpoint(addr)
|
||||
if !ok {
|
||||
return nil, proc.NoBreakpointError{Addr: addr}
|
||||
}
|
||||
|
||||
if _, err := dbp.currentThread.ClearBreakpoint(bp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(dbp.breakpoints, addr)
|
||||
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
// Status returns the status of the current main thread context.
|
||||
func (dbp *Process) Status() *WaitStatus {
|
||||
return dbp.currentThread.Status
|
||||
}
|
||||
|
||||
func (dbp *Process) ContinueOnce() (proc.IThread, error) {
|
||||
if dbp.exited {
|
||||
return nil, &proc.ProcessExitedError{}
|
||||
}
|
||||
|
||||
if err := dbp.resume(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.allGCache = nil
|
||||
for _, th := range dbp.threads {
|
||||
th.clearBreakpointState()
|
||||
}
|
||||
|
||||
trapthread, err := dbp.trapWait(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := dbp.Halt(); err != nil {
|
||||
return nil, dbp.exitGuard(err)
|
||||
}
|
||||
if err := dbp.setCurrentBreakpoints(trapthread); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return trapthread, err
|
||||
}
|
||||
|
||||
// StepInstruction will continue the current thread for exactly
|
||||
// one instruction. This method affects only the thread
|
||||
// asssociated with the selected goroutine. All other
|
||||
// threads will remain stopped.
|
||||
func (dbp *Process) StepInstruction() (err error) {
|
||||
if dbp.selectedGoroutine == nil {
|
||||
return errors.New("cannot single step: no selected goroutine")
|
||||
}
|
||||
if dbp.selectedGoroutine.Thread == nil {
|
||||
// Step called on parked goroutine
|
||||
if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(dbp.selectedGoroutine)); err != nil {
|
||||
return err
|
||||
}
|
||||
return proc.Continue(dbp)
|
||||
}
|
||||
dbp.allGCache = nil
|
||||
if dbp.exited {
|
||||
return &proc.ProcessExitedError{}
|
||||
}
|
||||
dbp.selectedGoroutine.Thread.(*Thread).clearBreakpointState()
|
||||
err = dbp.selectedGoroutine.Thread.(*Thread).StepInstruction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dbp.selectedGoroutine.Thread.(*Thread).SetCurrentBreakpoint()
|
||||
}
|
||||
|
||||
// SwitchThread changes from current thread to the thread specified by `tid`.
|
||||
func (dbp *Process) SwitchThread(tid int) error {
|
||||
if dbp.exited {
|
||||
return &proc.ProcessExitedError{}
|
||||
}
|
||||
if th, ok := dbp.threads[tid]; ok {
|
||||
dbp.currentThread = th
|
||||
dbp.selectedGoroutine, _ = proc.GetG(dbp.currentThread)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("thread %d does not exist", tid)
|
||||
}
|
||||
|
||||
// SwitchGoroutine changes from current thread to the thread
|
||||
// running the specified goroutine.
|
||||
func (dbp *Process) SwitchGoroutine(gid int) error {
|
||||
if dbp.exited {
|
||||
return &proc.ProcessExitedError{}
|
||||
}
|
||||
g, err := proc.FindGoroutine(dbp, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if g == nil {
|
||||
// user specified -1 and selectedGoroutine is nil
|
||||
return nil
|
||||
}
|
||||
if g.Thread != nil {
|
||||
return dbp.SwitchThread(g.Thread.ThreadID())
|
||||
}
|
||||
dbp.selectedGoroutine = g
|
||||
return nil
|
||||
}
|
||||
|
||||
// Halt stops all threads.
|
||||
func (dbp *Process) Halt() (err error) {
|
||||
if dbp.exited {
|
||||
return &proc.ProcessExitedError{}
|
||||
}
|
||||
for _, th := range dbp.threads {
|
||||
if err := th.Halt(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Registers obtains register values from the
|
||||
// "current" thread of the traced process.
|
||||
func (dbp *Process) Registers() (proc.Registers, error) {
|
||||
return dbp.currentThread.Registers(false)
|
||||
}
|
||||
|
||||
// PC returns the PC of the current thread.
|
||||
func (dbp *Process) PC() (uint64, error) {
|
||||
return dbp.currentThread.PC()
|
||||
}
|
||||
|
||||
// CurrentBreakpoint returns the breakpoint the current thread
|
||||
// is stopped at.
|
||||
func (dbp *Process) CurrentBreakpoint() *proc.Breakpoint {
|
||||
return dbp.currentThread.CurrentBreakpoint
|
||||
}
|
||||
|
||||
// FindBreakpointByID finds the breakpoint for the given ID.
|
||||
func (dbp *Process) FindBreakpointByID(id int) (*proc.Breakpoint, bool) {
|
||||
for _, bp := range dbp.breakpoints {
|
||||
if bp.ID == id {
|
||||
return bp, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// FindBreakpoint finds the breakpoint for the given pc.
|
||||
func (dbp *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
|
||||
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
|
||||
if bp, ok := dbp.breakpoints[pc-uint64(dbp.bi.Arch.BreakpointSize())]; ok {
|
||||
return bp, true
|
||||
}
|
||||
// Directly use addr to lookup breakpoint.
|
||||
if bp, ok := dbp.breakpoints[pc]; ok {
|
||||
return bp, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Returns a new Process struct.
|
||||
func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, error) {
|
||||
if attach {
|
||||
var err error
|
||||
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, _, err = dbp.wait(dbp.pid, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
process, err := os.FindProcess(dbp.pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.Process = process
|
||||
err = dbp.LoadInformation(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := dbp.updateThreadList(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ver, isextld, err := proc.GetGoInformation(dbp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.bi.Arch.SetGStructOffset(ver, isextld)
|
||||
// selectedGoroutine can not be set correctly by the call to updateThreadList
|
||||
// because without calling SetGStructOffset we can not read the G struct of currentThread
|
||||
// but without calling updateThreadList we can not examine memory to determine
|
||||
// the offset of g struct inside TLS
|
||||
dbp.selectedGoroutine, _ = proc.GetG(dbp.currentThread)
|
||||
|
||||
panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0)
|
||||
if err == nil {
|
||||
bp, err := dbp.SetBreakpoint(panicpc, proc.UserBreakpoint, nil)
|
||||
if err == nil {
|
||||
bp.Name = "unrecovered-panic"
|
||||
bp.ID = -1
|
||||
dbp.breakpointIDCounter--
|
||||
}
|
||||
}
|
||||
|
||||
return dbp, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) ClearInternalBreakpoints() error {
|
||||
for _, bp := range dbp.breakpoints {
|
||||
if !bp.Internal() {
|
||||
continue
|
||||
}
|
||||
if _, err := dbp.ClearBreakpoint(bp.Addr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := range dbp.threads {
|
||||
if dbp.threads[i].CurrentBreakpoint != nil && dbp.threads[i].CurrentBreakpoint.Internal() {
|
||||
dbp.threads[i].CurrentBreakpoint = nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) handlePtraceFuncs() {
|
||||
// We must ensure here that we are running on the same thread during
|
||||
// while invoking the ptrace(2) syscall. This is due to the fact that ptrace(2) expects
|
||||
// all commands after PTRACE_ATTACH to come from the same thread.
|
||||
runtime.LockOSThread()
|
||||
|
||||
for fn := range dbp.ptraceChan {
|
||||
fn()
|
||||
dbp.ptraceDoneChan <- nil
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) execPtraceFunc(fn func()) {
|
||||
dbp.ptraceChan <- fn
|
||||
<-dbp.ptraceDoneChan
|
||||
}
|
||||
|
||||
func (dbp *Process) postExit() {
|
||||
dbp.exited = true
|
||||
close(dbp.ptraceChan)
|
||||
close(dbp.ptraceDoneChan)
|
||||
}
|
||||
|
||||
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
|
||||
_, err := thread.WriteMemory(uintptr(addr), dbp.bi.Arch.BreakpointInstruction())
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbp *Process) AllGCache() *[]*proc.G {
|
||||
return &dbp.allGCache
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
// EvalPackageVariable will evaluate the package level variable
|
||||
// specified by 'name'.
|
||||
func (dbp *Process) EvalPackageVariable(name string, cfg proc.LoadConfig) (*proc.Variable, error) {
|
||||
scope := &proc.EvalScope{0, 0, dbp.currentThread, nil, dbp.BinInfo()}
|
||||
|
||||
v, err := scope.packageVarAddr(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.loadValue(cfg)
|
||||
return v, nil
|
||||
}
|
||||
*/
|
@ -1,4 +1,4 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
// #include "proc_darwin.h"
|
||||
// #include "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
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -16,6 +16,8 @@ import (
|
||||
"time"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Process statuses
|
||||
@ -43,34 +45,34 @@ type OSProcessDetails struct {
|
||||
// to be supplied to that process. `wd` is working directory of the program.
|
||||
func Launch(cmd []string, wd string) (*Process, error) {
|
||||
var (
|
||||
proc *exec.Cmd
|
||||
err error
|
||||
process *exec.Cmd
|
||||
err error
|
||||
)
|
||||
// check that the argument to Launch is an executable file
|
||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
||||
return nil, NotExecutableErr
|
||||
return nil, proc.NotExecutableErr
|
||||
}
|
||||
dbp := New(0)
|
||||
dbp.execPtraceFunc(func() {
|
||||
proc = exec.Command(cmd[0])
|
||||
proc.Args = cmd
|
||||
proc.Stdout = os.Stdout
|
||||
proc.Stderr = os.Stderr
|
||||
proc.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true}
|
||||
process = exec.Command(cmd[0])
|
||||
process.Args = cmd
|
||||
process.Stdout = os.Stdout
|
||||
process.Stderr = os.Stderr
|
||||
process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true}
|
||||
if wd != "" {
|
||||
proc.Dir = wd
|
||||
process.Dir = wd
|
||||
}
|
||||
err = proc.Start()
|
||||
err = process.Start()
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbp.pid = proc.Process.Pid
|
||||
_, _, err = dbp.wait(proc.Process.Pid, 0)
|
||||
dbp.pid = process.Process.Pid
|
||||
_, _, err = dbp.wait(process.Process.Pid, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
||||
}
|
||||
return initializeDebugProcess(dbp, proc.Path, false)
|
||||
return initializeDebugProcess(dbp, process.Path, false)
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID.
|
||||
@ -189,7 +191,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
if status.Exited() {
|
||||
if wpid == dbp.pid {
|
||||
dbp.postExit()
|
||||
return nil, ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
|
||||
return nil, proc.ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
|
||||
}
|
||||
delete(dbp.threads, wpid)
|
||||
continue
|
||||
@ -246,7 +248,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
// TODO(dp) alert user about unexpected signals here.
|
||||
if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
|
||||
if err == sys.ESRCH {
|
||||
return nil, ProcessExitedError{Pid: dbp.pid}
|
||||
return nil, proc.ProcessExitedError{Pid: dbp.pid}
|
||||
}
|
||||
return nil, err
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
import (
|
||||
"debug/pe"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@ -11,6 +13,8 @@ import (
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/windows"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// OSProcessDetails holds Windows specific information.
|
||||
@ -19,6 +23,19 @@ type OSProcessDetails struct {
|
||||
breakThread int
|
||||
}
|
||||
|
||||
func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
|
||||
f, err := os.OpenFile(path, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
peFile, err := pe.NewFile(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
return peFile, f, nil
|
||||
}
|
||||
|
||||
// Launch creates and begins debugging a new process.
|
||||
func Launch(cmd []string, wd string) (*Process, error) {
|
||||
argv0Go, err := filepath.Abs(cmd[0])
|
||||
@ -35,7 +52,7 @@ func Launch(cmd []string, wd string) (*Process, error) {
|
||||
|
||||
_, closer, err := openExecutablePathPE(argv0Go)
|
||||
if err != nil {
|
||||
return nil, NotExecutableErr
|
||||
return nil, proc.NotExecutableErr
|
||||
}
|
||||
closer.Close()
|
||||
|
||||
@ -129,7 +146,7 @@ func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
|
||||
}
|
||||
if tid == 0 {
|
||||
dbp.postExit()
|
||||
return nil, ProcessExitedError{Pid: dbp.pid, Status: exitCode}
|
||||
return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: exitCode}
|
||||
}
|
||||
// Suspend all threads so that the call to _ContinueDebugEvent will
|
||||
// not resume the target.
|
||||
@ -330,9 +347,9 @@ func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, e
|
||||
// this exception anymore.
|
||||
atbp := true
|
||||
if thread, found := dbp.threads[tid]; found {
|
||||
data := make([]byte, dbp.bi.arch.BreakpointSize())
|
||||
data := make([]byte, dbp.bi.Arch.BreakpointSize())
|
||||
if _, err := thread.ReadMemory(data, exception.ExceptionRecord.ExceptionAddress); err == nil {
|
||||
instr := dbp.bi.arch.BreakpointInstruction()
|
||||
instr := dbp.bi.Arch.BreakpointInstruction()
|
||||
for i := range instr {
|
||||
if data[i] != instr[i] {
|
||||
atbp = false
|
||||
@ -388,7 +405,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
}
|
||||
if tid == 0 {
|
||||
dbp.postExit()
|
||||
return nil, ProcessExitedError{Pid: dbp.pid, Status: exitCode}
|
||||
return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: exitCode}
|
||||
}
|
||||
th := dbp.threads[tid]
|
||||
return th, nil
|
@ -1,4 +1,4 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
import sys "golang.org/x/sys/unix"
|
||||
|
@ -1,4 +1,4 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
@ -1,4 +1,4 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
@ -1,4 +1,4 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
// #include "threads_darwin.h"
|
||||
import "C"
|
||||
@ -8,6 +8,8 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Regs represents CPU registers on an AMD64 processor.
|
||||
@ -34,10 +36,10 @@ type Regs struct {
|
||||
fs uint64
|
||||
gs uint64
|
||||
gsBase uint64
|
||||
fpregs []Register
|
||||
fpregs []proc.Register
|
||||
}
|
||||
|
||||
func (r *Regs) Slice() []Register {
|
||||
func (r *Regs) Slice() []proc.Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
@ -65,12 +67,12 @@ func (r *Regs) Slice() []Register {
|
||||
{"Gs", r.gs},
|
||||
{"Gs_base", r.gsBase},
|
||||
}
|
||||
out := make([]Register, 0, len(regs)+len(r.fpregs))
|
||||
out := make([]proc.Register, 0, len(regs)+len(r.fpregs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Rflags" {
|
||||
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
|
||||
out = proc.AppendEflagReg(out, reg.k, reg.v)
|
||||
} else {
|
||||
out = appendQwordReg(out, reg.k, reg.v)
|
||||
out = proc.AppendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
out = append(out, r.fpregs...)
|
||||
@ -110,7 +112,7 @@ func (r *Regs) GAddr() (uint64, bool) {
|
||||
}
|
||||
|
||||
// SetPC sets the RIP register to the value specified by `pc`.
|
||||
func (r *Regs) SetPC(t IThread, pc uint64) error {
|
||||
func (r *Regs) SetPC(t proc.IThread, pc uint64) error {
|
||||
thread := t.(*Thread)
|
||||
kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc))
|
||||
if kret != C.KERN_SUCCESS {
|
||||
@ -273,10 +275,10 @@ func (r *Regs) Get(n int) (uint64, error) {
|
||||
return r.r15, nil
|
||||
}
|
||||
|
||||
return 0, UnknownRegisterError
|
||||
return 0, proc.UnknownRegisterError
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
|
||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
||||
var state C.x86_thread_state64_t
|
||||
var identity C.thread_identifier_info_data_t
|
||||
kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &state)
|
||||
@ -332,31 +334,31 @@ func registers(thread *Thread, floatingPoint bool) (Registers, error) {
|
||||
return nil, fmt.Errorf("could not get floating point registers")
|
||||
}
|
||||
|
||||
regs.fpregs = appendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))
|
||||
regs.fpregs = appendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))
|
||||
regs.fpregs = appendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw))
|
||||
regs.fpregs = appendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop))
|
||||
regs.fpregs = appendQwordReg(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip))
|
||||
regs.fpregs = appendQwordReg(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp))
|
||||
regs.fpregs = proc.AppendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))
|
||||
regs.fpregs = proc.AppendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))
|
||||
regs.fpregs = proc.AppendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw))
|
||||
regs.fpregs = proc.AppendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop))
|
||||
regs.fpregs = proc.AppendQwordReg(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip))
|
||||
regs.fpregs = proc.AppendQwordReg(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp))
|
||||
|
||||
for i, st := range []*C.char{&fpstate.__fpu_stmm0.__mmst_reg[0], &fpstate.__fpu_stmm1.__mmst_reg[0], &fpstate.__fpu_stmm2.__mmst_reg[0], &fpstate.__fpu_stmm3.__mmst_reg[0], &fpstate.__fpu_stmm4.__mmst_reg[0], &fpstate.__fpu_stmm5.__mmst_reg[0], &fpstate.__fpu_stmm6.__mmst_reg[0], &fpstate.__fpu_stmm7.__mmst_reg[0]} {
|
||||
stb := C.GoBytes(unsafe.Pointer(st), 10)
|
||||
mantissa := binary.LittleEndian.Uint64(stb[:8])
|
||||
exponent := binary.LittleEndian.Uint16(stb[8:])
|
||||
regs.fpregs = appendX87Reg(regs.fpregs, i, exponent, mantissa)
|
||||
regs.fpregs = proc.AppendX87Reg(regs.fpregs, i, exponent, mantissa)
|
||||
}
|
||||
|
||||
regs.fpregs = appendFlagReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr), mxcsrDescription, 32)
|
||||
regs.fpregs = appendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask))
|
||||
regs.fpregs = proc.AppendMxcsrReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr))
|
||||
regs.fpregs = proc.AppendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask))
|
||||
|
||||
for i, xmm := range []*C.char{&fpstate.__fpu_xmm0.__xmm_reg[0], &fpstate.__fpu_xmm1.__xmm_reg[0], &fpstate.__fpu_xmm2.__xmm_reg[0], &fpstate.__fpu_xmm3.__xmm_reg[0], &fpstate.__fpu_xmm4.__xmm_reg[0], &fpstate.__fpu_xmm5.__xmm_reg[0], &fpstate.__fpu_xmm6.__xmm_reg[0], &fpstate.__fpu_xmm7.__xmm_reg[0], &fpstate.__fpu_xmm8.__xmm_reg[0], &fpstate.__fpu_xmm9.__xmm_reg[0], &fpstate.__fpu_xmm10.__xmm_reg[0], &fpstate.__fpu_xmm11.__xmm_reg[0], &fpstate.__fpu_xmm12.__xmm_reg[0], &fpstate.__fpu_xmm13.__xmm_reg[0], &fpstate.__fpu_xmm14.__xmm_reg[0], &fpstate.__fpu_xmm15.__xmm_reg[0]} {
|
||||
regs.fpregs = appendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16))
|
||||
regs.fpregs = proc.AppendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16))
|
||||
}
|
||||
}
|
||||
return regs, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) saveRegisters() (Registers, error) {
|
||||
func (thread *Thread) saveRegisters() (proc.Registers, error) {
|
||||
kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &thread.os.registers)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not save register contents")
|
@ -1,19 +1,21 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Regs is a wrapper for sys.PtraceRegs.
|
||||
type Regs struct {
|
||||
regs *sys.PtraceRegs
|
||||
fpregs []Register
|
||||
fpregs []proc.Register
|
||||
}
|
||||
|
||||
func (r *Regs) Slice() []Register {
|
||||
func (r *Regs) Slice() []proc.Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
@ -46,12 +48,12 @@ func (r *Regs) Slice() []Register {
|
||||
{"Fs", r.regs.Fs},
|
||||
{"Gs", r.regs.Gs},
|
||||
}
|
||||
out := make([]Register, 0, len(regs)+len(r.fpregs))
|
||||
out := make([]proc.Register, 0, len(regs)+len(r.fpregs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
|
||||
out = proc.AppendEflagReg(out, reg.k, reg.v)
|
||||
} else {
|
||||
out = appendQwordReg(out, reg.k, reg.v)
|
||||
out = proc.AppendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
out = append(out, r.fpregs...)
|
||||
@ -88,7 +90,7 @@ func (r *Regs) GAddr() (uint64, bool) {
|
||||
}
|
||||
|
||||
// SetPC sets RIP to the value specified by 'pc'.
|
||||
func (r *Regs) SetPC(t IThread, pc uint64) (err error) {
|
||||
func (r *Regs) SetPC(t proc.IThread, pc uint64) (err error) {
|
||||
thread := t.(*Thread)
|
||||
r.regs.SetPC(pc)
|
||||
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) })
|
||||
@ -249,10 +251,10 @@ func (r *Regs) Get(n int) (uint64, error) {
|
||||
return r.regs.R15, nil
|
||||
}
|
||||
|
||||
return 0, UnknownRegisterError
|
||||
return 0, proc.UnknownRegisterError
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
|
||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
||||
var (
|
||||
regs sys.PtraceRegs
|
||||
err error
|
||||
@ -302,30 +304,30 @@ const (
|
||||
_XSAVE_SSE_REGION_LEN = 416
|
||||
)
|
||||
|
||||
func (thread *Thread) fpRegisters() (regs []Register, err error) {
|
||||
func (thread *Thread) fpRegisters() (regs []proc.Register, err error) {
|
||||
var fpregs PtraceXsave
|
||||
thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
|
||||
|
||||
// x87 registers
|
||||
regs = appendWordReg(regs, "CW", fpregs.Cwd)
|
||||
regs = appendWordReg(regs, "SW", fpregs.Swd)
|
||||
regs = appendWordReg(regs, "TW", fpregs.Ftw)
|
||||
regs = appendWordReg(regs, "FOP", fpregs.Fop)
|
||||
regs = appendQwordReg(regs, "FIP", fpregs.Rip)
|
||||
regs = appendQwordReg(regs, "FDP", fpregs.Rdp)
|
||||
regs = proc.AppendWordReg(regs, "CW", fpregs.Cwd)
|
||||
regs = proc.AppendWordReg(regs, "SW", fpregs.Swd)
|
||||
regs = proc.AppendWordReg(regs, "TW", fpregs.Ftw)
|
||||
regs = proc.AppendWordReg(regs, "FOP", fpregs.Fop)
|
||||
regs = proc.AppendQwordReg(regs, "FIP", fpregs.Rip)
|
||||
regs = proc.AppendQwordReg(regs, "FDP", fpregs.Rdp)
|
||||
|
||||
for i := 0; i < len(fpregs.StSpace); i += 4 {
|
||||
regs = appendX87Reg(regs, i/4, uint16(fpregs.StSpace[i+2]), uint64(fpregs.StSpace[i+1])<<32|uint64(fpregs.StSpace[i]))
|
||||
regs = proc.AppendX87Reg(regs, i/4, uint16(fpregs.StSpace[i+2]), uint64(fpregs.StSpace[i+1])<<32|uint64(fpregs.StSpace[i]))
|
||||
}
|
||||
|
||||
// SSE registers
|
||||
regs = appendFlagReg(regs, "MXCSR", uint64(fpregs.Mxcsr), mxcsrDescription, 32)
|
||||
regs = appendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask)
|
||||
regs = proc.AppendMxcsrReg(regs, "MXCSR", uint64(fpregs.Mxcsr))
|
||||
regs = proc.AppendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask)
|
||||
|
||||
for i := 0; i < len(fpregs.XmmSpace); i += 16 {
|
||||
regs = appendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), fpregs.XmmSpace[i:i+16])
|
||||
regs = proc.AppendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), fpregs.XmmSpace[i:i+16])
|
||||
if fpregs.AvxState {
|
||||
regs = appendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), fpregs.YmmSpace[i:i+16])
|
||||
regs = proc.AppendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), fpregs.YmmSpace[i:i+16])
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Regs represents CPU registers on an AMD64 processor.
|
||||
@ -34,7 +36,7 @@ type Regs struct {
|
||||
fltSave *_XMM_SAVE_AREA32
|
||||
}
|
||||
|
||||
func (r *Regs) Slice() []Register {
|
||||
func (r *Regs) Slice() []proc.Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
@ -66,31 +68,31 @@ func (r *Regs) Slice() []Register {
|
||||
if r.fltSave != nil {
|
||||
outlen += 6 + 8 + 2 + 16
|
||||
}
|
||||
out := make([]Register, 0, outlen)
|
||||
out := make([]proc.Register, 0, outlen)
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = append(out, Register{reg.k, eflagsDescription.Describe(reg.v, 64)})
|
||||
out = proc.AppendEflagReg(out, reg.k, reg.v)
|
||||
} else {
|
||||
out = appendQwordReg(out, reg.k, reg.v)
|
||||
out = proc.AppendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
if r.fltSave != nil {
|
||||
out = appendWordReg(out, "CW", r.fltSave.ControlWord)
|
||||
out = appendWordReg(out, "SW", r.fltSave.StatusWord)
|
||||
out = appendWordReg(out, "TW", uint16(r.fltSave.TagWord))
|
||||
out = appendWordReg(out, "FOP", r.fltSave.ErrorOpcode)
|
||||
out = appendQwordReg(out, "FIP", uint64(r.fltSave.ErrorSelector)<<32|uint64(r.fltSave.ErrorOffset))
|
||||
out = appendQwordReg(out, "FDP", uint64(r.fltSave.DataSelector)<<32|uint64(r.fltSave.DataOffset))
|
||||
out = proc.AppendWordReg(out, "CW", r.fltSave.ControlWord)
|
||||
out = proc.AppendWordReg(out, "SW", r.fltSave.StatusWord)
|
||||
out = proc.AppendWordReg(out, "TW", uint16(r.fltSave.TagWord))
|
||||
out = proc.AppendWordReg(out, "FOP", r.fltSave.ErrorOpcode)
|
||||
out = proc.AppendQwordReg(out, "FIP", uint64(r.fltSave.ErrorSelector)<<32|uint64(r.fltSave.ErrorOffset))
|
||||
out = proc.AppendQwordReg(out, "FDP", uint64(r.fltSave.DataSelector)<<32|uint64(r.fltSave.DataOffset))
|
||||
|
||||
for i := range r.fltSave.FloatRegisters {
|
||||
out = appendX87Reg(out, i, uint16(r.fltSave.FloatRegisters[i].High), r.fltSave.FloatRegisters[i].Low)
|
||||
out = proc.AppendX87Reg(out, i, uint16(r.fltSave.FloatRegisters[i].High), r.fltSave.FloatRegisters[i].Low)
|
||||
}
|
||||
|
||||
out = appendFlagReg(out, "MXCSR", uint64(r.fltSave.MxCsr), mxcsrDescription, 32)
|
||||
out = appendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask)
|
||||
out = proc.AppendMxcsrReg(out, "MXCSR", uint64(r.fltSave.MxCsr))
|
||||
out = proc.AppendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask)
|
||||
|
||||
for i := 0; i < len(r.fltSave.XmmRegisters); i += 16 {
|
||||
out = appendSSEReg(out, fmt.Sprintf("XMM%d", i/16), r.fltSave.XmmRegisters[i:i+16])
|
||||
out = proc.AppendSSEReg(out, fmt.Sprintf("XMM%d", i/16), r.fltSave.XmmRegisters[i:i+16])
|
||||
}
|
||||
}
|
||||
return out
|
||||
@ -129,7 +131,7 @@ func (r *Regs) GAddr() (uint64, bool) {
|
||||
}
|
||||
|
||||
// SetPC sets the RIP register to the value specified by `pc`.
|
||||
func (r *Regs) SetPC(t IThread, pc uint64) error {
|
||||
func (r *Regs) SetPC(t proc.IThread, pc uint64) error {
|
||||
thread := t.(*Thread)
|
||||
context := newCONTEXT()
|
||||
context.ContextFlags = _CONTEXT_ALL
|
||||
@ -298,10 +300,10 @@ func (r *Regs) Get(n int) (uint64, error) {
|
||||
return r.r15, nil
|
||||
}
|
||||
|
||||
return 0, UnknownRegisterError
|
||||
return 0, proc.UnknownRegisterError
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
|
||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
||||
context := newCONTEXT()
|
||||
|
||||
context.ContextFlags = _CONTEXT_ALL
|
||||
@ -348,7 +350,7 @@ func registers(thread *Thread, floatingPoint bool) (Registers, error) {
|
||||
return regs, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) saveRegisters() (Registers, error) {
|
||||
func (thread *Thread) saveRegisters() (proc.Registers, error) {
|
||||
return nil, fmt.Errorf("not implemented: saveRegisters")
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
|
||||
|
||||
package proc
|
||||
package native
|
||||
|
||||
import (
|
||||
"syscall"
|
@ -1,4 +1,4 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
import "unsafe"
|
||||
|
199
pkg/proc/native/threads.go
Normal file
199
pkg/proc/native/threads.go
Normal file
@ -0,0 +1,199 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Thread represents a single thread in the traced process
|
||||
// ID represents the thread id or port, Process holds a reference to the
|
||||
// Process struct that contains info on the process as
|
||||
// a whole, and Status represents the last result of a `wait` call
|
||||
// on this thread.
|
||||
type Thread struct {
|
||||
ID int // Thread ID or mach port
|
||||
Status *WaitStatus // Status returned from last wait call
|
||||
CurrentBreakpoint *proc.Breakpoint // Breakpoint thread is currently stopped at
|
||||
BreakpointConditionMet bool // Output of evaluating the breakpoint's condition
|
||||
BreakpointConditionError error // Error evaluating the breakpoint's condition
|
||||
|
||||
dbp *Process
|
||||
singleStepping bool
|
||||
running bool
|
||||
os *OSSpecificDetails
|
||||
}
|
||||
|
||||
// Continue the execution of this thread.
|
||||
//
|
||||
// If we are currently at a breakpoint, we'll clear it
|
||||
// first and then resume execution. Thread will continue until
|
||||
// it hits a breakpoint or is signaled.
|
||||
func (thread *Thread) Continue() error {
|
||||
pc, err := thread.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check whether we are stopped at a breakpoint, and
|
||||
// if so, single step over it before continuing.
|
||||
if _, ok := thread.dbp.FindBreakpoint(pc); ok {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return thread.resume()
|
||||
}
|
||||
|
||||
// StepInstruction steps a single instruction.
|
||||
//
|
||||
// Executes exactly one instruction and then returns.
|
||||
// If the thread is at a breakpoint, we first clear it,
|
||||
// execute the instruction, and then replace the breakpoint.
|
||||
// Otherwise we simply execute the next instruction.
|
||||
func (thread *Thread) StepInstruction() (err error) {
|
||||
thread.running = true
|
||||
thread.singleStepping = true
|
||||
defer func() {
|
||||
thread.singleStepping = false
|
||||
thread.running = false
|
||||
}()
|
||||
pc, err := thread.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bp, ok := thread.dbp.FindBreakpoint(pc)
|
||||
if ok {
|
||||
// Clear the breakpoint so that we can continue execution.
|
||||
_, err = thread.ClearBreakpoint(bp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Restore breakpoint now that we have passed it.
|
||||
defer func() {
|
||||
err = thread.dbp.writeSoftwareBreakpoint(thread, bp.Addr)
|
||||
}()
|
||||
}
|
||||
|
||||
err = thread.singleStep()
|
||||
if err != nil {
|
||||
if _, exited := err.(proc.ProcessExitedError); exited {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("step failed: %s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Location returns the threads location, including the file:line
|
||||
// of the corresponding source code, the function we're in
|
||||
// and the current instruction address.
|
||||
func (thread *Thread) Location() (*proc.Location, error) {
|
||||
pc, err := thread.PC()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, l, fn := thread.dbp.bi.PCToLine(pc)
|
||||
return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) Arch() proc.Arch {
|
||||
return thread.dbp.bi.Arch
|
||||
}
|
||||
|
||||
func (thread *Thread) BinInfo() *proc.BinaryInfo {
|
||||
return &thread.dbp.bi
|
||||
}
|
||||
|
||||
// SetPC sets the PC for this thread.
|
||||
func (thread *Thread) SetPC(pc uint64) error {
|
||||
regs, err := thread.Registers(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return regs.SetPC(thread, pc)
|
||||
}
|
||||
|
||||
// Stopped returns whether the thread is stopped at
|
||||
// the operating system level. Actual implementation
|
||||
// is OS dependant, look in OS thread file.
|
||||
func (thread *Thread) Stopped() bool {
|
||||
return thread.stopped()
|
||||
}
|
||||
|
||||
// Halt stops this thread from executing. Actual
|
||||
// implementation is OS dependant. Look in OS
|
||||
// thread file.
|
||||
func (thread *Thread) Halt() (err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
thread.running = false
|
||||
}
|
||||
}()
|
||||
if thread.Stopped() {
|
||||
return
|
||||
}
|
||||
err = thread.halt()
|
||||
return
|
||||
}
|
||||
|
||||
// SetCurrentBreakpoint sets the current breakpoint that this
|
||||
// thread is stopped at as CurrentBreakpoint on the thread struct.
|
||||
func (thread *Thread) SetCurrentBreakpoint() error {
|
||||
thread.CurrentBreakpoint = nil
|
||||
pc, err := thread.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bp, ok := thread.dbp.FindBreakpoint(pc); ok {
|
||||
thread.CurrentBreakpoint = bp
|
||||
if err = thread.SetPC(bp.Addr); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.CheckCondition(thread)
|
||||
if thread.CurrentBreakpoint != nil && thread.BreakpointConditionMet {
|
||||
if g, err := proc.GetG(thread); err == nil {
|
||||
thread.CurrentBreakpoint.HitCount[g.ID]++
|
||||
}
|
||||
thread.CurrentBreakpoint.TotalHitCount++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (thread *Thread) clearBreakpointState() {
|
||||
thread.CurrentBreakpoint = nil
|
||||
thread.BreakpointConditionMet = false
|
||||
thread.BreakpointConditionError = nil
|
||||
}
|
||||
|
||||
func (th *Thread) Breakpoint() (*proc.Breakpoint, bool, error) {
|
||||
return th.CurrentBreakpoint, (th.CurrentBreakpoint != nil && th.BreakpointConditionMet), th.BreakpointConditionError
|
||||
}
|
||||
|
||||
func (th *Thread) ThreadID() int {
|
||||
return th.ID
|
||||
}
|
||||
|
||||
// ClearBreakpoint clears the specified breakpoint.
|
||||
func (thread *Thread) ClearBreakpoint(bp *proc.Breakpoint) (*proc.Breakpoint, error) {
|
||||
if _, err := thread.WriteMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
|
||||
return nil, fmt.Errorf("could not clear breakpoint %s", err)
|
||||
}
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
// Registers obtains register values from the debugged process.
|
||||
func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
|
||||
return registers(t, floatingPoint)
|
||||
}
|
||||
|
||||
// PC returns the current PC for this thread.
|
||||
func (t *Thread) PC() (uint64, error) {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return regs.PC(), nil
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
// #include "threads_darwin.h"
|
||||
// #include "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
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
type WaitStatus sys.WaitStatus
|
||||
@ -59,7 +61,7 @@ func (t *Thread) singleStep() (err error) {
|
||||
if status != nil {
|
||||
rs = status.ExitStatus()
|
||||
}
|
||||
return ProcessExitedError{Pid: t.dbp.pid, Status: rs}
|
||||
return proc.ProcessExitedError{Pid: t.dbp.pid, Status: rs}
|
||||
}
|
||||
if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
|
||||
return nil
|
||||
@ -67,20 +69,20 @@ func (t *Thread) singleStep() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func threadBlocked(t IThread) bool {
|
||||
func (t *Thread) Blocked() bool {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
pc := regs.PC()
|
||||
fn := t.BinInfo().goSymTable.PCToFunc(pc)
|
||||
fn := t.BinInfo().PCToFunc(pc)
|
||||
if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Thread) saveRegisters() (Registers, error) {
|
||||
func (t *Thread) saveRegisters() (proc.Registers, error) {
|
||||
var err error
|
||||
t.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(t.ID, &t.os.registers) })
|
||||
if err != nil {
|
||||
@ -94,7 +96,7 @@ func (t *Thread) restoreRegisters() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) writeMemory(addr uintptr, data []byte) (written int, err error) {
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (written int, err error) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package proc
|
||||
package native
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
|
||||
sys "golang.org/x/sys/windows"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// WaitStatus is a synonym for the platform-specific WaitStatus
|
||||
@ -57,7 +60,7 @@ func (t *Thread) singleStep() error {
|
||||
}
|
||||
if tid == 0 {
|
||||
t.dbp.postExit()
|
||||
return ProcessExitedError{Pid: t.dbp.pid, Status: exitCode}
|
||||
return proc.ProcessExitedError{Pid: t.dbp.pid, Status: exitCode}
|
||||
}
|
||||
|
||||
if t.dbp.os.breakThread == t.ID {
|
||||
@ -103,7 +106,7 @@ func (t *Thread) resume() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func threadBlocked(t IThread) bool {
|
||||
func (t *Thread) Blocked() bool {
|
||||
// TODO: Probably incorrect - what are the runtime functions that
|
||||
// indicate blocking on Windows?
|
||||
regs, err := t.Registers(false)
|
||||
@ -111,7 +114,7 @@ func threadBlocked(t IThread) bool {
|
||||
return false
|
||||
}
|
||||
pc := regs.PC()
|
||||
fn := t.BinInfo().goSymTable.PCToFunc(pc)
|
||||
fn := t.BinInfo().PCToFunc(pc)
|
||||
if fn == nil {
|
||||
return false
|
||||
}
|
||||
@ -129,7 +132,7 @@ func (t *Thread) stopped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
|
||||
var count uintptr
|
||||
err := _WriteProcessMemory(t.dbp.os.hProcess, addr, &data[0], uintptr(len(data)), &count)
|
||||
if err != nil {
|
||||
@ -138,6 +141,8 @@ func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
var ErrShortRead = errors.New("short read")
|
||||
|
||||
func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
|
||||
if len(buf) == 0 {
|
||||
return 0, nil
|
@ -1,6 +1,6 @@
|
||||
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||
|
||||
package proc
|
||||
package native
|
||||
|
||||
import (
|
||||
"syscall"
|
527
pkg/proc/proc.go
527
pkg/proc/proc.go
@ -8,48 +8,13 @@ import (
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/debug/dwarf"
|
||||
)
|
||||
|
||||
// Process represents all of the information the debugger
|
||||
// is holding onto regarding the process we are debugging.
|
||||
type Process struct {
|
||||
bi BinaryInfo
|
||||
pid int // Process Pid
|
||||
Process *os.Process // Pointer to process struct for the actual process we are debugging
|
||||
|
||||
// Breakpoint table, holds information on breakpoints.
|
||||
// Maps instruction address to Breakpoint struct.
|
||||
breakpoints map[uint64]*Breakpoint
|
||||
|
||||
// List of threads mapped as such: pid -> *Thread
|
||||
threads map[int]*Thread
|
||||
|
||||
// Active thread
|
||||
currentThread *Thread
|
||||
|
||||
// Goroutine that will be used by default to set breakpoint, eval variables, etc...
|
||||
// Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
|
||||
selectedGoroutine *G
|
||||
|
||||
allGCache []*G
|
||||
os *OSProcessDetails
|
||||
breakpointIDCounter int
|
||||
internalBreakpointIDCounter int
|
||||
firstStart bool
|
||||
halt bool
|
||||
exited bool
|
||||
ptraceChan chan func()
|
||||
ptraceDoneChan chan interface{}
|
||||
}
|
||||
|
||||
type functionDebugInfo struct {
|
||||
lowpc, highpc uint64
|
||||
offset dwarf.Offset
|
||||
@ -57,25 +22,6 @@ type functionDebugInfo struct {
|
||||
|
||||
var NotExecutableErr = errors.New("not an executable file")
|
||||
|
||||
// New returns an initialized Process struct. Before returning,
|
||||
// it will also launch a goroutine in order to handle ptrace(2)
|
||||
// functions. For more information, see the documentation on
|
||||
// `handlePtraceFuncs`.
|
||||
func New(pid int) *Process {
|
||||
dbp := &Process{
|
||||
pid: pid,
|
||||
threads: make(map[int]*Thread),
|
||||
breakpoints: make(map[uint64]*Breakpoint),
|
||||
firstStart: true,
|
||||
os: new(OSProcessDetails),
|
||||
ptraceChan: make(chan func()),
|
||||
ptraceDoneChan: make(chan interface{}),
|
||||
bi: NewBinaryInfo(runtime.GOOS, runtime.GOARCH),
|
||||
}
|
||||
go dbp.handlePtraceFuncs()
|
||||
return dbp
|
||||
}
|
||||
|
||||
// ProcessExitedError indicates that the process has exited and contains both
|
||||
// process id and exit status.
|
||||
type ProcessExitedError struct {
|
||||
@ -87,115 +33,9 @@ func (pe ProcessExitedError) Error() string {
|
||||
return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status)
|
||||
}
|
||||
|
||||
func (dbp *Process) BinInfo() *BinaryInfo {
|
||||
return &dbp.bi
|
||||
}
|
||||
|
||||
// Detach from the process being debugged, optionally killing it.
|
||||
func (dbp *Process) Detach(kill bool) (err error) {
|
||||
if dbp.exited {
|
||||
return nil
|
||||
}
|
||||
if dbp.Running() {
|
||||
if err = dbp.Halt(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !kill {
|
||||
// Clean up any breakpoints we've set.
|
||||
for _, bp := range dbp.breakpoints {
|
||||
if bp != nil {
|
||||
_, err := dbp.ClearBreakpoint(bp.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dbp.execPtraceFunc(func() {
|
||||
err = dbp.detach(kill)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if kill {
|
||||
err = killProcess(dbp.pid)
|
||||
}
|
||||
})
|
||||
dbp.bi.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// Exited returns whether the debugged
|
||||
// process has exited.
|
||||
func (dbp *Process) Exited() bool {
|
||||
return dbp.exited
|
||||
}
|
||||
|
||||
// Running returns whether the debugged
|
||||
// process is currently executing.
|
||||
func (dbp *Process) Running() bool {
|
||||
for _, th := range dbp.threads {
|
||||
if th.running {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (dbp *Process) Pid() int {
|
||||
return dbp.pid
|
||||
}
|
||||
|
||||
func (dbp *Process) SelectedGoroutine() *G {
|
||||
return dbp.selectedGoroutine
|
||||
}
|
||||
|
||||
func (dbp *Process) ThreadList() []IThread {
|
||||
r := make([]IThread, 0, len(dbp.threads))
|
||||
for _, v := range dbp.threads {
|
||||
r = append(r, v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (dbp *Process) FindThread(threadID int) (IThread, bool) {
|
||||
th, ok := dbp.threads[threadID]
|
||||
return th, ok
|
||||
}
|
||||
|
||||
func (dbp *Process) CurrentThread() IThread {
|
||||
return dbp.currentThread
|
||||
}
|
||||
|
||||
func (dbp *Process) Breakpoints() map[uint64]*Breakpoint {
|
||||
return dbp.breakpoints
|
||||
}
|
||||
|
||||
// LoadInformation finds the executable and then uses it
|
||||
// to parse the following information:
|
||||
// * Dwarf .debug_frame section
|
||||
// * Dwarf .debug_line section
|
||||
// * Go symbol table.
|
||||
func (dbp *Process) LoadInformation(path string) error {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
path = findExecutable(path, dbp.pid)
|
||||
|
||||
wg.Add(1)
|
||||
go dbp.loadProcessInformation(&wg)
|
||||
dbp.bi.LoadBinaryInfo(path, &wg)
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) {
|
||||
return FindFileLocation(dbp.currentThread, dbp.breakpoints, &dbp.bi, fileName, lineno)
|
||||
}
|
||||
|
||||
// FindFileLocation returns the PC for a given file:line.
|
||||
// Assumes that `file` is normailzed to lower case and '/' on Windows.
|
||||
func FindFileLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fileName string, lineno int) (uint64, error) {
|
||||
func FindFileLocation(mem MemoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fileName string, lineno int) (uint64, error) {
|
||||
pc, fn, err := bi.goSymTable.LineToPC(fileName, lineno)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@ -206,17 +46,13 @@ func FindFileLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint,
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||
return FindFunctionLocation(dbp.currentThread, dbp.breakpoints, &dbp.bi, funcName, firstLine, lineOffset)
|
||||
}
|
||||
|
||||
// FindFunctionLocation finds address of a function's line
|
||||
// If firstLine == true is passed FindFunctionLocation will attempt to find the first line of the function
|
||||
// If lineOffset is passed FindFunctionLocation will return the address of that line
|
||||
// Pass lineOffset == 0 and firstLine == false if you want the address for the function's entry point
|
||||
// Note that setting breakpoints at that address will cause surprising behavior:
|
||||
// https://github.com/derekparker/delve/issues/170
|
||||
func FindFunctionLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||
func FindFunctionLocation(mem MemoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||
origfn := bi.goSymTable.LookupFunc(funcName)
|
||||
if origfn == nil {
|
||||
return 0, fmt.Errorf("Could not find function %s\n", funcName)
|
||||
@ -233,93 +69,6 @@ func FindFunctionLocation(mem memoryReadWriter, breakpoints map[uint64]*Breakpoi
|
||||
return origfn.Entry, nil
|
||||
}
|
||||
|
||||
// CurrentLocation returns the location of the current thread.
|
||||
func (dbp *Process) CurrentLocation() (*Location, error) {
|
||||
return dbp.currentThread.Location()
|
||||
}
|
||||
|
||||
// RequestManualStop sets the `halt` flag and
|
||||
// sends SIGSTOP to all threads.
|
||||
func (dbp *Process) RequestManualStop() error {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
dbp.halt = true
|
||||
return dbp.requestManualStop()
|
||||
}
|
||||
|
||||
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
|
||||
// break point table. Setting a break point must be thread specific due to
|
||||
// ptrace actions needing the thread to be in a signal-delivery-stop.
|
||||
func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
|
||||
tid := dbp.currentThread.ID
|
||||
|
||||
if bp, ok := dbp.FindBreakpoint(addr); ok {
|
||||
return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
|
||||
}
|
||||
|
||||
f, l, fn := dbp.bi.goSymTable.PCToLine(uint64(addr))
|
||||
if fn == nil {
|
||||
return nil, InvalidAddressError{address: addr}
|
||||
}
|
||||
|
||||
newBreakpoint := &Breakpoint{
|
||||
FunctionName: fn.Name,
|
||||
File: f,
|
||||
Line: l,
|
||||
Addr: addr,
|
||||
Kind: kind,
|
||||
Cond: cond,
|
||||
HitCount: map[int]uint64{},
|
||||
}
|
||||
|
||||
if kind != UserBreakpoint {
|
||||
dbp.internalBreakpointIDCounter++
|
||||
newBreakpoint.ID = dbp.internalBreakpointIDCounter
|
||||
} else {
|
||||
dbp.breakpointIDCounter++
|
||||
newBreakpoint.ID = dbp.breakpointIDCounter
|
||||
}
|
||||
|
||||
thread := dbp.threads[tid]
|
||||
originalData := make([]byte, dbp.bi.arch.BreakpointSize())
|
||||
_, err := thread.ReadMemory(originalData, uintptr(addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := dbp.writeSoftwareBreakpoint(thread, addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newBreakpoint.OriginalData = originalData
|
||||
dbp.breakpoints[addr] = newBreakpoint
|
||||
|
||||
return newBreakpoint, nil
|
||||
}
|
||||
|
||||
// ClearBreakpoint clears the breakpoint at addr.
|
||||
func (dbp *Process) ClearBreakpoint(addr uint64) (*Breakpoint, error) {
|
||||
if dbp.exited {
|
||||
return nil, &ProcessExitedError{}
|
||||
}
|
||||
bp, ok := dbp.FindBreakpoint(addr)
|
||||
if !ok {
|
||||
return nil, NoBreakpointError{addr: addr}
|
||||
}
|
||||
|
||||
if _, err := dbp.currentThread.ClearBreakpoint(bp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(dbp.breakpoints, addr)
|
||||
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
// Status returns the status of the current main thread context.
|
||||
func (dbp *Process) Status() *WaitStatus {
|
||||
return dbp.currentThread.Status
|
||||
}
|
||||
|
||||
// Next continues execution until the next source line.
|
||||
func Next(dbp Continuable) (err error) {
|
||||
if dbp.Exited() {
|
||||
@ -343,33 +92,6 @@ func Next(dbp Continuable) (err error) {
|
||||
return Continue(dbp)
|
||||
}
|
||||
|
||||
func (dbp *Process) ContinueOnce() (IThread, error) {
|
||||
if dbp.exited {
|
||||
return nil, &ProcessExitedError{}
|
||||
}
|
||||
|
||||
if err := dbp.resume(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.allGCache = nil
|
||||
for _, th := range dbp.threads {
|
||||
th.clearBreakpointState()
|
||||
}
|
||||
|
||||
trapthread, err := dbp.trapWait(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := dbp.Halt(); err != nil {
|
||||
return nil, dbp.exitGuard(err)
|
||||
}
|
||||
if err := dbp.setCurrentBreakpoints(trapthread); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return trapthread, err
|
||||
}
|
||||
|
||||
// Continuable is the subinterface of target.Interface used to implement
|
||||
// Continue/Next/etc.
|
||||
type Continuable interface {
|
||||
@ -442,7 +164,7 @@ func Continue(dbp Continuable) error {
|
||||
// here we either set a breakpoint into the destination of the CALL
|
||||
// instruction or we determined that the called function is hidden,
|
||||
// either way we need to resume execution
|
||||
if err = setStepIntoBreakpoint(dbp, text, sameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
|
||||
if err = setStepIntoBreakpoint(dbp, text, SameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@ -528,8 +250,9 @@ func Step(dbp Continuable) (err error) {
|
||||
return Continue(dbp)
|
||||
}
|
||||
|
||||
// Returns an expression that evaluates to true when the current goroutine is g
|
||||
func sameGoroutineCondition(g *G) ast.Expr {
|
||||
// SameGoroutineCondition returns an expression that evaluates to true when
|
||||
// the current goroutine is g.
|
||||
func SameGoroutineCondition(g *G) ast.Expr {
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
@ -546,39 +269,12 @@ func sameGoroutineCondition(g *G) ast.Expr {
|
||||
}
|
||||
}
|
||||
|
||||
// StepInstruction will continue the current thread for exactly
|
||||
// one instruction. This method affects only the thread
|
||||
// asssociated with the selected goroutine. All other
|
||||
// threads will remain stopped.
|
||||
func (dbp *Process) StepInstruction() (err error) {
|
||||
if dbp.selectedGoroutine == nil {
|
||||
return errors.New("cannot single step: no selected goroutine")
|
||||
}
|
||||
if dbp.selectedGoroutine.thread == nil {
|
||||
// Step called on parked goroutine
|
||||
if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, NextBreakpoint, sameGoroutineCondition(dbp.selectedGoroutine)); err != nil {
|
||||
return err
|
||||
}
|
||||
return Continue(dbp)
|
||||
}
|
||||
dbp.allGCache = nil
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
dbp.selectedGoroutine.thread.(*Thread).clearBreakpointState()
|
||||
err = dbp.selectedGoroutine.thread.(*Thread).StepInstruction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dbp.selectedGoroutine.thread.(*Thread).SetCurrentBreakpoint()
|
||||
}
|
||||
|
||||
// StepOut will continue until the current goroutine exits the
|
||||
// function currently being executed or a deferred function is executed
|
||||
func StepOut(dbp Continuable) error {
|
||||
selg := dbp.SelectedGoroutine()
|
||||
curthread := dbp.CurrentThread()
|
||||
cond := sameGoroutineCondition(selg)
|
||||
cond := SameGoroutineCondition(selg)
|
||||
|
||||
topframe, err := topframe(selg, curthread)
|
||||
if err != nil {
|
||||
@ -631,50 +327,12 @@ func StepOut(dbp Continuable) error {
|
||||
return Continue(dbp)
|
||||
}
|
||||
|
||||
// SwitchThread changes from current thread to the thread specified by `tid`.
|
||||
func (dbp *Process) SwitchThread(tid int) error {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
if th, ok := dbp.threads[tid]; ok {
|
||||
dbp.currentThread = th
|
||||
dbp.selectedGoroutine, _ = GetG(dbp.currentThread)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("thread %d does not exist", tid)
|
||||
}
|
||||
|
||||
// SwitchGoroutine changes from current thread to the thread
|
||||
// running the specified goroutine.
|
||||
func (dbp *Process) SwitchGoroutine(gid int) error {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
g, err := FindGoroutine(dbp, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if g == nil {
|
||||
// user specified -1 and selectedGoroutine is nil
|
||||
return nil
|
||||
}
|
||||
if g.thread != nil {
|
||||
return dbp.SwitchThread(g.thread.ThreadID())
|
||||
}
|
||||
dbp.selectedGoroutine = g
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the argument of GoroutinesInfo implements AllGCache GoroutinesInfo
|
||||
// will use the pointer returned by AllGCache as a cache.
|
||||
type AllGCache interface {
|
||||
AllGCache() *[]*G
|
||||
}
|
||||
|
||||
func (dbp *Process) AllGCache() *[]*G {
|
||||
return &dbp.allGCache
|
||||
}
|
||||
|
||||
// GoroutinesInfo returns an array of G structures representing the information
|
||||
// Delve cares about from the internal runtime G structure.
|
||||
func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
|
||||
@ -695,7 +353,7 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
|
||||
|
||||
threads := dbp.ThreadList()
|
||||
for _, th := range threads {
|
||||
if threadBlocked(th) {
|
||||
if th.Blocked() {
|
||||
continue
|
||||
}
|
||||
g, _ := GetG(th)
|
||||
@ -724,12 +382,12 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
faddr := make([]byte, dbp.BinInfo().arch.PtrSize())
|
||||
faddr := make([]byte, dbp.BinInfo().Arch.PtrSize())
|
||||
_, err = dbp.CurrentThread().ReadMemory(faddr, uintptr(allgentryaddr))
|
||||
allgptr := binary.LittleEndian.Uint64(faddr)
|
||||
|
||||
for i := uint64(0); i < allglen; i++ {
|
||||
gvar, err := newGVariable(dbp.CurrentThread(), uintptr(allgptr+(i*uint64(dbp.BinInfo().arch.PtrSize()))), true)
|
||||
gvar, err := newGVariable(dbp.CurrentThread(), uintptr(allgptr+(i*uint64(dbp.BinInfo().Arch.PtrSize()))), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -742,7 +400,7 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.thread = thread
|
||||
g.Thread = thread
|
||||
// Prefer actual thread location information.
|
||||
g.CurrentLoc = *loc
|
||||
}
|
||||
@ -758,153 +416,8 @@ func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) {
|
||||
return allg, nil
|
||||
}
|
||||
|
||||
func (g *G) Thread() IThread {
|
||||
return g.thread
|
||||
}
|
||||
|
||||
// Halt stops all threads.
|
||||
func (dbp *Process) Halt() (err error) {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
for _, th := range dbp.threads {
|
||||
if err := th.Halt(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Registers obtains register values from the
|
||||
// "current" thread of the traced process.
|
||||
func (dbp *Process) Registers() (Registers, error) {
|
||||
return dbp.currentThread.Registers(false)
|
||||
}
|
||||
|
||||
// PC returns the PC of the current thread.
|
||||
func (dbp *Process) PC() (uint64, error) {
|
||||
return dbp.currentThread.PC()
|
||||
}
|
||||
|
||||
// CurrentBreakpoint returns the breakpoint the current thread
|
||||
// is stopped at.
|
||||
func (dbp *Process) CurrentBreakpoint() *Breakpoint {
|
||||
return dbp.currentThread.CurrentBreakpoint
|
||||
}
|
||||
|
||||
// FindBreakpointByID finds the breakpoint for the given ID.
|
||||
func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
|
||||
for _, bp := range dbp.breakpoints {
|
||||
if bp.ID == id {
|
||||
return bp, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// FindBreakpoint finds the breakpoint for the given pc.
|
||||
func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
|
||||
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
|
||||
if bp, ok := dbp.breakpoints[pc-uint64(dbp.bi.arch.BreakpointSize())]; ok {
|
||||
return bp, true
|
||||
}
|
||||
// Directly use addr to lookup breakpoint.
|
||||
if bp, ok := dbp.breakpoints[pc]; ok {
|
||||
return bp, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Returns a new Process struct.
|
||||
func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, error) {
|
||||
if attach {
|
||||
var err error
|
||||
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, _, err = dbp.wait(dbp.pid, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
proc, err := os.FindProcess(dbp.pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.Process = proc
|
||||
err = dbp.LoadInformation(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := dbp.updateThreadList(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scope := &EvalScope{0, 0, dbp.currentThread, nil, dbp.BinInfo()}
|
||||
ver, isextld, err := scope.getGoInformation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.bi.arch.SetGStructOffset(ver, isextld)
|
||||
// selectedGoroutine can not be set correctly by the call to updateThreadList
|
||||
// because without calling SetGStructOffset we can not read the G struct of currentThread
|
||||
// but without calling updateThreadList we can not examine memory to determine
|
||||
// the offset of g struct inside TLS
|
||||
dbp.selectedGoroutine, _ = GetG(dbp.currentThread)
|
||||
|
||||
panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0)
|
||||
if err == nil {
|
||||
bp, err := dbp.SetBreakpoint(panicpc, UserBreakpoint, nil)
|
||||
if err == nil {
|
||||
bp.Name = "unrecovered-panic"
|
||||
bp.ID = -1
|
||||
dbp.breakpointIDCounter--
|
||||
}
|
||||
}
|
||||
|
||||
return dbp, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) ClearInternalBreakpoints() error {
|
||||
for _, bp := range dbp.breakpoints {
|
||||
if !bp.Internal() {
|
||||
continue
|
||||
}
|
||||
if _, err := dbp.ClearBreakpoint(bp.Addr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := range dbp.threads {
|
||||
if dbp.threads[i].CurrentBreakpoint != nil && dbp.threads[i].CurrentBreakpoint.Internal() {
|
||||
dbp.threads[i].CurrentBreakpoint = nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) handlePtraceFuncs() {
|
||||
// We must ensure here that we are running on the same thread during
|
||||
// while invoking the ptrace(2) syscall. This is due to the fact that ptrace(2) expects
|
||||
// all commands after PTRACE_ATTACH to come from the same thread.
|
||||
runtime.LockOSThread()
|
||||
|
||||
for fn := range dbp.ptraceChan {
|
||||
fn()
|
||||
dbp.ptraceDoneChan <- nil
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) execPtraceFunc(fn func()) {
|
||||
dbp.ptraceChan <- fn
|
||||
<-dbp.ptraceDoneChan
|
||||
}
|
||||
|
||||
func (scope *EvalScope) getGoInformation() (ver GoVersion, isextld bool, err error) {
|
||||
func GetGoInformation(p Continuable) (ver GoVersion, isextld bool, err error) {
|
||||
scope := &EvalScope{0, 0, p.CurrentThread(), nil, p.BinInfo()}
|
||||
vv, err := scope.packageVarAddr("runtime.buildVersion")
|
||||
if err != nil {
|
||||
return ver, false, fmt.Errorf("Could not determine version number: %v", err)
|
||||
@ -921,7 +434,7 @@ func (scope *EvalScope) getGoInformation() (ver GoVersion, isextld bool, err err
|
||||
return
|
||||
}
|
||||
|
||||
rdr := scope.bi.DwarfReader()
|
||||
rdr := scope.BinInfo.DwarfReader()
|
||||
rdr.Seek(0)
|
||||
for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() {
|
||||
if err != nil {
|
||||
@ -976,11 +489,11 @@ func ConvertEvalScope(dbp EvalScopeConvertible, gid, frame int) (*EvalScope, err
|
||||
return ThreadScope(ct)
|
||||
}
|
||||
|
||||
var thread memoryReadWriter
|
||||
if g.thread == nil {
|
||||
var thread MemoryReadWriter
|
||||
if g.Thread == nil {
|
||||
thread = ct
|
||||
} else {
|
||||
thread = g.thread
|
||||
thread = g.Thread
|
||||
}
|
||||
|
||||
locs, err := g.Stacktrace(frame)
|
||||
@ -1001,9 +514,3 @@ func ConvertEvalScope(dbp EvalScopeConvertible, gid, frame int) (*EvalScope, err
|
||||
func FrameToScope(p EvalScopeConvertible, frame Stackframe) *EvalScope {
|
||||
return &EvalScope{frame.Current.PC, frame.CFA, p.CurrentThread(), nil, p.BinInfo()}
|
||||
}
|
||||
|
||||
func (dbp *Process) postExit() {
|
||||
dbp.exited = true
|
||||
close(dbp.ptraceChan)
|
||||
close(dbp.ptraceDoneChan)
|
||||
}
|
||||
|
14
pkg/proc/proc_general_test.go
Normal file
14
pkg/proc/proc_general_test.go
Normal file
@ -0,0 +1,14 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIssue554(t *testing.T) {
|
||||
// unsigned integer overflow in proc.(*memCache).contains was
|
||||
// causing it to always return true for address 0xffffffffffffffff
|
||||
mem := memCache{0x20, make([]byte, 100), nil}
|
||||
if mem.contains(0xffffffffffffffff, 40) {
|
||||
t.Fatalf("should be false")
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,16 @@
|
||||
// +build linux darwin
|
||||
|
||||
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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user