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