proc refactor: split out BinaryInfo implementation (#745)
* proc: refactor BinaryInfo part of proc.Process to own type The data structures and associated code used by proc.Process to implement target.BinaryInfo will also be useful to support a backend for examining core dumps, split this part of proc.Process to a different type. * proc: compile support for all executable formats unconditionally So far we only compiled in support for loading the executable format supported by the host operating system. Once support for core files is introduced it is however useful to support loading in all executable formats, there is no reason why it shouldn't be possible to examine a linux coredump on windows, or viceversa. * proc: bugfix: do not resume threads on detach if killing * Replace BinaryInfo interface with BinInfo() method returning proc.BinaryInfo
This commit is contained in:
parent
7b19fe9e69
commit
436a3c2149
@ -1,7 +1,5 @@
|
||||
package proc
|
||||
|
||||
import "runtime"
|
||||
|
||||
// Arch defines an interface for representing a
|
||||
// CPU architecture.
|
||||
type Arch interface {
|
||||
@ -19,11 +17,12 @@ type AMD64 struct {
|
||||
breakInstructionLen int
|
||||
gStructOffset uint64
|
||||
hardwareBreakpointUsage []bool
|
||||
goos string
|
||||
}
|
||||
|
||||
// AMD64Arch returns an initialized AMD64
|
||||
// struct.
|
||||
func AMD64Arch() *AMD64 {
|
||||
func AMD64Arch(goos string) *AMD64 {
|
||||
var breakInstr = []byte{0xCC}
|
||||
|
||||
return &AMD64{
|
||||
@ -31,6 +30,7 @@ func AMD64Arch() *AMD64 {
|
||||
breakInstruction: breakInstr,
|
||||
breakInstructionLen: len(breakInstr),
|
||||
hardwareBreakpointUsage: make([]bool, 4),
|
||||
goos: goos,
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ func AMD64Arch() *AMD64 {
|
||||
// arch struct. The offset is dependent on the Go compiler Version
|
||||
// and whether or not the target program was externally linked.
|
||||
func (a *AMD64) SetGStructOffset(ver GoVersion, isextld bool) {
|
||||
switch runtime.GOOS {
|
||||
switch a.goos {
|
||||
case "darwin":
|
||||
a.gStructOffset = 0x8a0
|
||||
case "linux":
|
||||
|
||||
513
pkg/proc/bininfo.go
Normal file
513
pkg/proc/bininfo.go
Normal file
@ -0,0 +1,513 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"debug/pe"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
||||
|
||||
"golang.org/x/debug/dwarf"
|
||||
"golang.org/x/debug/elf"
|
||||
"golang.org/x/debug/macho"
|
||||
)
|
||||
|
||||
type BinaryInfo struct {
|
||||
lastModified time.Time // Time the executable of this process was last modified
|
||||
|
||||
goos string
|
||||
closer io.Closer
|
||||
|
||||
// Maps package names to package paths, needed to lookup types inside DWARF info
|
||||
packageMap map[string]string
|
||||
|
||||
arch Arch
|
||||
dwarf *dwarf.Data
|
||||
frameEntries frame.FrameDescriptionEntries
|
||||
lineInfo line.DebugLines
|
||||
goSymTable *gosym.Table
|
||||
types map[string]dwarf.Offset
|
||||
functions []functionDebugInfo
|
||||
|
||||
loadModuleDataOnce sync.Once
|
||||
moduleData []moduleData
|
||||
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
|
||||
}
|
||||
|
||||
var UnsupportedLinuxArchErr = errors.New("unsupported architecture - only linux/amd64 is supported")
|
||||
var UnsupportedWindowsArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
|
||||
var UnsupportedDarwinArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported")
|
||||
|
||||
func NewBinaryInfo(goos, goarch string) BinaryInfo {
|
||||
r := BinaryInfo{goos: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry)}
|
||||
|
||||
// TODO: find better way to determine proc arch (perhaps use executable file info)
|
||||
switch goarch {
|
||||
case "amd64":
|
||||
r.arch = AMD64Arch(goos)
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (bininfo *BinaryInfo) LoadBinaryInfo(path string, wg *sync.WaitGroup) error {
|
||||
fi, err := os.Stat(path)
|
||||
if err == nil {
|
||||
bininfo.lastModified = fi.ModTime()
|
||||
}
|
||||
|
||||
switch bininfo.goos {
|
||||
case "linux":
|
||||
return bininfo.LoadBinaryInfoElf(path, wg)
|
||||
case "windows":
|
||||
return bininfo.LoadBinaryInfoPE(path, wg)
|
||||
case "darwin":
|
||||
return bininfo.LoadBinaryInfoMacho(path, wg)
|
||||
}
|
||||
return errors.New("unsupported operating system")
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) LastModified() time.Time {
|
||||
return bi.lastModified
|
||||
}
|
||||
|
||||
// DwarfReader returns a reader for the dwarf data
|
||||
func (bi *BinaryInfo) DwarfReader() *reader.Reader {
|
||||
return reader.New(bi.dwarf)
|
||||
}
|
||||
|
||||
// Sources returns list of source files that comprise the debugged binary.
|
||||
func (bi *BinaryInfo) Sources() map[string]*gosym.Obj {
|
||||
return bi.goSymTable.Files
|
||||
}
|
||||
|
||||
// Funcs returns list of functions present in the debugged program.
|
||||
func (bi *BinaryInfo) Funcs() []gosym.Func {
|
||||
return bi.goSymTable.Funcs
|
||||
}
|
||||
|
||||
// Types returns list of types present in the debugged program.
|
||||
func (bi *BinaryInfo) Types() ([]string, error) {
|
||||
types := make([]string, 0, len(bi.types))
|
||||
for k := range bi.types {
|
||||
types = append(types, k)
|
||||
}
|
||||
return types, nil
|
||||
}
|
||||
|
||||
// PCToLine converts an instruction address to a file/line/function.
|
||||
func (bi *BinaryInfo) PCToLine(pc uint64) (string, int, *gosym.Func) {
|
||||
return bi.goSymTable.PCToLine(pc)
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) Close() error {
|
||||
return bi.closer.Close()
|
||||
}
|
||||
|
||||
// ELF ///////////////////////////////////////////////////////////////
|
||||
|
||||
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, wg *sync.WaitGroup) error {
|
||||
exe, err := os.OpenFile(path, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bi.closer = exe
|
||||
elfFile, err := elf.NewFile(exe)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elfFile.Machine != elf.EM_X86_64 {
|
||||
return UnsupportedLinuxArchErr
|
||||
}
|
||||
bi.dwarf, err = elfFile.DWARF()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Add(4)
|
||||
go bi.parseDebugFrameElf(elfFile, wg)
|
||||
go bi.obtainGoSymbolsElf(elfFile, wg)
|
||||
go bi.parseDebugLineInfoElf(elfFile, wg)
|
||||
go bi.loadDebugInfoMaps(wg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) parseDebugFrameElf(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameSec := exe.Section(".debug_frame")
|
||||
debugInfoSec := exe.Section(".debug_info")
|
||||
|
||||
if debugFrameSec != nil && debugInfoSec != nil {
|
||||
debugFrame, err := exe.Section(".debug_frame").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_frame section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dat, err := debugInfoSec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_info section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
bi.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
||||
} else {
|
||||
fmt.Println("could not find .debug_frame section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) obtainGoSymbolsElf(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var (
|
||||
symdat []byte
|
||||
pclndat []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if sec := exe.Section(".gosymtab"); sec != nil {
|
||||
symdat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gosymtab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if sec := exe.Section(".gopclntab"); sec != nil {
|
||||
pclndat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gopclntab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
fmt.Println("could not get initialize line table", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
bi.goSymTable = tab
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) parseDebugLineInfoElf(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if sec := exe.Section(".debug_line"); sec != nil {
|
||||
debugLine, err := exe.Section(".debug_line").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_line section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
bi.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
fmt.Println("could not find .debug_line section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// PE ////////////////////////////////////////////////////////////////
|
||||
|
||||
func (bi *BinaryInfo) LoadBinaryInfoPE(path string, wg *sync.WaitGroup) error {
|
||||
peFile, closer, err := openExecutablePathPE(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bi.closer = closer
|
||||
if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
|
||||
return UnsupportedWindowsArchErr
|
||||
}
|
||||
bi.dwarf, err = dwarfFromPE(peFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Add(4)
|
||||
go bi.parseDebugFramePE(peFile, wg)
|
||||
go bi.obtainGoSymbolsPE(peFile, wg)
|
||||
go bi.parseDebugLineInfoPE(peFile, wg)
|
||||
go bi.loadDebugInfoMaps(wg)
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Adapted from src/debug/pe/file.go: pe.(*File).DWARF()
|
||||
func dwarfFromPE(f *pe.File) (*dwarf.Data, error) {
|
||||
// There are many other DWARF sections, but these
|
||||
// are the ones the debug/dwarf package uses.
|
||||
// Don't bother loading others.
|
||||
var names = [...]string{"abbrev", "info", "line", "str"}
|
||||
var dat [len(names)][]byte
|
||||
for i, name := range names {
|
||||
name = ".debug_" + name
|
||||
s := f.Section(name)
|
||||
if s == nil {
|
||||
continue
|
||||
}
|
||||
b, err := s.Data()
|
||||
if err != nil && uint32(len(b)) < s.Size {
|
||||
return nil, err
|
||||
}
|
||||
if 0 < s.VirtualSize && s.VirtualSize < s.Size {
|
||||
b = b[:s.VirtualSize]
|
||||
}
|
||||
dat[i] = b
|
||||
}
|
||||
|
||||
abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3]
|
||||
return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str)
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) parseDebugFramePE(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameSec := exe.Section(".debug_frame")
|
||||
debugInfoSec := exe.Section(".debug_info")
|
||||
|
||||
if debugFrameSec != nil && debugInfoSec != nil {
|
||||
debugFrame, err := debugFrameSec.Data()
|
||||
if err != nil && uint32(len(debugFrame)) < debugFrameSec.Size {
|
||||
fmt.Println("could not get .debug_frame section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if 0 < debugFrameSec.VirtualSize && debugFrameSec.VirtualSize < debugFrameSec.Size {
|
||||
debugFrame = debugFrame[:debugFrameSec.VirtualSize]
|
||||
}
|
||||
dat, err := debugInfoSec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_info section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
bi.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
||||
} else {
|
||||
fmt.Println("could not find .debug_frame section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *BinaryInfo) obtainGoSymbolsPE(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
_, symdat, pclndat, err := pclnPE(exe)
|
||||
if err != nil {
|
||||
fmt.Println("could not get Go symbols", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, uint64(exe.Section(".text").Offset))
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
fmt.Println("could not get initialize line table", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dbp.goSymTable = tab
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
||||
for _, s := range f.Symbols {
|
||||
if s.Name != name {
|
||||
continue
|
||||
}
|
||||
if s.SectionNumber <= 0 {
|
||||
return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
|
||||
}
|
||||
if len(f.Sections) < int(s.SectionNumber) {
|
||||
return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no %s symbol found", name)
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
|
||||
ssym, err := findPESymbol(f, sname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
esym, err := findPESymbol(f, ename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ssym.SectionNumber != esym.SectionNumber {
|
||||
return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
|
||||
}
|
||||
sect := f.Sections[ssym.SectionNumber-1]
|
||||
data, err := sect.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data[ssym.Value:esym.Value], nil
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func pclnPE(exe *pe.File) (textStart uint64, symtab, pclntab []byte, err error) {
|
||||
var imageBase uint64
|
||||
switch oh := exe.OptionalHeader.(type) {
|
||||
case *pe.OptionalHeader32:
|
||||
imageBase = uint64(oh.ImageBase)
|
||||
case *pe.OptionalHeader64:
|
||||
imageBase = oh.ImageBase
|
||||
default:
|
||||
return 0, nil, nil, fmt.Errorf("pe file format not recognized")
|
||||
}
|
||||
if sect := exe.Section(".text"); sect != nil {
|
||||
textStart = imageBase + uint64(sect.VirtualAddress)
|
||||
}
|
||||
if pclntab, err = loadPETable(exe, "runtime.pclntab", "runtime.epclntab"); err != nil {
|
||||
// We didn't find the symbols, so look for the names used in 1.3 and earlier.
|
||||
// TODO: Remove code looking for the old symbols when we no longer care about 1.3.
|
||||
var err2 error
|
||||
if pclntab, err2 = loadPETable(exe, "pclntab", "epclntab"); err2 != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
}
|
||||
if symtab, err = loadPETable(exe, "runtime.symtab", "runtime.esymtab"); err != nil {
|
||||
// Same as above.
|
||||
var err2 error
|
||||
if symtab, err2 = loadPETable(exe, "symtab", "esymtab"); err2 != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
}
|
||||
return textStart, symtab, pclntab, nil
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) parseDebugLineInfoPE(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if sec := exe.Section(".debug_line"); sec != nil {
|
||||
debugLine, err := sec.Data()
|
||||
if err != nil && uint32(len(debugLine)) < sec.Size {
|
||||
fmt.Println("could not get .debug_line section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
|
||||
debugLine = debugLine[:sec.VirtualSize]
|
||||
}
|
||||
bi.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
fmt.Println("could not find .debug_line section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// MACH-O ////////////////////////////////////////////////////////////
|
||||
|
||||
func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, wg *sync.WaitGroup) error {
|
||||
exe, err := macho.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bi.closer = exe
|
||||
if exe.Cpu != macho.CpuAmd64 {
|
||||
return UnsupportedDarwinArchErr
|
||||
}
|
||||
bi.dwarf, err = exe.DWARF()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wg.Add(4)
|
||||
go bi.parseDebugFrameMacho(exe, wg)
|
||||
go bi.obtainGoSymbolsMacho(exe, wg)
|
||||
go bi.parseDebugLineInfoMacho(exe, wg)
|
||||
go bi.loadDebugInfoMaps(wg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) parseDebugFrameMacho(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameSec := exe.Section("__debug_frame")
|
||||
debugInfoSec := exe.Section("__debug_info")
|
||||
|
||||
if debugFrameSec != nil && debugInfoSec != nil {
|
||||
debugFrame, err := exe.Section("__debug_frame").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get __debug_frame section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dat, err := debugInfoSec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_info section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
bi.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
||||
} else {
|
||||
fmt.Println("could not find __debug_frame section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) obtainGoSymbolsMacho(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var (
|
||||
symdat []byte
|
||||
pclndat []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if sec := exe.Section("__gosymtab"); sec != nil {
|
||||
symdat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gosymtab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if sec := exe.Section("__gopclntab"); sec != nil {
|
||||
pclndat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gopclntab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, exe.Section("__text").Addr)
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
fmt.Println("could not get initialize line table", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
bi.goSymTable = tab
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) parseDebugLineInfoMacho(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if sec := exe.Section("__debug_line"); sec != nil {
|
||||
debugLine, err := exe.Section("__debug_line").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get __debug_line section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
bi.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
fmt.Println("could not find __debug_line section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@ -104,7 +104,7 @@ func (iae InvalidAddressError) Error() string {
|
||||
}
|
||||
|
||||
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
|
||||
_, err := thread.writeMemory(uintptr(addr), dbp.arch.BreakpointInstruction())
|
||||
_, err := thread.writeMemory(uintptr(addr), dbp.bi.arch.BreakpointInstruction())
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ func (thread *Thread) Disassemble(startPC, endPC uint64, currentGoroutine bool)
|
||||
mem[i] = bp.OriginalData[i]
|
||||
}
|
||||
}
|
||||
file, line, fn := thread.dbp.PCToLine(pc)
|
||||
file, line, fn := thread.dbp.bi.PCToLine(pc)
|
||||
loc := Location{PC: pc, File: file, Line: line, Fn: fn}
|
||||
inst, err := asmDecode(mem, pc)
|
||||
if err == nil {
|
||||
|
||||
@ -109,7 +109,7 @@ func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs
|
||||
return nil
|
||||
}
|
||||
|
||||
file, line, fn := thread.dbp.PCToLine(pc)
|
||||
file, line, fn := thread.dbp.bi.PCToLine(pc)
|
||||
if fn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -141,7 +141,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
|
||||
fnnode = p.X
|
||||
}
|
||||
|
||||
styp, err := scope.Thread.dbp.findTypeExpr(fnnode)
|
||||
styp, err := scope.Thread.dbp.bi.findTypeExpr(fnnode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -454,7 +454,7 @@ func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
|
||||
return v, nil
|
||||
}
|
||||
// if it's not a local variable then it could be a package variable w/o explicit package name
|
||||
_, _, fn := scope.Thread.dbp.PCToLine(scope.PC)
|
||||
_, _, fn := scope.Thread.dbp.bi.PCToLine(scope.PC)
|
||||
if fn != nil {
|
||||
if v, err = scope.packageVarAddr(fn.PackageName() + "." + node.Name); err == nil {
|
||||
v.Name = node.Name
|
||||
@ -492,7 +492,7 @@ func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, err
|
||||
if xv.Children[0].Addr == 0 {
|
||||
return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type))
|
||||
}
|
||||
typ, err := scope.Thread.dbp.findTypeExpr(node.Type)
|
||||
typ, err := scope.Thread.dbp.bi.findTypeExpr(node.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -637,7 +637,7 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) {
|
||||
xev.OnlyAddr = true
|
||||
|
||||
typename := "*" + xev.DwarfType.Common().Name
|
||||
rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.Thread.dbp.arch.PtrSize()), Name: typename}, Type: xev.DwarfType})
|
||||
rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.Thread.dbp.bi.arch.PtrSize()), Name: typename}, Type: xev.DwarfType})
|
||||
rv.Children = []Variable{*xev}
|
||||
rv.loaded = true
|
||||
|
||||
|
||||
@ -11,9 +11,9 @@ type moduleData struct {
|
||||
typemapVar *Variable
|
||||
}
|
||||
|
||||
func (dbp *Process) loadModuleData() (err error) {
|
||||
dbp.loadModuleDataOnce.Do(func() {
|
||||
scope := &EvalScope{Thread: dbp.currentThread, PC: 0, CFA: 0}
|
||||
func (bi *BinaryInfo) loadModuleData(thread *Thread) (err error) {
|
||||
bi.loadModuleDataOnce.Do(func() {
|
||||
scope, _ := thread.Scope()
|
||||
var md *Variable
|
||||
md, err = scope.packageVarAddr("runtime.firstmoduledata")
|
||||
if err != nil {
|
||||
@ -43,7 +43,7 @@ func (dbp *Process) loadModuleData() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
dbp.moduleData = append(dbp.moduleData, moduleData{uintptr(types), uintptr(etypes), typemapVar})
|
||||
bi.moduleData = append(bi.moduleData, moduleData{uintptr(types), uintptr(etypes), typemapVar})
|
||||
|
||||
md = nextVar.maybeDereference()
|
||||
if md.Unreadable != nil {
|
||||
@ -56,26 +56,27 @@ func (dbp *Process) loadModuleData() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (dbp *Process) resolveTypeOff(typeAddr uintptr, off uintptr) (*Variable, error) {
|
||||
func (bi *BinaryInfo) resolveTypeOff(typeAddr uintptr, off uintptr, thread *Thread) (*Variable, error) {
|
||||
var mem memoryReadWriter = thread
|
||||
// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
|
||||
if err := dbp.loadModuleData(); err != nil {
|
||||
if err := bi.loadModuleData(thread); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var md *moduleData
|
||||
for i := range dbp.moduleData {
|
||||
if typeAddr >= dbp.moduleData[i].types && typeAddr < dbp.moduleData[i].etypes {
|
||||
md = &dbp.moduleData[i]
|
||||
for i := range bi.moduleData {
|
||||
if typeAddr >= bi.moduleData[i].types && typeAddr < bi.moduleData[i].etypes {
|
||||
md = &bi.moduleData[i]
|
||||
}
|
||||
}
|
||||
|
||||
rtyp, err := dbp.findType("runtime._type")
|
||||
rtyp, err := bi.findType("runtime._type")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if md == nil {
|
||||
v, err := dbp.reflectOffsMapAccess(off)
|
||||
v, err := bi.reflectOffsMapAccess(off, thread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -84,28 +85,29 @@ func (dbp *Process) resolveTypeOff(typeAddr uintptr, off uintptr) (*Variable, er
|
||||
return v.newVariable(v.Name, uintptr(addr), rtyp), nil
|
||||
}
|
||||
|
||||
if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.currentThread)); t != nil {
|
||||
if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem)); t != nil {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
res := md.types + uintptr(off)
|
||||
|
||||
return dbp.currentThread.newVariable("", res, rtyp), nil
|
||||
return newVariable("", res, rtyp, thread.dbp, thread), nil
|
||||
}
|
||||
|
||||
func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (name, tag string, pkgpathoff int32, err error) {
|
||||
func (bi *BinaryInfo) resolveNameOff(typeAddr uintptr, off uintptr, thread *Thread) (name, tag string, pkgpathoff int32, err error) {
|
||||
var mem memoryReadWriter = thread
|
||||
// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
|
||||
if err = dbp.loadModuleData(); err != nil {
|
||||
if err = bi.loadModuleData(thread); err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
for _, md := range dbp.moduleData {
|
||||
for _, md := range bi.moduleData {
|
||||
if typeAddr >= md.types && typeAddr < md.etypes {
|
||||
return dbp.loadName(md.types + off)
|
||||
return bi.loadName(md.types+off, mem)
|
||||
}
|
||||
}
|
||||
|
||||
v, err := dbp.reflectOffsMapAccess(off)
|
||||
v, err := bi.reflectOffsMapAccess(off, thread)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
@ -115,11 +117,11 @@ func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (name, tag str
|
||||
return "", "", 0, resv.Unreadable
|
||||
}
|
||||
|
||||
return dbp.loadName(resv.Addr)
|
||||
return bi.loadName(resv.Addr, mem)
|
||||
}
|
||||
|
||||
func (dbp *Process) reflectOffsMapAccess(off uintptr) (*Variable, error) {
|
||||
scope := &EvalScope{Thread: dbp.currentThread, PC: 0, CFA: 0}
|
||||
func (bi *BinaryInfo) reflectOffsMapAccess(off uintptr, thread *Thread) (*Variable, error) {
|
||||
scope, _ := thread.Scope()
|
||||
reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -130,7 +132,7 @@ func (dbp *Process) reflectOffsMapAccess(off uintptr) (*Variable, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.currentThread))
|
||||
return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), thread))
|
||||
}
|
||||
|
||||
const (
|
||||
@ -140,9 +142,9 @@ const (
|
||||
nameflagHasPkg = 1 << 2
|
||||
)
|
||||
|
||||
func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32, err error) {
|
||||
func (bi *BinaryInfo) loadName(addr uintptr, mem memoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
|
||||
off := addr
|
||||
namedata, err := dbp.currentThread.readMemory(off, 3)
|
||||
namedata, err := mem.readMemory(off, 3)
|
||||
off += 3
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
@ -150,7 +152,7 @@ func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32,
|
||||
|
||||
namelen := uint16(namedata[1]<<8) | uint16(namedata[2])
|
||||
|
||||
rawstr, err := dbp.currentThread.readMemory(off, int(namelen))
|
||||
rawstr, err := mem.readMemory(off, int(namelen))
|
||||
off += uintptr(namelen)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
@ -159,14 +161,14 @@ func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32,
|
||||
name = string(rawstr)
|
||||
|
||||
if namedata[0]&nameflagHasTag != 0 {
|
||||
taglendata, err := dbp.currentThread.readMemory(off, 2)
|
||||
taglendata, err := mem.readMemory(off, 2)
|
||||
off += 2
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
taglen := uint16(taglendata[0]<<8) | uint16(taglendata[1])
|
||||
|
||||
rawstr, err := dbp.currentThread.readMemory(off, int(taglen))
|
||||
rawstr, err := mem.readMemory(off, int(taglen))
|
||||
off += uintptr(taglen)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
@ -176,7 +178,7 @@ func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32,
|
||||
}
|
||||
|
||||
if namedata[0]&nameflagHasPkg != 0 {
|
||||
pkgdata, err := dbp.currentThread.readMemory(off, 4)
|
||||
pkgdata, err := mem.readMemory(off, 4)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
110
pkg/proc/proc.go
110
pkg/proc/proc.go
@ -1,7 +1,6 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -14,20 +13,16 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
||||
"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
|
||||
lastModified time.Time // Time the executable of this process was last modified
|
||||
|
||||
// Breakpoint table, holds information on breakpoints.
|
||||
// Maps instruction address to Breakpoint struct.
|
||||
@ -43,16 +38,8 @@ type Process struct {
|
||||
// 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
|
||||
|
||||
// Maps package names to package paths, needed to lookup types inside DWARF info
|
||||
packageMap map[string]string
|
||||
|
||||
allGCache []*G
|
||||
dwarf *dwarf.Data
|
||||
goSymTable *gosym.Table
|
||||
frameEntries frame.FrameDescriptionEntries
|
||||
lineInfo line.DebugLines
|
||||
os *OSProcessDetails
|
||||
arch Arch
|
||||
breakpointIDCounter int
|
||||
internalBreakpointIDCounter int
|
||||
firstStart bool
|
||||
@ -60,12 +47,6 @@ type Process struct {
|
||||
exited bool
|
||||
ptraceChan chan func()
|
||||
ptraceDoneChan chan interface{}
|
||||
types map[string]dwarf.Offset
|
||||
functions []functionDebugInfo
|
||||
|
||||
loadModuleDataOnce sync.Once
|
||||
moduleData []moduleData
|
||||
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
|
||||
}
|
||||
|
||||
type functionDebugInfo struct {
|
||||
@ -88,12 +69,7 @@ func New(pid int) *Process {
|
||||
os: new(OSProcessDetails),
|
||||
ptraceChan: make(chan func()),
|
||||
ptraceDoneChan: make(chan interface{}),
|
||||
nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry),
|
||||
}
|
||||
// TODO: find better way to determine proc arch (perhaps use executable file info)
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
dbp.arch = AMD64Arch()
|
||||
bi: NewBinaryInfo(runtime.GOOS, runtime.GOARCH),
|
||||
}
|
||||
go dbp.handlePtraceFuncs()
|
||||
return dbp
|
||||
@ -110,6 +86,10 @@ func (pe ProcessExitedError) Error() string {
|
||||
return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status)
|
||||
}
|
||||
|
||||
func (dbp *Process) BinInfo() *BinaryInfo {
|
||||
return &dbp.bi
|
||||
}
|
||||
|
||||
// Detach from the process being debugged, optionally killing it.
|
||||
func (dbp *Process) Detach(kill bool) (err error) {
|
||||
if dbp.exited {
|
||||
@ -132,7 +112,7 @@ func (dbp *Process) Detach(kill bool) (err error) {
|
||||
}
|
||||
}
|
||||
dbp.execPtraceFunc(func() {
|
||||
err = dbp.detach()
|
||||
err = dbp.detach(kill)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -140,6 +120,7 @@ func (dbp *Process) Detach(kill bool) (err error) {
|
||||
err = killProcess(dbp.pid)
|
||||
}
|
||||
})
|
||||
dbp.bi.Close()
|
||||
return
|
||||
}
|
||||
|
||||
@ -160,10 +141,6 @@ func (dbp *Process) Running() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (dbp *Process) LastModified() time.Time {
|
||||
return dbp.lastModified
|
||||
}
|
||||
|
||||
func (dbp *Process) Pid() int {
|
||||
return dbp.pid
|
||||
}
|
||||
@ -192,21 +169,11 @@ func (dbp *Process) Breakpoints() map[uint64]*Breakpoint {
|
||||
func (dbp *Process) LoadInformation(path string) error {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
exe, path, err := dbp.findExecutable(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fi, err := os.Stat(path)
|
||||
if err == nil {
|
||||
dbp.lastModified = fi.ModTime()
|
||||
}
|
||||
path = findExecutable(path, dbp.pid)
|
||||
|
||||
wg.Add(5)
|
||||
wg.Add(1)
|
||||
go dbp.loadProcessInformation(&wg)
|
||||
go dbp.parseDebugFrame(exe, &wg)
|
||||
go dbp.obtainGoSymbols(exe, &wg)
|
||||
go dbp.parseDebugLineInfo(exe, &wg)
|
||||
go dbp.loadDebugInfoMaps(&wg)
|
||||
dbp.bi.LoadBinaryInfo(path, &wg)
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
@ -215,7 +182,7 @@ func (dbp *Process) LoadInformation(path string) error {
|
||||
// FindFileLocation returns the PC for a given file:line.
|
||||
// Assumes that `file` is normailzed to lower case and '/' on Windows.
|
||||
func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) {
|
||||
pc, fn, err := dbp.goSymTable.LineToPC(fileName, lineno)
|
||||
pc, fn, err := dbp.bi.goSymTable.LineToPC(fileName, lineno)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -232,7 +199,7 @@ func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error
|
||||
// Note that setting breakpoints at that address will cause surprising behavior:
|
||||
// https://github.com/derekparker/delve/issues/170
|
||||
func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||
origfn := dbp.goSymTable.LookupFunc(funcName)
|
||||
origfn := dbp.bi.goSymTable.LookupFunc(funcName)
|
||||
if origfn == nil {
|
||||
return 0, fmt.Errorf("Could not find function %s\n", funcName)
|
||||
}
|
||||
@ -240,8 +207,8 @@ func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOf
|
||||
if firstLine {
|
||||
return dbp.FirstPCAfterPrologue(origfn, false)
|
||||
} else if lineOffset > 0 {
|
||||
filename, lineno, _ := dbp.goSymTable.PCToLine(origfn.Entry)
|
||||
breakAddr, _, err := dbp.goSymTable.LineToPC(filename, lineno+lineOffset)
|
||||
filename, lineno, _ := dbp.bi.goSymTable.PCToLine(origfn.Entry)
|
||||
breakAddr, _, err := dbp.bi.goSymTable.LineToPC(filename, lineno+lineOffset)
|
||||
return breakAddr, err
|
||||
}
|
||||
|
||||
@ -273,7 +240,7 @@ func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Exp
|
||||
return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
|
||||
}
|
||||
|
||||
f, l, fn := dbp.goSymTable.PCToLine(uint64(addr))
|
||||
f, l, fn := dbp.bi.goSymTable.PCToLine(uint64(addr))
|
||||
if fn == nil {
|
||||
return nil, InvalidAddressError{address: addr}
|
||||
}
|
||||
@ -297,7 +264,7 @@ func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Exp
|
||||
}
|
||||
|
||||
thread := dbp.threads[tid]
|
||||
originalData, err := thread.readMemory(uintptr(addr), dbp.arch.BreakpointSize())
|
||||
originalData, err := thread.readMemory(uintptr(addr), dbp.bi.arch.BreakpointSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -572,7 +539,7 @@ func (dbp *Process) StepOut() error {
|
||||
if dbp.selectedGoroutine != nil {
|
||||
deferPCEntry := dbp.selectedGoroutine.DeferPC()
|
||||
if deferPCEntry != 0 {
|
||||
_, _, deferfn := dbp.goSymTable.PCToLine(deferPCEntry)
|
||||
_, _, deferfn := dbp.bi.goSymTable.PCToLine(deferPCEntry)
|
||||
deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -658,7 +625,7 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) {
|
||||
var (
|
||||
threadg = map[int]*Thread{}
|
||||
allg []*G
|
||||
rdr = dbp.DwarfReader()
|
||||
rdr = dbp.bi.DwarfReader()
|
||||
)
|
||||
|
||||
for i := range dbp.threads {
|
||||
@ -690,11 +657,11 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
faddr, err := dbp.currentThread.readMemory(uintptr(allgentryaddr), dbp.arch.PtrSize())
|
||||
faddr, err := dbp.currentThread.readMemory(uintptr(allgentryaddr), dbp.bi.arch.PtrSize())
|
||||
allgptr := binary.LittleEndian.Uint64(faddr)
|
||||
|
||||
for i := uint64(0); i < allglen; i++ {
|
||||
gvar, err := dbp.currentThread.newGVariable(uintptr(allgptr+(i*uint64(dbp.arch.PtrSize()))), true)
|
||||
gvar, err := dbp.currentThread.newGVariable(uintptr(allgptr+(i*uint64(dbp.bi.arch.PtrSize()))), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -753,35 +720,6 @@ func (dbp *Process) CurrentBreakpoint() *Breakpoint {
|
||||
return dbp.currentThread.CurrentBreakpoint
|
||||
}
|
||||
|
||||
// DwarfReader returns a reader for the dwarf data
|
||||
func (dbp *Process) DwarfReader() *reader.Reader {
|
||||
return reader.New(dbp.dwarf)
|
||||
}
|
||||
|
||||
// Sources returns list of source files that comprise the debugged binary.
|
||||
func (dbp *Process) Sources() map[string]*gosym.Obj {
|
||||
return dbp.goSymTable.Files
|
||||
}
|
||||
|
||||
// Funcs returns list of functions present in the debugged program.
|
||||
func (dbp *Process) Funcs() []gosym.Func {
|
||||
return dbp.goSymTable.Funcs
|
||||
}
|
||||
|
||||
// Types returns list of types present in the debugged program.
|
||||
func (dbp *Process) Types() ([]string, error) {
|
||||
types := make([]string, 0, len(dbp.types))
|
||||
for k := range dbp.types {
|
||||
types = append(types, k)
|
||||
}
|
||||
return types, nil
|
||||
}
|
||||
|
||||
// PCToLine converts an instruction address to a file/line/function.
|
||||
func (dbp *Process) PCToLine(pc uint64) (string, int, *gosym.Func) {
|
||||
return dbp.goSymTable.PCToLine(pc)
|
||||
}
|
||||
|
||||
// FindBreakpointByID finds the breakpoint for the given ID.
|
||||
func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
|
||||
for _, bp := range dbp.breakpoints {
|
||||
@ -795,7 +733,7 @@ func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
|
||||
// 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.arch.BreakpointSize())]; ok {
|
||||
if bp, ok := dbp.breakpoints[pc-uint64(dbp.bi.arch.BreakpointSize())]; ok {
|
||||
return bp, true
|
||||
}
|
||||
// Directly use addr to lookup breakpoint.
|
||||
@ -839,7 +777,7 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.arch.SetGStructOffset(ver, isextld)
|
||||
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
|
||||
@ -910,7 +848,7 @@ func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error)
|
||||
return
|
||||
}
|
||||
|
||||
rdr := dbp.DwarfReader()
|
||||
rdr := dbp.bi.DwarfReader()
|
||||
rdr.Seek(0)
|
||||
for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() {
|
||||
if err != nil {
|
||||
|
||||
@ -6,7 +6,6 @@ package proc
|
||||
// #include <stdlib.h>
|
||||
import "C"
|
||||
import (
|
||||
"debug/gosym"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@ -15,10 +14,6 @@ import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/debug/macho"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
@ -259,99 +254,11 @@ func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameSec := exe.Section("__debug_frame")
|
||||
debugInfoSec := exe.Section("__debug_info")
|
||||
|
||||
if debugFrameSec != nil && debugInfoSec != nil {
|
||||
debugFrame, err := exe.Section("__debug_frame").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get __debug_frame section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dat, err := debugInfoSec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_info section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
||||
} else {
|
||||
fmt.Println("could not find __debug_frame section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var (
|
||||
symdat []byte
|
||||
pclndat []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if sec := exe.Section("__gosymtab"); sec != nil {
|
||||
symdat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gosymtab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if sec := exe.Section("__gopclntab"); sec != nil {
|
||||
pclndat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gopclntab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, exe.Section("__text").Addr)
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
fmt.Println("could not get initialize line table", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dbp.goSymTable = tab
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if sec := exe.Section("__debug_line"); sec != nil {
|
||||
debugLine, err := exe.Section("__debug_line").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get __debug_line section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
fmt.Println("could not find __debug_line section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var UnsupportedArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported")
|
||||
|
||||
func (dbp *Process) findExecutable(path string) (*macho.File, string, error) {
|
||||
func findExecutable(path string, pid int) string {
|
||||
if path == "" {
|
||||
path = C.GoString(C.find_executable(C.int(dbp.pid)))
|
||||
path = C.GoString(C.find_executable(C.int(pid)))
|
||||
}
|
||||
exe, err := macho.Open(path)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
if exe.Cpu != macho.CpuAmd64 {
|
||||
return nil, path, UnsupportedArchErr
|
||||
}
|
||||
dbp.dwarf, err = exe.DWARF()
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
return exe, path, nil
|
||||
return path
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
@ -510,6 +417,6 @@ func (dbp *Process) resume() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) detach() error {
|
||||
func (dbp *Process) detach(kill bool) error {
|
||||
return PtraceDetach(dbp.pid, 0)
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/gosym"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@ -17,10 +16,6 @@ import (
|
||||
"time"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||
"golang.org/x/debug/elf"
|
||||
)
|
||||
|
||||
// Process statuses
|
||||
@ -171,103 +166,11 @@ func (dbp *Process) updateThreadList() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var UnsupportedArchErr = errors.New("unsupported architecture - only linux/amd64 is supported")
|
||||
|
||||
func (dbp *Process) findExecutable(path string) (*elf.File, string, error) {
|
||||
func findExecutable(path string, pid int) string {
|
||||
if path == "" {
|
||||
path = fmt.Sprintf("/proc/%d/exe", dbp.pid)
|
||||
}
|
||||
f, err := os.OpenFile(path, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
elfFile, err := elf.NewFile(f)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
if elfFile.Machine != elf.EM_X86_64 {
|
||||
return nil, path, UnsupportedArchErr
|
||||
}
|
||||
dbp.dwarf, err = elfFile.DWARF()
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
return elfFile, path, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugFrame(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameSec := exe.Section(".debug_frame")
|
||||
debugInfoSec := exe.Section(".debug_info")
|
||||
|
||||
if debugFrameSec != nil && debugInfoSec != nil {
|
||||
debugFrame, err := exe.Section(".debug_frame").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_frame section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dat, err := debugInfoSec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_info section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
||||
} else {
|
||||
fmt.Println("could not find .debug_frame section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) obtainGoSymbols(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var (
|
||||
symdat []byte
|
||||
pclndat []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if sec := exe.Section(".gosymtab"); sec != nil {
|
||||
symdat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gosymtab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if sec := exe.Section(".gopclntab"); sec != nil {
|
||||
pclndat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gopclntab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
fmt.Println("could not get initialize line table", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dbp.goSymTable = tab
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugLineInfo(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if sec := exe.Section(".debug_line"); sec != nil {
|
||||
debugLine, err := exe.Section(".debug_line").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_line section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
fmt.Println("could not find .debug_line section in binary")
|
||||
os.Exit(1)
|
||||
path = fmt.Sprintf("/proc/%d/exe", pid)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
@ -484,13 +387,16 @@ func (dbp *Process) resume() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) detach() error {
|
||||
func (dbp *Process) detach(kill bool) error {
|
||||
for threadID := range dbp.threads {
|
||||
err := PtraceDetach(threadID, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if kill {
|
||||
return nil
|
||||
}
|
||||
// For some reason the process will sometimes enter stopped state after a
|
||||
// detach, this doesn't happen immediately either.
|
||||
// We have to wait a bit here, then check if the main thread is stopped and
|
||||
|
||||
@ -94,7 +94,7 @@ func currentPC(p *Process, t *testing.T) uint64 {
|
||||
|
||||
func currentLineNumber(p *Process, t *testing.T) (string, int) {
|
||||
pc := currentPC(p, t)
|
||||
f, l, _ := p.goSymTable.PCToLine(pc)
|
||||
f, l, _ := p.BinInfo().goSymTable.PCToLine(pc)
|
||||
|
||||
return f, l
|
||||
}
|
||||
@ -198,7 +198,7 @@ func TestHalt(t *testing.T) {
|
||||
|
||||
func TestStep(t *testing.T) {
|
||||
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
|
||||
helloworldfunc := p.goSymTable.LookupFunc("main.helloworld")
|
||||
helloworldfunc := p.BinInfo().goSymTable.LookupFunc("main.helloworld")
|
||||
helloworldaddr := helloworldfunc.Entry
|
||||
|
||||
_, err := p.SetBreakpoint(helloworldaddr, UserBreakpoint, nil)
|
||||
@ -220,7 +220,7 @@ func TestStep(t *testing.T) {
|
||||
|
||||
func TestBreakpoint(t *testing.T) {
|
||||
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
|
||||
helloworldfunc := p.goSymTable.LookupFunc("main.helloworld")
|
||||
helloworldfunc := p.BinInfo().goSymTable.LookupFunc("main.helloworld")
|
||||
helloworldaddr := helloworldfunc.Entry
|
||||
|
||||
bp, err := p.SetBreakpoint(helloworldaddr, UserBreakpoint, nil)
|
||||
@ -237,7 +237,7 @@ func TestBreakpoint(t *testing.T) {
|
||||
}
|
||||
|
||||
if pc-1 != bp.Addr && pc != bp.Addr {
|
||||
f, l, _ := p.goSymTable.PCToLine(pc)
|
||||
f, l, _ := p.BinInfo().goSymTable.PCToLine(pc)
|
||||
t.Fatalf("Break not respected:\nPC:%#v %s:%d\nFN:%#v \n", pc, f, l, bp.Addr)
|
||||
}
|
||||
})
|
||||
@ -245,7 +245,7 @@ func TestBreakpoint(t *testing.T) {
|
||||
|
||||
func TestBreakpointInSeperateGoRoutine(t *testing.T) {
|
||||
withTestProcess("testthreads", t, func(p *Process, fixture protest.Fixture) {
|
||||
fn := p.goSymTable.LookupFunc("main.anotherthread")
|
||||
fn := p.BinInfo().goSymTable.LookupFunc("main.anotherthread")
|
||||
if fn == nil {
|
||||
t.Fatal("No fn exists")
|
||||
}
|
||||
@ -265,7 +265,7 @@ func TestBreakpointInSeperateGoRoutine(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
f, l, _ := p.goSymTable.PCToLine(pc)
|
||||
f, l, _ := p.BinInfo().goSymTable.PCToLine(pc)
|
||||
if f != "testthreads.go" && l != 8 {
|
||||
t.Fatal("Program did not hit breakpoint")
|
||||
}
|
||||
@ -283,7 +283,7 @@ func TestBreakpointWithNonExistantFunction(t *testing.T) {
|
||||
|
||||
func TestClearBreakpointBreakpoint(t *testing.T) {
|
||||
withTestProcess("testprog", t, func(p *Process, fixture protest.Fixture) {
|
||||
fn := p.goSymTable.LookupFunc("main.sleepytime")
|
||||
fn := p.BinInfo().goSymTable.LookupFunc("main.sleepytime")
|
||||
bp, err := p.SetBreakpoint(fn.Entry, UserBreakpoint, nil)
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
|
||||
@ -579,7 +579,7 @@ func TestRuntimeBreakpoint(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, l, _ := p.PCToLine(pc)
|
||||
_, l, _ := p.BinInfo().PCToLine(pc)
|
||||
if l != 10 {
|
||||
t.Fatal("did not respect breakpoint")
|
||||
}
|
||||
@ -588,7 +588,7 @@ func TestRuntimeBreakpoint(t *testing.T) {
|
||||
|
||||
func TestFindReturnAddress(t *testing.T) {
|
||||
withTestProcess("testnextprog", t, func(p *Process, fixture protest.Fixture) {
|
||||
start, _, err := p.goSymTable.LineToPC(fixture.Source, 24)
|
||||
start, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 24)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -604,7 +604,7 @@ func TestFindReturnAddress(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, l, _ := p.goSymTable.PCToLine(addr)
|
||||
_, l, _ := p.BinInfo().goSymTable.PCToLine(addr)
|
||||
if l != 40 {
|
||||
t.Fatalf("return address not found correctly, expected line 40")
|
||||
}
|
||||
@ -614,7 +614,7 @@ func TestFindReturnAddress(t *testing.T) {
|
||||
func TestFindReturnAddressTopOfStackFn(t *testing.T) {
|
||||
withTestProcess("testreturnaddress", t, func(p *Process, fixture protest.Fixture) {
|
||||
fnName := "runtime.rt0_go"
|
||||
fn := p.goSymTable.LookupFunc(fnName)
|
||||
fn := p.BinInfo().goSymTable.LookupFunc(fnName)
|
||||
if fn == nil {
|
||||
t.Fatalf("could not find function %s", fnName)
|
||||
}
|
||||
@ -1002,7 +1002,7 @@ func TestProcessReceivesSIGCHLD(t *testing.T) {
|
||||
|
||||
func TestIssue239(t *testing.T) {
|
||||
withTestProcess("is sue239", t, func(p *Process, fixture protest.Fixture) {
|
||||
pos, _, err := p.goSymTable.LineToPC(fixture.Source, 17)
|
||||
pos, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 17)
|
||||
assertNoError(err, t, "LineToPC()")
|
||||
_, err = p.SetBreakpoint(pos, UserBreakpoint, nil)
|
||||
assertNoError(err, t, fmt.Sprintf("SetBreakpoint(%d)", pos))
|
||||
@ -1266,7 +1266,7 @@ func TestIssue325(t *testing.T) {
|
||||
|
||||
func TestBreakpointCounts(t *testing.T) {
|
||||
withTestProcess("bpcountstest", t, func(p *Process, fixture protest.Fixture) {
|
||||
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 12)
|
||||
addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 12)
|
||||
assertNoError(err, t, "LineToPC")
|
||||
bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil)
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
@ -1317,7 +1317,7 @@ func TestBreakpointCountsWithDetection(t *testing.T) {
|
||||
}
|
||||
m := map[int64]int64{}
|
||||
withTestProcess("bpcountstest", t, func(p *Process, fixture protest.Fixture) {
|
||||
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 12)
|
||||
addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 12)
|
||||
assertNoError(err, t, "LineToPC")
|
||||
bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil)
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
@ -1412,7 +1412,7 @@ func BenchmarkGoroutinesInfo(b *testing.B) {
|
||||
func TestIssue262(t *testing.T) {
|
||||
// Continue does not work when the current breakpoint is set on a NOP instruction
|
||||
withTestProcess("issue262", t, func(p *Process, fixture protest.Fixture) {
|
||||
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 11)
|
||||
addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 11)
|
||||
assertNoError(err, t, "LineToPC")
|
||||
_, err = p.SetBreakpoint(addr, UserBreakpoint, nil)
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
@ -1434,7 +1434,7 @@ func TestIssue305(t *testing.T) {
|
||||
// the internal breakpoints aren't cleared preventing further use of
|
||||
// 'next' command
|
||||
withTestProcess("issue305", t, func(p *Process, fixture protest.Fixture) {
|
||||
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 5)
|
||||
addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 5)
|
||||
assertNoError(err, t, "LineToPC()")
|
||||
_, err = p.SetBreakpoint(addr, UserBreakpoint, nil)
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
@ -1477,7 +1477,7 @@ func BenchmarkLocalVariables(b *testing.B) {
|
||||
|
||||
func TestCondBreakpoint(t *testing.T) {
|
||||
withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) {
|
||||
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 9)
|
||||
addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 9)
|
||||
assertNoError(err, t, "LineToPC")
|
||||
bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil)
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
@ -1501,7 +1501,7 @@ func TestCondBreakpoint(t *testing.T) {
|
||||
|
||||
func TestCondBreakpointError(t *testing.T) {
|
||||
withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) {
|
||||
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 9)
|
||||
addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 9)
|
||||
assertNoError(err, t, "LineToPC")
|
||||
bp, err := p.SetBreakpoint(addr, UserBreakpoint, nil)
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
@ -1581,7 +1581,7 @@ func TestStepIntoFunction(t *testing.T) {
|
||||
func TestIssue384(t *testing.T) {
|
||||
// Crash related to reading uninitialized memory, introduced by the memory prefetching optimization
|
||||
withTestProcess("issue384", t, func(p *Process, fixture protest.Fixture) {
|
||||
start, _, err := p.goSymTable.LineToPC(fixture.Source, 13)
|
||||
start, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 13)
|
||||
assertNoError(err, t, "LineToPC()")
|
||||
_, err = p.SetBreakpoint(start, UserBreakpoint, nil)
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
@ -1594,7 +1594,7 @@ func TestIssue384(t *testing.T) {
|
||||
func TestIssue332_Part1(t *testing.T) {
|
||||
// Next shouldn't step inside a function call
|
||||
withTestProcess("issue332", t, func(p *Process, fixture protest.Fixture) {
|
||||
start, _, err := p.goSymTable.LineToPC(fixture.Source, 8)
|
||||
start, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 8)
|
||||
assertNoError(err, t, "LineToPC()")
|
||||
_, err = p.SetBreakpoint(start, UserBreakpoint, nil)
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
@ -1620,7 +1620,7 @@ func TestIssue332_Part2(t *testing.T) {
|
||||
// which leads to 'next' and 'stack' failing with error "could not find FDE for PC: <garbage>"
|
||||
// because the incorrect FDE data leads to reading the wrong stack address as the return address
|
||||
withTestProcess("issue332", t, func(p *Process, fixture protest.Fixture) {
|
||||
start, _, err := p.goSymTable.LineToPC(fixture.Source, 8)
|
||||
start, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 8)
|
||||
assertNoError(err, t, "LineToPC()")
|
||||
_, err = p.SetBreakpoint(start, UserBreakpoint, nil)
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
@ -1671,7 +1671,7 @@ func TestIssue396(t *testing.T) {
|
||||
func TestIssue414(t *testing.T) {
|
||||
// Stepping until the program exits
|
||||
withTestProcess("math", t, func(p *Process, fixture protest.Fixture) {
|
||||
start, _, err := p.goSymTable.LineToPC(fixture.Source, 9)
|
||||
start, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 9)
|
||||
assertNoError(err, t, "LineToPC()")
|
||||
_, err = p.SetBreakpoint(start, UserBreakpoint, nil)
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
@ -1936,7 +1936,7 @@ func TestUnsupportedArch(t *testing.T) {
|
||||
|
||||
p, err := Launch([]string{outfile}, ".")
|
||||
switch err {
|
||||
case UnsupportedArchErr:
|
||||
case UnsupportedLinuxArchErr, UnsupportedWindowsArchErr, UnsupportedDarwinArchErr:
|
||||
// all good
|
||||
case nil:
|
||||
p.Halt()
|
||||
@ -1951,7 +1951,7 @@ func TestIssue573(t *testing.T) {
|
||||
// calls to runtime.duffzero and runtime.duffcopy jump directly into the middle
|
||||
// of the function and the internal breakpoint set by StepInto may be missed.
|
||||
withTestProcess("issue573", t, func(p *Process, fixture protest.Fixture) {
|
||||
f := p.goSymTable.LookupFunc("main.foo")
|
||||
f := p.BinInfo().goSymTable.LookupFunc("main.foo")
|
||||
_, err := p.SetBreakpoint(f.Entry, UserBreakpoint, nil)
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
@ -2271,7 +2271,7 @@ func TestStepOutDefer(t *testing.T) {
|
||||
|
||||
assertNoError(p.StepOut(), t, "StepOut()")
|
||||
|
||||
f, l, _ := p.goSymTable.PCToLine(currentPC(p, t))
|
||||
f, l, _ := p.BinInfo().goSymTable.PCToLine(currentPC(p, t))
|
||||
if f == fixture.Source || l == 6 {
|
||||
t.Fatalf("wrong location %s:%d, expected to end somewhere in runtime", f, l)
|
||||
}
|
||||
@ -2375,7 +2375,7 @@ func TestWorkDir(t *testing.T) {
|
||||
wd = "/private/tmp"
|
||||
}
|
||||
withTestProcessArgs("workdir", t, wd, func(p *Process, fixture protest.Fixture) {
|
||||
addr, _, err := p.goSymTable.LineToPC(fixture.Source, 14)
|
||||
addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 14)
|
||||
assertNoError(err, t, "LineToPC")
|
||||
p.SetBreakpoint(addr, UserBreakpoint, nil)
|
||||
p.Continue()
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"debug/pe"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@ -13,10 +11,6 @@ import (
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/windows"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||
"golang.org/x/debug/dwarf"
|
||||
)
|
||||
|
||||
// OSProcessDetails holds Windows specific information.
|
||||
@ -39,11 +33,11 @@ func Launch(cmd []string, wd string) (*Process, error) {
|
||||
}
|
||||
}
|
||||
|
||||
peFile, err := openExecutablePath(argv0Go)
|
||||
_, closer, err := openExecutablePathPE(argv0Go)
|
||||
if err != nil {
|
||||
return nil, NotExecutableErr
|
||||
}
|
||||
peFile.Close()
|
||||
closer.Close()
|
||||
|
||||
// Duplicate the stdin/stdout/stderr handles
|
||||
files := []uintptr{uintptr(syscall.Stdin), uintptr(syscall.Stdout), uintptr(syscall.Stderr)}
|
||||
@ -253,191 +247,8 @@ func (dbp *Process) addThread(hThread syscall.Handle, threadID int, attach, susp
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugFrame(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameSec := exe.Section(".debug_frame")
|
||||
debugInfoSec := exe.Section(".debug_info")
|
||||
|
||||
if debugFrameSec != nil && debugInfoSec != nil {
|
||||
debugFrame, err := debugFrameSec.Data()
|
||||
if err != nil && uint32(len(debugFrame)) < debugFrameSec.Size {
|
||||
fmt.Println("could not get .debug_frame section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if 0 < debugFrameSec.VirtualSize && debugFrameSec.VirtualSize < debugFrameSec.Size {
|
||||
debugFrame = debugFrame[:debugFrameSec.VirtualSize]
|
||||
}
|
||||
dat, err := debugInfoSec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_info section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
||||
} else {
|
||||
fmt.Println("could not find .debug_frame section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
||||
for _, s := range f.Symbols {
|
||||
if s.Name != name {
|
||||
continue
|
||||
}
|
||||
if s.SectionNumber <= 0 {
|
||||
return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
|
||||
}
|
||||
if len(f.Sections) < int(s.SectionNumber) {
|
||||
return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no %s symbol found", name)
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
|
||||
ssym, err := findPESymbol(f, sname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
esym, err := findPESymbol(f, ename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ssym.SectionNumber != esym.SectionNumber {
|
||||
return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
|
||||
}
|
||||
sect := f.Sections[ssym.SectionNumber-1]
|
||||
data, err := sect.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data[ssym.Value:esym.Value], nil
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func pcln(exe *pe.File) (textStart uint64, symtab, pclntab []byte, err error) {
|
||||
var imageBase uint64
|
||||
switch oh := exe.OptionalHeader.(type) {
|
||||
case *pe.OptionalHeader32:
|
||||
imageBase = uint64(oh.ImageBase)
|
||||
case *pe.OptionalHeader64:
|
||||
imageBase = oh.ImageBase
|
||||
default:
|
||||
return 0, nil, nil, fmt.Errorf("pe file format not recognized")
|
||||
}
|
||||
if sect := exe.Section(".text"); sect != nil {
|
||||
textStart = imageBase + uint64(sect.VirtualAddress)
|
||||
}
|
||||
if pclntab, err = loadPETable(exe, "runtime.pclntab", "runtime.epclntab"); err != nil {
|
||||
// We didn't find the symbols, so look for the names used in 1.3 and earlier.
|
||||
// TODO: Remove code looking for the old symbols when we no longer care about 1.3.
|
||||
var err2 error
|
||||
if pclntab, err2 = loadPETable(exe, "pclntab", "epclntab"); err2 != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
}
|
||||
if symtab, err = loadPETable(exe, "runtime.symtab", "runtime.esymtab"); err != nil {
|
||||
// Same as above.
|
||||
var err2 error
|
||||
if symtab, err2 = loadPETable(exe, "symtab", "esymtab"); err2 != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
}
|
||||
return textStart, symtab, pclntab, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) obtainGoSymbols(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
_, symdat, pclndat, err := pcln(exe)
|
||||
if err != nil {
|
||||
fmt.Println("could not get Go symbols", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, uint64(exe.Section(".text").Offset))
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
fmt.Println("could not get initialize line table", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dbp.goSymTable = tab
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugLineInfo(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if sec := exe.Section(".debug_line"); sec != nil {
|
||||
debugLine, err := sec.Data()
|
||||
if err != nil && uint32(len(debugLine)) < sec.Size {
|
||||
fmt.Println("could not get .debug_line section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
|
||||
debugLine = debugLine[:sec.VirtualSize]
|
||||
}
|
||||
dbp.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
fmt.Println("could not find .debug_line section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var UnsupportedArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
|
||||
|
||||
func (dbp *Process) findExecutable(path string) (*pe.File, string, error) {
|
||||
peFile, err := openExecutablePath(path)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
|
||||
return nil, path, UnsupportedArchErr
|
||||
}
|
||||
dbp.dwarf, err = dwarfFromPE(peFile)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
return peFile, path, nil
|
||||
}
|
||||
|
||||
func openExecutablePath(path string) (*pe.File, error) {
|
||||
f, err := os.OpenFile(path, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pe.NewFile(f)
|
||||
}
|
||||
|
||||
// Adapted from src/debug/pe/file.go: pe.(*File).DWARF()
|
||||
func dwarfFromPE(f *pe.File) (*dwarf.Data, error) {
|
||||
// There are many other DWARF sections, but these
|
||||
// are the ones the debug/dwarf package uses.
|
||||
// Don't bother loading others.
|
||||
var names = [...]string{"abbrev", "info", "line", "str"}
|
||||
var dat [len(names)][]byte
|
||||
for i, name := range names {
|
||||
name = ".debug_" + name
|
||||
s := f.Section(name)
|
||||
if s == nil {
|
||||
continue
|
||||
}
|
||||
b, err := s.Data()
|
||||
if err != nil && uint32(len(b)) < s.Size {
|
||||
return nil, err
|
||||
}
|
||||
if 0 < s.VirtualSize && s.VirtualSize < s.Size {
|
||||
b = b[:s.VirtualSize]
|
||||
}
|
||||
dat[i] = b
|
||||
}
|
||||
|
||||
abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3]
|
||||
return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str)
|
||||
func findExecutable(path string, pid int) string {
|
||||
return path
|
||||
}
|
||||
|
||||
type waitForDebugEventFlags int
|
||||
@ -519,8 +330,8 @@ func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, e
|
||||
// this exception anymore.
|
||||
atbp := true
|
||||
if thread, found := dbp.threads[tid]; found {
|
||||
if data, err := thread.readMemory(exception.ExceptionRecord.ExceptionAddress, dbp.arch.BreakpointSize()); err == nil {
|
||||
instr := dbp.arch.BreakpointInstruction()
|
||||
if data, err := thread.readMemory(exception.ExceptionRecord.ExceptionAddress, dbp.bi.arch.BreakpointSize()); err == nil {
|
||||
instr := dbp.bi.arch.BreakpointInstruction()
|
||||
for i := range instr {
|
||||
if data[i] != instr[i] {
|
||||
atbp = false
|
||||
@ -663,13 +474,15 @@ func (dbp *Process) resume() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) detach() error {
|
||||
func (dbp *Process) detach(kill bool) error {
|
||||
if !kill {
|
||||
for _, thread := range dbp.threads {
|
||||
_, err := _ResumeThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return PtraceDetach(dbp.pid, 0)
|
||||
}
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ func (g *G) Stacktrace(depth int) ([]Stackframe, error) {
|
||||
// GoroutineLocation returns the location of the given
|
||||
// goroutine.
|
||||
func (dbp *Process) GoroutineLocation(g *G) *Location {
|
||||
f, l, fn := dbp.PCToLine(g.PC)
|
||||
f, l, fn := dbp.bi.PCToLine(g.PC)
|
||||
return &Location{PC: g.PC, File: f, Line: l, Fn: fn}
|
||||
}
|
||||
|
||||
@ -130,11 +130,11 @@ type savedLR struct {
|
||||
}
|
||||
|
||||
func newStackIterator(dbp *Process, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
|
||||
stackBarrierFunc := dbp.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9
|
||||
stackBarrierFunc := dbp.bi.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9
|
||||
var stackBarrierPC uint64
|
||||
if stackBarrierFunc != nil && stkbar != nil {
|
||||
stackBarrierPC = stackBarrierFunc.Entry
|
||||
fn := dbp.goSymTable.PCToFunc(pc)
|
||||
fn := dbp.bi.goSymTable.PCToFunc(pc)
|
||||
if fn != nil && fn.Name == runtimeStackBarrier {
|
||||
// We caught the goroutine as it's executing the stack barrier, we must
|
||||
// determine whether or not g.stackPos has already been incremented or not.
|
||||
@ -188,7 +188,7 @@ func (it *stackIterator) Next() bool {
|
||||
it.top = false
|
||||
it.pc = it.frame.Ret
|
||||
it.sp = uint64(it.frame.CFA)
|
||||
it.bp, _ = readUintRaw(it.dbp.currentThread, uintptr(it.bp), int64(it.dbp.arch.PtrSize()))
|
||||
it.bp, _ = readUintRaw(it.dbp.currentThread, uintptr(it.bp), int64(it.dbp.bi.arch.PtrSize()))
|
||||
return true
|
||||
}
|
||||
|
||||
@ -206,14 +206,14 @@ func (it *stackIterator) Err() error {
|
||||
}
|
||||
|
||||
func (dbp *Process) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) {
|
||||
fde, err := dbp.frameEntries.FDEForPC(pc)
|
||||
fde, err := dbp.bi.frameEntries.FDEForPC(pc)
|
||||
if _, nofde := err.(*frame.NoFDEForPCError); nofde {
|
||||
if bp == 0 {
|
||||
return Stackframe{}, err
|
||||
}
|
||||
// When no FDE is available attempt to use BP instead
|
||||
retaddr := uintptr(int(bp) + dbp.arch.PtrSize())
|
||||
cfa := int64(retaddr) + int64(dbp.arch.PtrSize())
|
||||
retaddr := uintptr(int(bp) + dbp.bi.arch.PtrSize())
|
||||
cfa := int64(retaddr) + int64(dbp.bi.arch.PtrSize())
|
||||
return dbp.newStackframe(pc, cfa, retaddr, nil, top)
|
||||
}
|
||||
|
||||
@ -228,14 +228,14 @@ func (dbp *Process) newStackframe(pc uint64, cfa int64, retaddr uintptr, fde *fr
|
||||
if retaddr == 0 {
|
||||
return Stackframe{}, NullAddrError{}
|
||||
}
|
||||
f, l, fn := dbp.PCToLine(pc)
|
||||
ret, err := readUintRaw(dbp.currentThread, retaddr, int64(dbp.arch.PtrSize()))
|
||||
f, l, fn := dbp.bi.PCToLine(pc)
|
||||
ret, err := readUintRaw(dbp.currentThread, retaddr, int64(dbp.bi.arch.PtrSize()))
|
||||
if err != nil {
|
||||
return Stackframe{}, err
|
||||
}
|
||||
r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: ret, addrret: uint64(retaddr)}
|
||||
if !top {
|
||||
r.Call.File, r.Call.Line, r.Call.Fn = dbp.PCToLine(pc - 1)
|
||||
r.Call.File, r.Call.Line, r.Call.Fn = dbp.bi.PCToLine(pc - 1)
|
||||
r.Call.PC = r.Current.PC
|
||||
} else {
|
||||
r.Call = r.Current
|
||||
|
||||
@ -112,7 +112,7 @@ func (thread *Thread) Location() (*Location, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, l, fn := thread.dbp.PCToLine(pc)
|
||||
f, l, fn := thread.dbp.bi.PCToLine(pc)
|
||||
return &Location{PC: pc, File: f, Line: l, Fn: fn}, nil
|
||||
}
|
||||
|
||||
@ -218,7 +218,7 @@ func (dbp *Process) next(stepInto bool) error {
|
||||
if dbp.selectedGoroutine != nil {
|
||||
deferPCEntry := dbp.selectedGoroutine.DeferPC()
|
||||
if deferPCEntry != 0 {
|
||||
_, _, deferfn := dbp.goSymTable.PCToLine(deferPCEntry)
|
||||
_, _, deferfn := dbp.bi.goSymTable.PCToLine(deferPCEntry)
|
||||
var err error
|
||||
deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
|
||||
if err != nil {
|
||||
@ -240,7 +240,7 @@ func (dbp *Process) next(stepInto bool) error {
|
||||
}
|
||||
|
||||
// Add breakpoints on all the lines in the current function
|
||||
pcs, err := dbp.lineInfo.AllPCsBetween(topframe.FDE.Begin(), topframe.FDE.End()-1, topframe.Current.File)
|
||||
pcs, err := dbp.bi.lineInfo.AllPCsBetween(topframe.FDE.Begin(), topframe.FDE.End()-1, topframe.Current.File)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -255,7 +255,7 @@ func (dbp *Process) next(stepInto bool) error {
|
||||
}
|
||||
|
||||
if !covered {
|
||||
fn := dbp.goSymTable.PCToFunc(topframe.Ret)
|
||||
fn := dbp.bi.goSymTable.PCToFunc(topframe.Ret)
|
||||
if dbp.selectedGoroutine != nil && fn != nil && fn.Name == "runtime.goexit" {
|
||||
return nil
|
||||
}
|
||||
@ -340,13 +340,13 @@ func (thread *Thread) getGVariable() (*Variable, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if thread.dbp.arch.GStructOffset() == 0 {
|
||||
if thread.dbp.bi.arch.GStructOffset() == 0 {
|
||||
// GetG was called through SwitchThread / updateThreadList during initialization
|
||||
// thread.dbp.arch isn't setup yet (it needs a current thread to read global variables from)
|
||||
return nil, fmt.Errorf("g struct offset not initialized")
|
||||
}
|
||||
|
||||
gaddrbs, err := thread.readMemory(uintptr(regs.TLS()+thread.dbp.arch.GStructOffset()), thread.dbp.arch.PtrSize())
|
||||
gaddrbs, err := thread.readMemory(uintptr(regs.TLS()+thread.dbp.bi.arch.GStructOffset()), thread.dbp.bi.arch.PtrSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -360,7 +360,7 @@ func (thread *Thread) getGVariable() (*Variable, error) {
|
||||
}
|
||||
|
||||
func (thread *Thread) newGVariable(gaddr uintptr, deref bool) (*Variable, error) {
|
||||
typ, err := thread.dbp.findType("runtime.g")
|
||||
typ, err := thread.dbp.bi.findType("runtime.g")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -368,7 +368,7 @@ func (thread *Thread) newGVariable(gaddr uintptr, deref bool) (*Variable, error)
|
||||
name := ""
|
||||
|
||||
if deref {
|
||||
typ = &dwarf.PtrType{dwarf.CommonType{int64(thread.dbp.arch.PtrSize()), "", reflect.Ptr, 0}, typ}
|
||||
typ = &dwarf.PtrType{dwarf.CommonType{int64(thread.dbp.bi.arch.PtrSize()), "", reflect.Ptr, 0}, typ}
|
||||
} else {
|
||||
name = "runtime.curg"
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ func (t *Thread) blocked() bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
fn := t.dbp.goSymTable.PCToFunc(pc)
|
||||
fn := t.dbp.bi.goSymTable.PCToFunc(pc)
|
||||
if fn == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ func (t *Thread) singleStep() (err error) {
|
||||
|
||||
func (t *Thread) blocked() bool {
|
||||
pc, _ := t.PC()
|
||||
fn := t.dbp.goSymTable.PCToFunc(pc)
|
||||
fn := t.dbp.bi.goSymTable.PCToFunc(pc)
|
||||
if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -110,7 +110,7 @@ func (t *Thread) blocked() bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
fn := t.dbp.goSymTable.PCToFunc(pc)
|
||||
fn := t.dbp.bi.goSymTable.PCToFunc(pc)
|
||||
if fn == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -38,40 +38,40 @@ const (
|
||||
)
|
||||
|
||||
// Do not call this function directly it isn't able to deal correctly with package paths
|
||||
func (dbp *Process) findType(name string) (dwarf.Type, error) {
|
||||
off, found := dbp.types[name]
|
||||
func (bi *BinaryInfo) findType(name string) (dwarf.Type, error) {
|
||||
off, found := bi.types[name]
|
||||
if !found {
|
||||
return nil, reader.TypeNotFoundErr
|
||||
}
|
||||
return dbp.dwarf.Type(off)
|
||||
return bi.dwarf.Type(off)
|
||||
}
|
||||
|
||||
func (dbp *Process) pointerTo(typ dwarf.Type) dwarf.Type {
|
||||
return &dwarf.PtrType{dwarf.CommonType{int64(dbp.arch.PtrSize()), "*" + typ.Common().Name, reflect.Ptr, 0}, typ}
|
||||
func pointerTo(typ dwarf.Type, arch Arch) dwarf.Type {
|
||||
return &dwarf.PtrType{dwarf.CommonType{int64(arch.PtrSize()), "*" + typ.Common().Name, reflect.Ptr, 0}, typ}
|
||||
}
|
||||
|
||||
func (dbp *Process) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
|
||||
dbp.loadPackageMap()
|
||||
func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
|
||||
bi.loadPackageMap()
|
||||
if lit, islit := expr.(*ast.BasicLit); islit && lit.Kind == token.STRING {
|
||||
// Allow users to specify type names verbatim as quoted
|
||||
// string. Useful as a catch-all workaround for cases where we don't
|
||||
// parse/serialize types correctly or can not resolve package paths.
|
||||
typn, _ := strconv.Unquote(lit.Value)
|
||||
return dbp.findType(typn)
|
||||
return bi.findType(typn)
|
||||
}
|
||||
dbp.expandPackagesInType(expr)
|
||||
bi.expandPackagesInType(expr)
|
||||
if snode, ok := expr.(*ast.StarExpr); ok {
|
||||
// Pointer types only appear in the dwarf informations when
|
||||
// a pointer to the type is used in the target program, here
|
||||
// we create a pointer type on the fly so that the user can
|
||||
// specify a pointer to any variable used in the target program
|
||||
ptyp, err := dbp.findTypeExpr(snode.X)
|
||||
ptyp, err := bi.findTypeExpr(snode.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dbp.pointerTo(ptyp), nil
|
||||
return pointerTo(ptyp, bi.arch), nil
|
||||
}
|
||||
return dbp.findType(exprToString(expr))
|
||||
return bi.findType(exprToString(expr))
|
||||
}
|
||||
|
||||
func complexType(typename string) bool {
|
||||
@ -84,12 +84,12 @@ func complexType(typename string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (dbp *Process) loadPackageMap() error {
|
||||
if dbp.packageMap != nil {
|
||||
func (bi *BinaryInfo) loadPackageMap() error {
|
||||
if bi.packageMap != nil {
|
||||
return nil
|
||||
}
|
||||
dbp.packageMap = map[string]string{}
|
||||
reader := dbp.DwarfReader()
|
||||
bi.packageMap = map[string]string{}
|
||||
reader := bi.DwarfReader()
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return err
|
||||
@ -114,7 +114,7 @@ func (dbp *Process) loadPackageMap() error {
|
||||
continue
|
||||
}
|
||||
name := path[slash+1:]
|
||||
dbp.packageMap[name] = path
|
||||
bi.packageMap[name] = path
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -129,11 +129,11 @@ func (v sortFunctionsDebugInfoByLowpc) Swap(i, j int) {
|
||||
v[j] = temp
|
||||
}
|
||||
|
||||
func (dbp *Process) loadDebugInfoMaps(wg *sync.WaitGroup) {
|
||||
func (bi *BinaryInfo) loadDebugInfoMaps(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
dbp.types = make(map[string]dwarf.Offset)
|
||||
dbp.functions = []functionDebugInfo{}
|
||||
reader := dbp.DwarfReader()
|
||||
bi.types = make(map[string]dwarf.Offset)
|
||||
bi.functions = []functionDebugInfo{}
|
||||
reader := bi.DwarfReader()
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
break
|
||||
@ -144,8 +144,8 @@ func (dbp *Process) loadDebugInfoMaps(wg *sync.WaitGroup) {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if _, exists := dbp.types[name]; !exists {
|
||||
dbp.types[name] = entry.Offset
|
||||
if _, exists := bi.types[name]; !exists {
|
||||
bi.types[name] = entry.Offset
|
||||
}
|
||||
case dwarf.TagSubprogram:
|
||||
lowpc, ok := entry.Val(dwarf.AttrLowpc).(uint64)
|
||||
@ -156,19 +156,19 @@ func (dbp *Process) loadDebugInfoMaps(wg *sync.WaitGroup) {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
dbp.functions = append(dbp.functions, functionDebugInfo{lowpc, highpc, entry.Offset})
|
||||
bi.functions = append(bi.functions, functionDebugInfo{lowpc, highpc, entry.Offset})
|
||||
}
|
||||
}
|
||||
sort.Sort(sortFunctionsDebugInfoByLowpc(dbp.functions))
|
||||
sort.Sort(sortFunctionsDebugInfoByLowpc(bi.functions))
|
||||
}
|
||||
|
||||
func (dbp *Process) findFunctionDebugInfo(pc uint64) (dwarf.Offset, error) {
|
||||
i := sort.Search(len(dbp.functions), func(i int) bool {
|
||||
fn := dbp.functions[i]
|
||||
func (bi *BinaryInfo) findFunctionDebugInfo(pc uint64) (dwarf.Offset, error) {
|
||||
i := sort.Search(len(bi.functions), func(i int) bool {
|
||||
fn := bi.functions[i]
|
||||
return pc <= fn.lowpc || (fn.lowpc <= pc && pc < fn.highpc)
|
||||
})
|
||||
if i != len(dbp.functions) {
|
||||
fn := dbp.functions[i]
|
||||
if i != len(bi.functions) {
|
||||
fn := bi.functions[i]
|
||||
if fn.lowpc <= pc && pc < fn.highpc {
|
||||
return fn.offset, nil
|
||||
}
|
||||
@ -176,37 +176,37 @@ func (dbp *Process) findFunctionDebugInfo(pc uint64) (dwarf.Offset, error) {
|
||||
return 0, errors.New("unable to find function context")
|
||||
}
|
||||
|
||||
func (dbp *Process) expandPackagesInType(expr ast.Expr) {
|
||||
func (bi *BinaryInfo) expandPackagesInType(expr ast.Expr) {
|
||||
switch e := expr.(type) {
|
||||
case *ast.ArrayType:
|
||||
dbp.expandPackagesInType(e.Elt)
|
||||
bi.expandPackagesInType(e.Elt)
|
||||
case *ast.ChanType:
|
||||
dbp.expandPackagesInType(e.Value)
|
||||
bi.expandPackagesInType(e.Value)
|
||||
case *ast.FuncType:
|
||||
for i := range e.Params.List {
|
||||
dbp.expandPackagesInType(e.Params.List[i].Type)
|
||||
bi.expandPackagesInType(e.Params.List[i].Type)
|
||||
}
|
||||
if e.Results != nil {
|
||||
for i := range e.Results.List {
|
||||
dbp.expandPackagesInType(e.Results.List[i].Type)
|
||||
bi.expandPackagesInType(e.Results.List[i].Type)
|
||||
}
|
||||
}
|
||||
case *ast.MapType:
|
||||
dbp.expandPackagesInType(e.Key)
|
||||
dbp.expandPackagesInType(e.Value)
|
||||
bi.expandPackagesInType(e.Key)
|
||||
bi.expandPackagesInType(e.Value)
|
||||
case *ast.ParenExpr:
|
||||
dbp.expandPackagesInType(e.X)
|
||||
bi.expandPackagesInType(e.X)
|
||||
case *ast.SelectorExpr:
|
||||
switch x := e.X.(type) {
|
||||
case *ast.Ident:
|
||||
if path, ok := dbp.packageMap[x.Name]; ok {
|
||||
if path, ok := bi.packageMap[x.Name]; ok {
|
||||
x.Name = path
|
||||
}
|
||||
default:
|
||||
dbp.expandPackagesInType(e.X)
|
||||
bi.expandPackagesInType(e.X)
|
||||
}
|
||||
case *ast.StarExpr:
|
||||
dbp.expandPackagesInType(e.X)
|
||||
bi.expandPackagesInType(e.X)
|
||||
default:
|
||||
// nothing to do
|
||||
}
|
||||
@ -221,7 +221,7 @@ type nameOfRuntimeTypeEntry struct {
|
||||
// _type is a non-loaded Variable pointing to runtime._type struct in the target.
|
||||
// The returned string is in the format that's used in DWARF data
|
||||
func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error) {
|
||||
if e, ok := _type.dbp.nameOfRuntimeType[_type.Addr]; ok {
|
||||
if e, ok := _type.dbp.bi.nameOfRuntimeType[_type.Addr]; ok {
|
||||
return e.typename, e.kind, nil
|
||||
}
|
||||
|
||||
@ -244,7 +244,7 @@ func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error)
|
||||
return typename, kind, err
|
||||
}
|
||||
|
||||
_type.dbp.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename, kind}
|
||||
_type.dbp.bi.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename, kind}
|
||||
|
||||
return typename, kind, nil
|
||||
}
|
||||
@ -277,7 +277,7 @@ func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string
|
||||
// For a description of how memory is organized for type names read
|
||||
// the comment to 'type name struct' in $GOROOT/src/reflect/type.go
|
||||
|
||||
typename, _, _, err = _type.dbp.resolveNameOff(_type.Addr, uintptr(strOff))
|
||||
typename, _, _, err = _type.dbp.bi.resolveNameOff(_type.Addr, uintptr(strOff), _type.dbp.currentThread)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -303,7 +303,7 @@ func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string
|
||||
if ut := uncommon(_type, tflag); ut != nil {
|
||||
if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil {
|
||||
pkgPathOff, _ := constant.Int64Val(pkgPathField.Value)
|
||||
pkgPath, _, _, err := _type.dbp.resolveNameOff(_type.Addr, uintptr(pkgPathOff))
|
||||
pkgPath, _, _, err := _type.dbp.bi.resolveNameOff(_type.Addr, uintptr(pkgPathOff), _type.dbp.currentThread)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -376,11 +376,11 @@ func nameOfUnnamedRuntimeType(_type *Variable, kind, tflag int64) (string, error
|
||||
// (optional) and then by an array of pointers to runtime._type,
|
||||
// one for each input and output argument.
|
||||
func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string, error) {
|
||||
rtyp, err := _type.dbp.findType("runtime._type")
|
||||
rtyp, err := _type.dbp.bi.findType("runtime._type")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
prtyp := _type.dbp.pointerTo(rtyp)
|
||||
prtyp := pointerTo(rtyp, _type.dbp.bi.arch)
|
||||
|
||||
uadd := _type.RealType.Common().ByteSize
|
||||
if ut := uncommon(_type, tflag); ut != nil {
|
||||
@ -407,7 +407,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
|
||||
|
||||
for i := int64(0); i < inCount; i++ {
|
||||
argtype := cursortyp.maybeDereference()
|
||||
cursortyp.Addr += uintptr(_type.dbp.arch.PtrSize())
|
||||
cursortyp.Addr += uintptr(_type.dbp.bi.arch.PtrSize())
|
||||
argtypename, _, err := nameOfRuntimeType(argtype)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -434,7 +434,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
|
||||
buf.WriteString(" (")
|
||||
for i := int64(0); i < outCount; i++ {
|
||||
argtype := cursortyp.maybeDereference()
|
||||
cursortyp.Addr += uintptr(_type.dbp.arch.PtrSize())
|
||||
cursortyp.Addr += uintptr(_type.dbp.bi.arch.PtrSize())
|
||||
argtypename, _, err := nameOfRuntimeType(argtype)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -473,14 +473,14 @@ func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, err
|
||||
case "name":
|
||||
nameoff, _ := constant.Int64Val(im.Children[i].Value)
|
||||
var err error
|
||||
methodname, _, _, err = _type.dbp.resolveNameOff(_type.Addr, uintptr(nameoff))
|
||||
methodname, _, _, err = _type.dbp.bi.resolveNameOff(_type.Addr, uintptr(nameoff), _type.dbp.currentThread)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
case "typ":
|
||||
typeoff, _ := constant.Int64Val(im.Children[i].Value)
|
||||
typ, err := _type.dbp.resolveTypeOff(_type.Addr, uintptr(typeoff))
|
||||
typ, err := _type.dbp.bi.resolveTypeOff(_type.Addr, uintptr(typeoff), _type.dbp.currentThread)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -536,7 +536,7 @@ func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error)
|
||||
case "name":
|
||||
nameoff, _ := constant.Int64Val(field.Children[i].Value)
|
||||
var err error
|
||||
fieldname, _, _, err = _type.dbp.loadName(uintptr(nameoff))
|
||||
fieldname, _, _, err = _type.dbp.bi.loadName(uintptr(nameoff), _type.mem)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -578,13 +578,13 @@ func fieldToType(_type *Variable, fieldName string) (string, error) {
|
||||
}
|
||||
|
||||
func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
|
||||
rtyp, err := _type.dbp.findType("runtime._type")
|
||||
rtyp, err := _type.dbp.bi.findType("runtime._type")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prtyp := _type.dbp.pointerTo(rtyp)
|
||||
prtyp := pointerTo(rtyp, _type.dbp.bi.arch)
|
||||
|
||||
uintptrtyp, err := _type.dbp.findType("uintptr")
|
||||
uintptrtyp, err := _type.dbp.bi.findType("uintptr")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -602,7 +602,7 @@ func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
|
||||
|
||||
newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType {
|
||||
r := newStructType("[]"+elemtype.Common().Name, uintptr(3*uintptrtyp.Size()))
|
||||
appendField(r, "array", _type.dbp.pointerTo(elemtype), 0)
|
||||
appendField(r, "array", pointerTo(elemtype, _type.dbp.bi.arch), 0)
|
||||
appendField(r, "len", uintptrtyp, uintptr(uintptrtyp.Size()))
|
||||
appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size()))
|
||||
return &dwarf.SliceType{StructType: *r, ElemType: elemtype}
|
||||
@ -746,7 +746,7 @@ func uncommon(_type *Variable, tflag int64) *Variable {
|
||||
return nil
|
||||
}
|
||||
|
||||
typ, err := _type.dbp.findType("runtime.uncommontype")
|
||||
typ, err := _type.dbp.bi.findType("runtime.uncommontype")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -190,7 +190,7 @@ func newVariable(name string, addr uintptr, dwarfType dwarf.Type, dbp *Process,
|
||||
v.stride = 1
|
||||
v.fieldType = &dwarf.UintType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 1, Name: "byte"}, BitSize: 8, BitOffset: 0}}
|
||||
if v.Addr != 0 {
|
||||
v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.dbp.arch, v.Addr)
|
||||
v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.dbp.bi.arch, v.Addr)
|
||||
}
|
||||
case *dwarf.SliceType:
|
||||
v.Kind = reflect.Slice
|
||||
@ -321,17 +321,17 @@ func (v *Variable) toField(field *dwarf.StructField) (*Variable, error) {
|
||||
// DwarfReader returns the DwarfReader containing the
|
||||
// Dwarf information for the target process.
|
||||
func (scope *EvalScope) DwarfReader() *reader.Reader {
|
||||
return scope.Thread.dbp.DwarfReader()
|
||||
return scope.Thread.dbp.bi.DwarfReader()
|
||||
}
|
||||
|
||||
// Type returns the Dwarf type entry at `offset`.
|
||||
func (scope *EvalScope) Type(offset dwarf.Offset) (dwarf.Type, error) {
|
||||
return scope.Thread.dbp.dwarf.Type(offset)
|
||||
return scope.Thread.dbp.bi.dwarf.Type(offset)
|
||||
}
|
||||
|
||||
// PtrSize returns the size of a pointer.
|
||||
func (scope *EvalScope) PtrSize() int {
|
||||
return scope.Thread.dbp.arch.PtrSize()
|
||||
return scope.Thread.dbp.bi.arch.PtrSize()
|
||||
}
|
||||
|
||||
// ChanRecvBlocked returns whether the goroutine is blocked on
|
||||
@ -367,7 +367,7 @@ func (gvar *Variable) parseG() (*G, error) {
|
||||
_, deref := gvar.RealType.(*dwarf.PtrType)
|
||||
|
||||
if deref {
|
||||
gaddrbytes, err := mem.readMemory(uintptr(gaddr), dbp.arch.PtrSize())
|
||||
gaddrbytes, err := mem.readMemory(uintptr(gaddr), dbp.bi.arch.PtrSize())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error derefing *G %s", err)
|
||||
}
|
||||
@ -405,7 +405,7 @@ func (gvar *Variable) parseG() (*G, error) {
|
||||
}
|
||||
|
||||
status, _ := constant.Int64Val(gvar.fieldVariable("atomicstatus").Value)
|
||||
f, l, fn := gvar.dbp.goSymTable.PCToLine(uint64(pc))
|
||||
f, l, fn := gvar.dbp.bi.goSymTable.PCToLine(uint64(pc))
|
||||
g := &G{
|
||||
ID: int(id),
|
||||
GoPC: uint64(gopc),
|
||||
@ -498,7 +498,7 @@ func (g *G) UserCurrent() Location {
|
||||
// Go returns the location of the 'go' statement
|
||||
// that spawned this goroutine.
|
||||
func (g *G) Go() Location {
|
||||
f, l, fn := g.dbp.goSymTable.PCToLine(g.GoPC)
|
||||
f, l, fn := g.dbp.bi.goSymTable.PCToLine(g.GoPC)
|
||||
return Location{PC: g.GoPC, File: f, Line: l, Fn: fn}
|
||||
}
|
||||
|
||||
@ -586,7 +586,7 @@ func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry, cfg LoadCon
|
||||
|
||||
func (scope *EvalScope) extractVarInfo(varName string) (*Variable, error) {
|
||||
reader := scope.DwarfReader()
|
||||
off, err := scope.Thread.dbp.findFunctionDebugInfo(scope.PC)
|
||||
off, err := scope.Thread.dbp.bi.findFunctionDebugInfo(scope.PC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1207,7 +1207,7 @@ func (v *Variable) writeBool(value bool) error {
|
||||
}
|
||||
|
||||
func (v *Variable) readFunctionPtr() {
|
||||
val, err := v.mem.readMemory(v.Addr, v.dbp.arch.PtrSize())
|
||||
val, err := v.mem.readMemory(v.Addr, v.dbp.bi.arch.PtrSize())
|
||||
if err != nil {
|
||||
v.Unreadable = err
|
||||
return
|
||||
@ -1221,14 +1221,14 @@ func (v *Variable) readFunctionPtr() {
|
||||
return
|
||||
}
|
||||
|
||||
val, err = v.mem.readMemory(fnaddr, v.dbp.arch.PtrSize())
|
||||
val, err = v.mem.readMemory(fnaddr, v.dbp.bi.arch.PtrSize())
|
||||
if err != nil {
|
||||
v.Unreadable = err
|
||||
return
|
||||
}
|
||||
|
||||
v.Base = uintptr(binary.LittleEndian.Uint64(val))
|
||||
fn := v.dbp.goSymTable.PCToFunc(uint64(v.Base))
|
||||
fn := v.dbp.bi.goSymTable.PCToFunc(uint64(v.Base))
|
||||
if fn == nil {
|
||||
v.Unreadable = fmt.Errorf("could not find function for %#v", v.Base)
|
||||
return
|
||||
@ -1603,7 +1603,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
|
||||
return
|
||||
}
|
||||
|
||||
typ, err = v.dbp.findType(typename)
|
||||
typ, err = v.dbp.bi.findType(typename)
|
||||
if err != nil {
|
||||
v.Unreadable = fmt.Errorf("interface type %q not found for %#x: %v", typename, data.Addr, err)
|
||||
return
|
||||
@ -1627,7 +1627,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
|
||||
return
|
||||
}
|
||||
|
||||
typ, err = v.dbp.findTypeExpr(t)
|
||||
typ, err = v.dbp.bi.findTypeExpr(t)
|
||||
if err != nil {
|
||||
v.Unreadable = fmt.Errorf("interface type %q not found for %#x: %v", typename, data.Addr, err)
|
||||
return
|
||||
@ -1637,7 +1637,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
|
||||
if kind&kindDirectIface == 0 {
|
||||
realtyp := resolveTypedef(typ)
|
||||
if _, isptr := realtyp.(*dwarf.PtrType); !isptr {
|
||||
typ = v.dbp.pointerTo(typ)
|
||||
typ = pointerTo(typ, v.dbp.bi.arch)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1655,7 +1655,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
|
||||
// Fetches all variables of a specific type in the current function scope
|
||||
func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) {
|
||||
reader := scope.DwarfReader()
|
||||
off, err := scope.Thread.dbp.findFunctionDebugInfo(scope.PC)
|
||||
off, err := scope.Thread.dbp.bi.findFunctionDebugInfo(scope.PC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ package target
|
||||
import (
|
||||
"debug/gosym"
|
||||
"go/ast"
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
@ -22,23 +21,14 @@ type Info interface {
|
||||
Pid() int
|
||||
Exited() bool
|
||||
Running() bool
|
||||
BinInfo() *proc.BinaryInfo
|
||||
|
||||
BinaryInfo
|
||||
ThreadInfo
|
||||
GoroutineInfo
|
||||
}
|
||||
|
||||
// BinaryInfo is an interface for accessing information on the binary file
|
||||
// and the contents of binary sections.
|
||||
type BinaryInfo interface {
|
||||
LastModified() time.Time
|
||||
Sources() map[string]*gosym.Obj
|
||||
FindFileLocation(fileName string, lineNumber int) (uint64, error)
|
||||
FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error)
|
||||
Funcs() []gosym.Func
|
||||
Types() ([]string, error)
|
||||
PCToLine(uint64) (string, int, *gosym.Func)
|
||||
FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error)
|
||||
FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error)
|
||||
}
|
||||
|
||||
// ThreadInfo is an interface for getting information on active threads
|
||||
@ -82,3 +72,5 @@ type BreakpointManipulation interface {
|
||||
type VariableEval interface {
|
||||
ConvertEvalScope(gid, frame int) (*proc.EvalScope, error)
|
||||
}
|
||||
|
||||
var _ Interface = &proc.Process{}
|
||||
|
||||
@ -68,7 +68,7 @@ func New(config *Config) (*Debugger, error) {
|
||||
log.Printf("launching process with args: %v", d.config.ProcessArgs)
|
||||
p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
|
||||
if err != nil {
|
||||
if err != proc.NotExecutableErr && err != proc.UnsupportedArchErr {
|
||||
if err != proc.NotExecutableErr && err != proc.UnsupportedLinuxArchErr && err != proc.UnsupportedWindowsArchErr && err != proc.UnsupportedDarwinArchErr {
|
||||
err = fmt.Errorf("could not launch process: %s", err)
|
||||
}
|
||||
return nil, err
|
||||
@ -87,7 +87,7 @@ func (d *Debugger) ProcessPid() int {
|
||||
// LastModified returns the time that the process' executable was last
|
||||
// modified.
|
||||
func (d *Debugger) LastModified() time.Time {
|
||||
return d.target.LastModified()
|
||||
return d.target.BinInfo().LastModified()
|
||||
}
|
||||
|
||||
// Detach detaches from the target process.
|
||||
@ -223,7 +223,7 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
|
||||
if runtime.GOOS == "windows" {
|
||||
// Accept fileName which is case-insensitive and slash-insensitive match
|
||||
fileNameNormalized := strings.ToLower(filepath.ToSlash(fileName))
|
||||
for symFile := range d.target.Sources() {
|
||||
for symFile := range d.target.BinInfo().Sources() {
|
||||
if fileNameNormalized == strings.ToLower(filepath.ToSlash(symFile)) {
|
||||
fileName = symFile
|
||||
break
|
||||
@ -531,7 +531,7 @@ func (d *Debugger) Sources(filter string) ([]string, error) {
|
||||
}
|
||||
|
||||
files := []string{}
|
||||
for f := range d.target.Sources() {
|
||||
for f := range d.target.BinInfo().Sources() {
|
||||
if regex.Match([]byte(f)) {
|
||||
files = append(files, f)
|
||||
}
|
||||
@ -544,7 +544,7 @@ func (d *Debugger) Functions(filter string) ([]string, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
return regexFilterFuncs(filter, d.target.Funcs())
|
||||
return regexFilterFuncs(filter, d.target.BinInfo().Funcs())
|
||||
}
|
||||
|
||||
func (d *Debugger) Types(filter string) ([]string, error) {
|
||||
@ -556,7 +556,7 @@ func (d *Debugger) Types(filter string) ([]string, error) {
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
|
||||
types, err := d.target.Types()
|
||||
types, err := d.target.BinInfo().Types()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -785,7 +785,7 @@ func (d *Debugger) FindLocation(scope api.EvalScope, locStr string) ([]api.Locat
|
||||
|
||||
locs, err := loc.Find(d, s, locStr)
|
||||
for i := range locs {
|
||||
file, line, fn := d.target.PCToLine(locs[i].PC)
|
||||
file, line, fn := d.target.BinInfo().PCToLine(locs[i].PC)
|
||||
locs[i].File = file
|
||||
locs[i].Line = line
|
||||
locs[i].Function = api.ConvertFunction(fn)
|
||||
@ -800,7 +800,7 @@ func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavo
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
if endPC == 0 {
|
||||
_, _, fn := d.target.PCToLine(startPC)
|
||||
_, _, fn := d.target.BinInfo().PCToLine(startPC)
|
||||
if fn == nil {
|
||||
return nil, fmt.Errorf("Address 0x%x does not belong to any function", startPC)
|
||||
}
|
||||
|
||||
@ -243,7 +243,7 @@ func (spec *FuncLocationSpec) Match(sym *gosym.Sym) bool {
|
||||
}
|
||||
|
||||
func (loc *RegexLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
funcs := d.target.Funcs()
|
||||
funcs := d.target.BinInfo().Funcs()
|
||||
matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -278,7 +278,7 @@ func (loc *AddrLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr str
|
||||
addr, _ := constant.Uint64Val(v.Value)
|
||||
return []api.Location{{PC: addr}}, nil
|
||||
case reflect.Func:
|
||||
_, _, fn := d.target.PCToLine(uint64(v.Base))
|
||||
_, _, fn := d.target.BinInfo().PCToLine(uint64(v.Base))
|
||||
pc, err := d.target.FirstPCAfterPrologue(fn, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -327,8 +327,8 @@ func (ale AmbiguousLocationError) Error() string {
|
||||
}
|
||||
|
||||
func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
funcs := d.target.Funcs()
|
||||
files := d.target.Sources()
|
||||
funcs := d.target.BinInfo().Funcs()
|
||||
files := d.target.BinInfo().Sources()
|
||||
|
||||
candidates := []string{}
|
||||
for file := range files {
|
||||
@ -390,7 +390,7 @@ func (loc *OffsetLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr s
|
||||
if scope == nil {
|
||||
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
||||
}
|
||||
file, line, fn := d.target.PCToLine(scope.PC)
|
||||
file, line, fn := d.target.BinInfo().PCToLine(scope.PC)
|
||||
if fn == nil {
|
||||
return nil, fmt.Errorf("could not determine current location")
|
||||
}
|
||||
@ -402,7 +402,7 @@ func (loc *LineLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr str
|
||||
if scope == nil {
|
||||
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
||||
}
|
||||
file, _, fn := d.target.PCToLine(scope.PC)
|
||||
file, _, fn := d.target.BinInfo().PCToLine(scope.PC)
|
||||
if fn == nil {
|
||||
return nil, fmt.Errorf("could not determine current location")
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user