提交 436a3c21 编写于 作者: A Alessandro Arzilli 提交者: Derek Parker

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
上级 7b19fe9e
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":
......
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
}
......
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 {
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
bi BinaryInfo
pid int // Process Pid
Process *os.Process // Pointer to process struct for the actual process we are debugging
// Breakpoint table, holds information on breakpoints.
// Maps instruction address to Breakpoint struct.
......@@ -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 {
......@@ -81,19 +62,14 @@ var NotExecutableErr = errors.New("not an executable file")
// `handlePtraceFuncs`.
func New(pid int) *Process {
dbp := &Process{
pid: pid,
threads: make(map[int]*Thread),
breakpoints: make(map[uint64]*Breakpoint),
firstStart: true,
os: new(OSProcessDetails),
ptraceChan: make(chan func()),
ptraceDoneChan: make(chan interface{}),
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()
pid: pid,
threads: make(map[int]*Thread),
breakpoints: make(map[uint64]*Breakpoint),
firstStart: true,
os: new(OSProcessDetails),
ptraceChan: make(chan func()),
ptraceDoneChan: make(chan interface{}),
bi: NewBinaryInfo(runtime.GOOS, runtime.GOARCH),
}
go dbp.handlePtraceFuncs()
return dbp
......@@ -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)))
}
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
path = C.GoString(C.find_executable(C.int(pid)))
}
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()
......
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,11 +474,13 @@ func (dbp *Process) resume() error {
return nil
}
func (dbp *Process) detach() error {
for _, thread := range dbp.threads {
_, err := _ResumeThread(thread.os.hThread)
if err != nil {
return err
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")
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册