pkg/prog: Improve support for external debug info
Adds a config file option to allow specifying a list of directories to search in when looking for seperate external debug info files. Fixes #1353
This commit is contained in:
parent
a2346ef69a
commit
51c342c6b7
@ -563,15 +563,16 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
|||||||
switch APIVersion {
|
switch APIVersion {
|
||||||
case 1, 2:
|
case 1, 2:
|
||||||
server = rpccommon.NewServer(&service.Config{
|
server = rpccommon.NewServer(&service.Config{
|
||||||
Listener: listener,
|
Listener: listener,
|
||||||
ProcessArgs: processArgs,
|
ProcessArgs: processArgs,
|
||||||
AttachPid: attachPid,
|
AttachPid: attachPid,
|
||||||
AcceptMulti: AcceptMulti,
|
AcceptMulti: AcceptMulti,
|
||||||
APIVersion: APIVersion,
|
APIVersion: APIVersion,
|
||||||
WorkingDir: WorkingDir,
|
WorkingDir: WorkingDir,
|
||||||
Backend: Backend,
|
Backend: Backend,
|
||||||
CoreFile: coreFile,
|
CoreFile: coreFile,
|
||||||
Foreground: Headless,
|
Foreground: Headless,
|
||||||
|
DebugInfoDirectories: conf.DebugInfoDirectories,
|
||||||
|
|
||||||
DisconnectChan: disconnectChan,
|
DisconnectChan: disconnectChan,
|
||||||
})
|
})
|
||||||
|
@ -43,10 +43,14 @@ type Config struct {
|
|||||||
// If ShowLocationExpr is true whatis will print the DWARF location
|
// If ShowLocationExpr is true whatis will print the DWARF location
|
||||||
// expression for its argument.
|
// expression for its argument.
|
||||||
ShowLocationExpr bool `yaml:"show-location-expr"`
|
ShowLocationExpr bool `yaml:"show-location-expr"`
|
||||||
|
|
||||||
// Source list line-number color (3/4 bit color codes as defined
|
// Source list line-number color (3/4 bit color codes as defined
|
||||||
// here: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
|
// here: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
|
||||||
SourceListLineColor int `yaml:"source-list-line-color"`
|
SourceListLineColor int `yaml:"source-list-line-color"`
|
||||||
|
|
||||||
|
// DebugFileDirectories is the list of directories Delve will use
|
||||||
|
// in order to resolve external debug info files.
|
||||||
|
DebugInfoDirectories []string `yaml:"debug-info-directories"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadConfig attempts to populate a Config object from the config.yml file.
|
// LoadConfig attempts to populate a Config object from the config.yml file.
|
||||||
@ -160,6 +164,9 @@ substitute-path:
|
|||||||
|
|
||||||
# Uncomment the following line to make the whatis command also print the DWARF location expression of its argument.
|
# Uncomment the following line to make the whatis command also print the DWARF location expression of its argument.
|
||||||
# show-location-expr: true
|
# show-location-expr: true
|
||||||
|
|
||||||
|
# List of directories to use when searching for separate debug info files.
|
||||||
|
debug-info-directories: ["/usr/lib/debug/.build-id"]
|
||||||
`)
|
`)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -27,6 +28,8 @@ import (
|
|||||||
|
|
||||||
// BinaryInfo holds information on the binary being executed.
|
// BinaryInfo holds information on the binary being executed.
|
||||||
type BinaryInfo struct {
|
type BinaryInfo struct {
|
||||||
|
// Path on disk of the binary being executed.
|
||||||
|
Path string
|
||||||
// Architecture of this binary.
|
// Architecture of this binary.
|
||||||
Arch Arch
|
Arch Arch
|
||||||
|
|
||||||
@ -91,6 +94,9 @@ var ErrUnsupportedDarwinArch = errors.New("unsupported architecture - only darwi
|
|||||||
// position independant executable.
|
// position independant executable.
|
||||||
var ErrCouldNotDetermineRelocation = errors.New("could not determine the base address of a PIE")
|
var ErrCouldNotDetermineRelocation = errors.New("could not determine the base address of a PIE")
|
||||||
|
|
||||||
|
// ErrNoDebugInfoFound is returned when Delve cannot find the external debug information file.
|
||||||
|
var ErrNoDebugInfoFound = errors.New("could not find external debug info file")
|
||||||
|
|
||||||
const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
|
const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
|
||||||
|
|
||||||
type compileUnit struct {
|
type compileUnit struct {
|
||||||
@ -298,22 +304,22 @@ func NewBinaryInfo(goos, goarch string) *BinaryInfo {
|
|||||||
// LoadBinaryInfo will load and store the information from the binary at 'path'.
|
// LoadBinaryInfo will load and store the information from the binary at 'path'.
|
||||||
// It is expected this will be called in parallel with other initialization steps
|
// It is expected this will be called in parallel with other initialization steps
|
||||||
// so a sync.WaitGroup must be provided.
|
// so a sync.WaitGroup must be provided.
|
||||||
func (bi *BinaryInfo) LoadBinaryInfo(path string, entryPoint uint64, wg *sync.WaitGroup) error {
|
func (bi *BinaryInfo) LoadBinaryInfo(path string, entryPoint uint64, debugInfoDirs []string, wg *sync.WaitGroup) error {
|
||||||
fi, err := os.Stat(path)
|
fi, err := os.Stat(path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
bi.lastModified = fi.ModTime()
|
bi.lastModified = fi.ModTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bi.Path = path
|
||||||
switch bi.GOOS {
|
switch bi.GOOS {
|
||||||
case "linux":
|
case "linux":
|
||||||
return bi.LoadBinaryInfoElf(path, entryPoint, wg)
|
return bi.LoadBinaryInfoElf(path, entryPoint, debugInfoDirs, wg)
|
||||||
case "windows":
|
case "windows":
|
||||||
return bi.LoadBinaryInfoPE(path, entryPoint, wg)
|
return bi.LoadBinaryInfoPE(path, entryPoint, wg)
|
||||||
case "darwin":
|
case "darwin":
|
||||||
return bi.LoadBinaryInfoMacho(path, entryPoint, wg)
|
return bi.LoadBinaryInfoMacho(path, entryPoint, wg)
|
||||||
}
|
}
|
||||||
return errors.New("unsupported operating system")
|
return errors.New("unsupported operating system")
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GStructOffset returns the offset of the G
|
// GStructOffset returns the offset of the G
|
||||||
@ -563,35 +569,32 @@ func (e *ErrNoBuildIDNote) Error() string {
|
|||||||
// in GDB's documentation [1], and if found returns two handles, one
|
// in GDB's documentation [1], and if found returns two handles, one
|
||||||
// for the bare file, and another for its corresponding elf.File.
|
// for the bare file, and another for its corresponding elf.File.
|
||||||
// [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
|
// [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
|
||||||
func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File) (*os.File, *elf.File, error) {
|
//
|
||||||
buildid := exe.Section(".note.gnu.build-id")
|
// Alternatively, if the debug file cannot be found be the build-id, Delve
|
||||||
if buildid == nil {
|
// will look in directories specified by the debug-info-directories config value.
|
||||||
return nil, nil, &ErrNoBuildIDNote{}
|
func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File, debugInfoDirectories []string) (*os.File, *elf.File, error) {
|
||||||
|
var debugFilePath string
|
||||||
|
for _, dir := range debugInfoDirectories {
|
||||||
|
var potentialDebugFilePath string
|
||||||
|
if strings.Contains(dir, "build-id") {
|
||||||
|
desc1, desc2, err := parseBuildID(exe)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
potentialDebugFilePath = fmt.Sprintf("%s/%s/%s.debug", dir, desc1, desc2)
|
||||||
|
} else {
|
||||||
|
potentialDebugFilePath = fmt.Sprintf("%s/%s.debug", dir, filepath.Base(bi.Path))
|
||||||
|
}
|
||||||
|
_, err := os.Stat(potentialDebugFilePath)
|
||||||
|
if err == nil {
|
||||||
|
debugFilePath = potentialDebugFilePath
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if debugFilePath == "" {
|
||||||
br := buildid.Open()
|
return nil, nil, ErrNoDebugInfoFound
|
||||||
bh := new(buildIDHeader)
|
|
||||||
if err := binary.Read(br, binary.LittleEndian, bh); err != nil {
|
|
||||||
return nil, nil, errors.New("can't read build-id header: " + err.Error())
|
|
||||||
}
|
}
|
||||||
|
sepFile, err := os.OpenFile(debugFilePath, 0, os.ModePerm)
|
||||||
name := make([]byte, bh.Namesz)
|
|
||||||
if err := binary.Read(br, binary.LittleEndian, name); err != nil {
|
|
||||||
return nil, nil, errors.New("can't read build-id name: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.TrimSpace(string(name)) != "GNU\x00" {
|
|
||||||
return nil, nil, errors.New("invalid build-id signature")
|
|
||||||
}
|
|
||||||
|
|
||||||
descBinary := make([]byte, bh.Descsz)
|
|
||||||
if err := binary.Read(br, binary.LittleEndian, descBinary); err != nil {
|
|
||||||
return nil, nil, errors.New("can't read build-id desc: " + err.Error())
|
|
||||||
}
|
|
||||||
desc := hex.EncodeToString(descBinary)
|
|
||||||
|
|
||||||
debugPath := fmt.Sprintf("/usr/lib/debug/.build-id/%s/%s.debug", desc[:2], desc[2:])
|
|
||||||
sepFile, err := os.OpenFile(debugPath, 0, os.ModePerm)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.New("can't open separate debug file: " + err.Error())
|
return nil, nil, errors.New("can't open separate debug file: " + err.Error())
|
||||||
}
|
}
|
||||||
@ -599,19 +602,48 @@ func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File) (*os.File, *elf.File,
|
|||||||
elfFile, err := elf.NewFile(sepFile)
|
elfFile, err := elf.NewFile(sepFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sepFile.Close()
|
sepFile.Close()
|
||||||
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugPath, err.Error())
|
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if elfFile.Machine != elf.EM_X86_64 {
|
if elfFile.Machine != elf.EM_X86_64 {
|
||||||
sepFile.Close()
|
sepFile.Close()
|
||||||
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugPath, ErrUnsupportedLinuxArch.Error())
|
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, ErrUnsupportedLinuxArch.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return sepFile, elfFile, nil
|
return sepFile, elfFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseBuildID(exe *elf.File) (string, string, error) {
|
||||||
|
buildid := exe.Section(".note.gnu.build-id")
|
||||||
|
if buildid == nil {
|
||||||
|
return "", "", &ErrNoBuildIDNote{}
|
||||||
|
}
|
||||||
|
|
||||||
|
br := buildid.Open()
|
||||||
|
bh := new(buildIDHeader)
|
||||||
|
if err := binary.Read(br, binary.LittleEndian, bh); err != nil {
|
||||||
|
return "", "", errors.New("can't read build-id header: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
name := make([]byte, bh.Namesz)
|
||||||
|
if err := binary.Read(br, binary.LittleEndian, name); err != nil {
|
||||||
|
return "", "", errors.New("can't read build-id name: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(string(name)) != "GNU\x00" {
|
||||||
|
return "", "", errors.New("invalid build-id signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
descBinary := make([]byte, bh.Descsz)
|
||||||
|
if err := binary.Read(br, binary.LittleEndian, descBinary); err != nil {
|
||||||
|
return "", "", errors.New("can't read build-id desc: " + err.Error())
|
||||||
|
}
|
||||||
|
desc := hex.EncodeToString(descBinary)
|
||||||
|
return desc[:2], desc[2:], nil
|
||||||
|
}
|
||||||
|
|
||||||
// LoadBinaryInfoElf specifically loads information from an ELF binary.
|
// LoadBinaryInfoElf specifically loads information from an ELF binary.
|
||||||
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, entryPoint uint64, wg *sync.WaitGroup) error {
|
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, entryPoint uint64, debugInfoDirectories []string, wg *sync.WaitGroup) error {
|
||||||
exe, err := os.OpenFile(path, 0, os.ModePerm)
|
exe, err := os.OpenFile(path, 0, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -639,7 +671,7 @@ func (bi *BinaryInfo) LoadBinaryInfoElf(path string, entryPoint uint64, wg *sync
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
var sepFile *os.File
|
var sepFile *os.File
|
||||||
var serr error
|
var serr error
|
||||||
sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(elfFile)
|
sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(elfFile, debugInfoDirectories)
|
||||||
if serr != nil {
|
if serr != nil {
|
||||||
if _, ok := serr.(*ErrNoBuildIDNote); ok {
|
if _, ok := serr.(*ErrNoBuildIDNote); ok {
|
||||||
return err
|
return err
|
||||||
|
@ -185,14 +185,16 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// OpenCore will open the core file and return a Process struct.
|
// OpenCore will open the core file and return a Process struct.
|
||||||
func OpenCore(corePath, exePath string) (*Process, error) {
|
// If the DWARF information cannot be found in the binary, Delve will look
|
||||||
|
// for external debug files in the directories passed in.
|
||||||
|
func OpenCore(corePath, exePath string, debugInfoDirs []string) (*Process, error) {
|
||||||
p, err := readLinuxAMD64Core(corePath, exePath)
|
p, err := readLinuxAMD64Core(corePath, exePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
err = p.bi.LoadBinaryInfo(exePath, p.entryPoint, &wg)
|
err = p.bi.LoadBinaryInfo(exePath, p.entryPoint, debugInfoDirs, &wg)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = p.bi.LoadError()
|
err = p.bi.LoadError()
|
||||||
|
@ -172,7 +172,7 @@ func withCoreFile(t *testing.T, name, args string) *Process {
|
|||||||
}
|
}
|
||||||
corePath := cores[0]
|
corePath := cores[0]
|
||||||
|
|
||||||
p, err := OpenCore(corePath, fix.Path)
|
p, err := OpenCore(corePath, fix.Path, []string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("ReadCore(%q) failed: %v", corePath, err)
|
t.Errorf("ReadCore(%q) failed: %v", corePath, err)
|
||||||
pat, err := ioutil.ReadFile("/proc/sys/kernel/core_pattern")
|
pat, err := ioutil.ReadFile("/proc/sys/kernel/core_pattern")
|
||||||
|
@ -205,7 +205,7 @@ func New(process *os.Process) *Process {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Listen waits for a connection from the stub.
|
// Listen waits for a connection from the stub.
|
||||||
func (p *Process) Listen(listener net.Listener, path string, pid int) error {
|
func (p *Process) Listen(listener net.Listener, path string, pid int, debugInfoDirs []string) error {
|
||||||
acceptChan := make(chan net.Conn)
|
acceptChan := make(chan net.Conn)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -219,7 +219,7 @@ func (p *Process) Listen(listener net.Listener, path string, pid int) error {
|
|||||||
if conn == nil {
|
if conn == nil {
|
||||||
return errors.New("could not connect")
|
return errors.New("could not connect")
|
||||||
}
|
}
|
||||||
return p.Connect(conn, path, pid)
|
return p.Connect(conn, path, pid, debugInfoDirs)
|
||||||
case status := <-p.waitChan:
|
case status := <-p.waitChan:
|
||||||
listener.Close()
|
listener.Close()
|
||||||
return fmt.Errorf("stub exited while waiting for connection: %v", status)
|
return fmt.Errorf("stub exited while waiting for connection: %v", status)
|
||||||
@ -227,11 +227,11 @@ func (p *Process) Listen(listener net.Listener, path string, pid int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dial attempts to connect to the stub.
|
// Dial attempts to connect to the stub.
|
||||||
func (p *Process) Dial(addr string, path string, pid int) error {
|
func (p *Process) Dial(addr string, path string, pid int, debugInfoDirs []string) error {
|
||||||
for {
|
for {
|
||||||
conn, err := net.Dial("tcp", addr)
|
conn, err := net.Dial("tcp", addr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return p.Connect(conn, path, pid)
|
return p.Connect(conn, path, pid, debugInfoDirs)
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case status := <-p.waitChan:
|
case status := <-p.waitChan:
|
||||||
@ -248,7 +248,7 @@ func (p *Process) Dial(addr string, path string, pid int) error {
|
|||||||
// program and the PID of the target process, both are optional, however
|
// program and the PID of the target process, both are optional, however
|
||||||
// some stubs do not provide ways to determine path and pid automatically
|
// some stubs do not provide ways to determine path and pid automatically
|
||||||
// and Connect will be unable to function without knowing them.
|
// and Connect will be unable to function without knowing them.
|
||||||
func (p *Process) Connect(conn net.Conn, path string, pid int) error {
|
func (p *Process) Connect(conn net.Conn, path string, pid int, debugInfoDirs []string) error {
|
||||||
p.conn.conn = conn
|
p.conn.conn = conn
|
||||||
|
|
||||||
p.conn.pid = pid
|
p.conn.pid = pid
|
||||||
@ -312,7 +312,7 @@ func (p *Process) Connect(conn net.Conn, path string, pid int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
err = p.bi.LoadBinaryInfo(path, entryPoint, &wg)
|
err = p.bi.LoadBinaryInfo(path, entryPoint, debugInfoDirs, &wg)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = p.bi.LoadError()
|
err = p.bi.LoadError()
|
||||||
@ -405,7 +405,7 @@ func getLdEnvVars() []string {
|
|||||||
// LLDBLaunch starts an instance of lldb-server and connects to it, asking
|
// LLDBLaunch starts an instance of lldb-server and connects to it, asking
|
||||||
// it to launch the specified target program with the specified arguments
|
// it to launch the specified target program with the specified arguments
|
||||||
// (cmd) on the specified directory wd.
|
// (cmd) on the specified directory wd.
|
||||||
func LLDBLaunch(cmd []string, wd string, foreground bool) (*Process, error) {
|
func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*Process, error) {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
return nil, ErrUnsupportedOS
|
return nil, ErrUnsupportedOS
|
||||||
@ -482,9 +482,9 @@ func LLDBLaunch(cmd []string, wd string, foreground bool) (*Process, error) {
|
|||||||
p.conn.isDebugserver = isDebugserver
|
p.conn.isDebugserver = isDebugserver
|
||||||
|
|
||||||
if listener != nil {
|
if listener != nil {
|
||||||
err = p.Listen(listener, cmd[0], 0)
|
err = p.Listen(listener, cmd[0], 0, debugInfoDirs)
|
||||||
} else {
|
} else {
|
||||||
err = p.Dial(port, cmd[0], 0)
|
err = p.Dial(port, cmd[0], 0, debugInfoDirs)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -497,13 +497,13 @@ func LLDBLaunch(cmd []string, wd string, foreground bool) (*Process, error) {
|
|||||||
// Path is path to the target's executable, path only needs to be specified
|
// Path is path to the target's executable, path only needs to be specified
|
||||||
// for some stubs that do not provide an automated way of determining it
|
// for some stubs that do not provide an automated way of determining it
|
||||||
// (for example debugserver).
|
// (for example debugserver).
|
||||||
func LLDBAttach(pid int, path string) (*Process, error) {
|
func LLDBAttach(pid int, path string, debugInfoDirs []string) (*Process, error) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
return nil, ErrUnsupportedOS
|
return nil, ErrUnsupportedOS
|
||||||
}
|
}
|
||||||
|
|
||||||
isDebugserver := false
|
isDebugserver := false
|
||||||
var proc *exec.Cmd
|
var process *exec.Cmd
|
||||||
var listener net.Listener
|
var listener net.Listener
|
||||||
var port string
|
var port string
|
||||||
if _, err := os.Stat(debugserverExecutable); err == nil {
|
if _, err := os.Stat(debugserverExecutable); err == nil {
|
||||||
@ -512,32 +512,32 @@ func LLDBAttach(pid int, path string) (*Process, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
proc = exec.Command(debugserverExecutable, "-R", fmt.Sprintf("127.0.0.1:%d", listener.Addr().(*net.TCPAddr).Port), "--attach="+strconv.Itoa(pid))
|
process = exec.Command(debugserverExecutable, "-R", fmt.Sprintf("127.0.0.1:%d", listener.Addr().(*net.TCPAddr).Port), "--attach="+strconv.Itoa(pid))
|
||||||
} else {
|
} else {
|
||||||
if _, err := exec.LookPath("lldb-server"); err != nil {
|
if _, err := exec.LookPath("lldb-server"); err != nil {
|
||||||
return nil, &ErrBackendUnavailable{}
|
return nil, &ErrBackendUnavailable{}
|
||||||
}
|
}
|
||||||
port = unusedPort()
|
port = unusedPort()
|
||||||
proc = exec.Command("lldb-server", "gdbserver", "--attach", strconv.Itoa(pid), port)
|
process = exec.Command("lldb-server", "gdbserver", "--attach", strconv.Itoa(pid), port)
|
||||||
}
|
}
|
||||||
|
|
||||||
proc.Stdout = os.Stdout
|
process.Stdout = os.Stdout
|
||||||
proc.Stderr = os.Stderr
|
process.Stderr = os.Stderr
|
||||||
|
|
||||||
proc.SysProcAttr = sysProcAttr(false)
|
process.SysProcAttr = sysProcAttr(false)
|
||||||
|
|
||||||
err := proc.Start()
|
err := process.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
p := New(proc.Process)
|
p := New(process.Process)
|
||||||
p.conn.isDebugserver = isDebugserver
|
p.conn.isDebugserver = isDebugserver
|
||||||
|
|
||||||
if listener != nil {
|
if listener != nil {
|
||||||
err = p.Listen(listener, path, pid)
|
err = p.Listen(listener, path, pid, debugInfoDirs)
|
||||||
} else {
|
} else {
|
||||||
err = p.Dial(port, path, pid)
|
err = p.Dial(port, path, pid, debugInfoDirs)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -53,7 +53,7 @@ func Record(cmd []string, wd string, quiet bool) (tracedir string, err error) {
|
|||||||
|
|
||||||
// Replay starts an instance of rr in replay mode, with the specified trace
|
// Replay starts an instance of rr in replay mode, with the specified trace
|
||||||
// directory, and connects to it.
|
// directory, and connects to it.
|
||||||
func Replay(tracedir string, quiet bool) (*Process, error) {
|
func Replay(tracedir string, quiet bool, debugInfoDirs []string) (*Process, error) {
|
||||||
if err := checkRRAvailabe(); err != nil {
|
if err := checkRRAvailabe(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ func Replay(tracedir string, quiet bool) (*Process, error) {
|
|||||||
|
|
||||||
p := New(rrcmd.Process)
|
p := New(rrcmd.Process)
|
||||||
p.tracedir = tracedir
|
p.tracedir = tracedir
|
||||||
err = p.Dial(init.port, init.exe, 0)
|
err = p.Dial(init.port, init.exe, 0, debugInfoDirs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rrcmd.Process.Kill()
|
rrcmd.Process.Kill()
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -257,11 +257,11 @@ func splitQuotedFields(in string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RecordAndReplay acts like calling Record and then Replay.
|
// RecordAndReplay acts like calling Record and then Replay.
|
||||||
func RecordAndReplay(cmd []string, wd string, quiet bool) (p *Process, tracedir string, err error) {
|
func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string) (p *Process, tracedir string, err error) {
|
||||||
tracedir, err = Record(cmd, wd, quiet)
|
tracedir, err = Record(cmd, wd, quiet)
|
||||||
if tracedir == "" {
|
if tracedir == "" {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
p, err = Replay(tracedir, quiet)
|
p, err = Replay(tracedir, quiet, debugInfoDirs)
|
||||||
return p, tracedir, err
|
return p, tracedir, err
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ func withTestRecording(name string, t testing.TB, fn func(p *gdbserial.Process,
|
|||||||
t.Skip("test skipped, rr not found")
|
t.Skip("test skipped, rr not found")
|
||||||
}
|
}
|
||||||
t.Log("recording")
|
t.Log("recording")
|
||||||
p, tracedir, err := gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true)
|
p, tracedir, err := gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true, []string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Launch():", err)
|
t.Fatal("Launch():", err)
|
||||||
}
|
}
|
||||||
|
@ -12,12 +12,12 @@ import (
|
|||||||
var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")
|
var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")
|
||||||
|
|
||||||
// Launch returns ErrNativeBackendDisabled.
|
// Launch returns ErrNativeBackendDisabled.
|
||||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, error) {
|
||||||
return nil, ErrNativeBackendDisabled
|
return nil, ErrNativeBackendDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach returns ErrNativeBackendDisabled.
|
// Attach returns ErrNativeBackendDisabled.
|
||||||
func Attach(pid int) (*Process, error) {
|
func Attach(pid int, _ []string) (*Process, error) {
|
||||||
return nil, ErrNativeBackendDisabled
|
return nil, ErrNativeBackendDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ func (dbp *Process) Breakpoints() *proc.BreakpointMap {
|
|||||||
// * Dwarf .debug_frame section
|
// * Dwarf .debug_frame section
|
||||||
// * Dwarf .debug_line section
|
// * Dwarf .debug_line section
|
||||||
// * Go symbol table.
|
// * Go symbol table.
|
||||||
func (dbp *Process) LoadInformation(path string) error {
|
func (dbp *Process) LoadInformation(path string, debugInfoDirs []string) error {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
path = findExecutable(path, dbp.pid)
|
path = findExecutable(path, dbp.pid)
|
||||||
@ -201,7 +201,7 @@ func (dbp *Process) LoadInformation(path string) error {
|
|||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go dbp.loadProcessInformation(&wg)
|
go dbp.loadProcessInformation(&wg)
|
||||||
err = dbp.bi.LoadBinaryInfo(path, entryPoint, &wg)
|
err = dbp.bi.LoadBinaryInfo(path, entryPoint, debugInfoDirs, &wg)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = dbp.bi.LoadError()
|
err = dbp.bi.LoadError()
|
||||||
@ -380,8 +380,8 @@ func (dbp *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns a new Process struct.
|
// Returns a new Process struct.
|
||||||
func initializeDebugProcess(dbp *Process, path string) (*Process, error) {
|
func initializeDebugProcess(dbp *Process, path string, debugInfoDirs []string) (*Process, error) {
|
||||||
err := dbp.LoadInformation(path)
|
err := dbp.LoadInformation(path, debugInfoDirs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return dbp, err
|
return dbp, err
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ type OSProcessDetails struct {
|
|||||||
// custom fork/exec process in order to take advantage of
|
// custom fork/exec process in order to take advantage of
|
||||||
// PT_SIGEXC on Darwin which will turn Unix signals into
|
// PT_SIGEXC on Darwin which will turn Unix signals into
|
||||||
// Mach exceptions.
|
// Mach exceptions.
|
||||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, error) {
|
||||||
// check that the argument to Launch is an executable file
|
// check that the argument to Launch is an executable file
|
||||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
||||||
return nil, proc.ErrNotExecutable
|
return nil, proc.ErrNotExecutable
|
||||||
@ -119,7 +119,7 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbp.os.initialized = true
|
dbp.os.initialized = true
|
||||||
dbp, err = initializeDebugProcess(dbp, argv0Go)
|
dbp, err = initializeDebugProcess(dbp, argv0Go, []string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -132,7 +132,7 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attach to an existing process with the given PID.
|
// Attach to an existing process with the given PID.
|
||||||
func Attach(pid int) (*Process, error) {
|
func Attach(pid int, _ []string) (*Process, error) {
|
||||||
dbp := New(pid)
|
dbp := New(pid)
|
||||||
|
|
||||||
kret := C.acquire_mach_task(C.int(pid),
|
kret := C.acquire_mach_task(C.int(pid),
|
||||||
@ -155,7 +155,7 @@ func Attach(pid int) (*Process, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dbp, err = initializeDebugProcess(dbp, "")
|
dbp, err = initializeDebugProcess(dbp, "", []string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dbp.Detach(false)
|
dbp.Detach(false)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -46,7 +46,9 @@ type OSProcessDetails struct {
|
|||||||
// Launch creates and begins debugging a new process. First entry in
|
// Launch creates and begins debugging a new process. First entry in
|
||||||
// `cmd` is the program to run, and then rest are the arguments
|
// `cmd` is the program to run, and then rest are the arguments
|
||||||
// to be supplied to that process. `wd` is working directory of the program.
|
// to be supplied to that process. `wd` is working directory of the program.
|
||||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
// If the DWARF information cannot be found in the binary, Delve will look
|
||||||
|
// for external debug files in the directories passed in.
|
||||||
|
func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*Process, error) {
|
||||||
var (
|
var (
|
||||||
process *exec.Cmd
|
process *exec.Cmd
|
||||||
err error
|
err error
|
||||||
@ -88,11 +90,13 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
||||||
}
|
}
|
||||||
return initializeDebugProcess(dbp, process.Path)
|
return initializeDebugProcess(dbp, process.Path, debugInfoDirs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach to an existing process with the given PID.
|
// Attach to an existing process with the given PID. Once attached, if
|
||||||
func Attach(pid int) (*Process, error) {
|
// the DWARF information cannot be found in the binary, Delve will look
|
||||||
|
// for external debug files in the directories passed in.
|
||||||
|
func Attach(pid int, debugInfoDirs []string) (*Process, error) {
|
||||||
dbp := New(pid)
|
dbp := New(pid)
|
||||||
dbp.common = proc.NewCommonProcess(true)
|
dbp.common = proc.NewCommonProcess(true)
|
||||||
|
|
||||||
@ -106,7 +110,7 @@ func Attach(pid int) (*Process, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dbp, err = initializeDebugProcess(dbp, "")
|
dbp, err = initializeDebugProcess(dbp, "", debugInfoDirs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
dbp.Detach(false)
|
dbp.Detach(false)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -37,7 +37,7 @@ func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Launch creates and begins debugging a new process.
|
// Launch creates and begins debugging a new process.
|
||||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, error) {
|
||||||
argv0Go, err := filepath.Abs(cmd[0])
|
argv0Go, err := filepath.Abs(cmd[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -115,7 +115,7 @@ func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return initializeDebugProcess(dbp, exepath)
|
return initializeDebugProcess(dbp, exepath, []string{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// findExePath searches for process pid, and returns its executable path.
|
// findExePath searches for process pid, and returns its executable path.
|
||||||
@ -153,7 +153,7 @@ func findExePath(pid int) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attach to an existing process with the given PID.
|
// Attach to an existing process with the given PID.
|
||||||
func Attach(pid int) (*Process, error) {
|
func Attach(pid int, _ []string) (*Process, error) {
|
||||||
// TODO: Probably should have SeDebugPrivilege before starting here.
|
// TODO: Probably should have SeDebugPrivilege before starting here.
|
||||||
err := _DebugActiveProcess(uint32(pid))
|
err := _DebugActiveProcess(uint32(pid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
39
pkg/proc/proc_linux_test.go
Normal file
39
pkg/proc/proc_linux_test.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package proc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/derekparker/delve/pkg/proc/native"
|
||||||
|
protest "github.com/derekparker/delve/pkg/proc/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadingExternalDebugInfo(t *testing.T) {
|
||||||
|
fixture := protest.BuildFixture("locationsprog", 0)
|
||||||
|
defer os.Remove(fixture.Path)
|
||||||
|
stripAndCopyDebugInfo(fixture, t)
|
||||||
|
p, err := native.Launch(append([]string{fixture.Path}, ""), "", false, []string{filepath.Dir(fixture.Path)})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
p.Detach(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stripAndCopyDebugInfo(f protest.Fixture, t *testing.T) {
|
||||||
|
name := filepath.Base(f.Path)
|
||||||
|
// Copy the debug information to an external file.
|
||||||
|
copyCmd := exec.Command("objcopy", "--only-keep-debug", name, name+".debug")
|
||||||
|
copyCmd.Dir = filepath.Dir(f.Path)
|
||||||
|
if err := copyCmd.Run(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip the original binary of the debug information.
|
||||||
|
stripCmd := exec.Command("strip", "--strip-debug", "--strip-unneeded", name)
|
||||||
|
stripCmd.Dir = filepath.Dir(f.Path)
|
||||||
|
if err := stripCmd.Run(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
@ -66,13 +66,13 @@ func withTestProcessArgs(name string, t testing.TB, wd string, args []string, bu
|
|||||||
|
|
||||||
switch testBackend {
|
switch testBackend {
|
||||||
case "native":
|
case "native":
|
||||||
p, err = native.Launch(append([]string{fixture.Path}, args...), wd, false)
|
p, err = native.Launch(append([]string{fixture.Path}, args...), wd, false, []string{})
|
||||||
case "lldb":
|
case "lldb":
|
||||||
p, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd, false)
|
p, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd, false, []string{})
|
||||||
case "rr":
|
case "rr":
|
||||||
protest.MustHaveRecordingAllowed(t)
|
protest.MustHaveRecordingAllowed(t)
|
||||||
t.Log("recording")
|
t.Log("recording")
|
||||||
p, tracedir, err = gdbserial.RecordAndReplay(append([]string{fixture.Path}, args...), wd, true)
|
p, tracedir, err = gdbserial.RecordAndReplay(append([]string{fixture.Path}, args...), wd, true, []string{})
|
||||||
t.Logf("replaying %q", tracedir)
|
t.Logf("replaying %q", tracedir)
|
||||||
default:
|
default:
|
||||||
t.Fatal("unknown backend")
|
t.Fatal("unknown backend")
|
||||||
@ -2048,9 +2048,9 @@ func TestIssue509(t *testing.T) {
|
|||||||
|
|
||||||
switch testBackend {
|
switch testBackend {
|
||||||
case "native":
|
case "native":
|
||||||
_, err = native.Launch([]string{exepath}, ".", false)
|
_, err = native.Launch([]string{exepath}, ".", false, []string{})
|
||||||
case "lldb":
|
case "lldb":
|
||||||
_, err = gdbserial.LLDBLaunch([]string{exepath}, ".", false)
|
_, err = gdbserial.LLDBLaunch([]string{exepath}, ".", false, []string{})
|
||||||
default:
|
default:
|
||||||
t.Skip("test not valid for this backend")
|
t.Skip("test not valid for this backend")
|
||||||
}
|
}
|
||||||
@ -2090,9 +2090,9 @@ func TestUnsupportedArch(t *testing.T) {
|
|||||||
|
|
||||||
switch testBackend {
|
switch testBackend {
|
||||||
case "native":
|
case "native":
|
||||||
p, err = native.Launch([]string{outfile}, ".", false)
|
p, err = native.Launch([]string{outfile}, ".", false, []string{})
|
||||||
case "lldb":
|
case "lldb":
|
||||||
p, err = gdbserial.LLDBLaunch([]string{outfile}, ".", false)
|
p, err = gdbserial.LLDBLaunch([]string{outfile}, ".", false, []string{})
|
||||||
default:
|
default:
|
||||||
t.Skip("test not valid for this backend")
|
t.Skip("test not valid for this backend")
|
||||||
}
|
}
|
||||||
@ -2839,13 +2839,13 @@ func TestAttachDetach(t *testing.T) {
|
|||||||
|
|
||||||
switch testBackend {
|
switch testBackend {
|
||||||
case "native":
|
case "native":
|
||||||
p, err = native.Attach(cmd.Process.Pid)
|
p, err = native.Attach(cmd.Process.Pid, []string{})
|
||||||
case "lldb":
|
case "lldb":
|
||||||
path := ""
|
path := ""
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
path = fixture.Path
|
path = fixture.Path
|
||||||
}
|
}
|
||||||
p, err = gdbserial.LLDBAttach(cmd.Process.Pid, path)
|
p, err = gdbserial.LLDBAttach(cmd.Process.Pid, path, []string{})
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unknown backend %q", testBackend)
|
err = fmt.Errorf("unknown backend %q", testBackend)
|
||||||
}
|
}
|
||||||
@ -3141,13 +3141,13 @@ func TestAttachStripped(t *testing.T) {
|
|||||||
|
|
||||||
switch testBackend {
|
switch testBackend {
|
||||||
case "native":
|
case "native":
|
||||||
p, err = native.Attach(cmd.Process.Pid)
|
p, err = native.Attach(cmd.Process.Pid, []string{})
|
||||||
case "lldb":
|
case "lldb":
|
||||||
path := ""
|
path := ""
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
path = fixture.Path
|
path = fixture.Path
|
||||||
}
|
}
|
||||||
p, err = gdbserial.LLDBAttach(cmd.Process.Pid, path)
|
p, err = gdbserial.LLDBAttach(cmd.Process.Pid, path, []string{})
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unknown backend %q", testBackend)
|
t.Fatalf("unknown backend %q", testBackend)
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,10 @@ type Config struct {
|
|||||||
// CoreFile specifies the path to the core dump to open.
|
// CoreFile specifies the path to the core dump to open.
|
||||||
CoreFile string
|
CoreFile string
|
||||||
|
|
||||||
|
// DebugInfoDirectories is the list of directories to look for
|
||||||
|
// when resolving external debug info files.
|
||||||
|
DebugInfoDirectories []string
|
||||||
|
|
||||||
// Selects server backend.
|
// Selects server backend.
|
||||||
Backend string
|
Backend string
|
||||||
|
|
||||||
|
@ -65,6 +65,10 @@ type Config struct {
|
|||||||
|
|
||||||
// Foreground lets target process access stdin.
|
// Foreground lets target process access stdin.
|
||||||
Foreground bool
|
Foreground bool
|
||||||
|
|
||||||
|
// DebugInfoDirectories is the list of directories to look for
|
||||||
|
// when resolving external debug info files.
|
||||||
|
DebugInfoDirectories []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new Debugger. ProcessArgs specify the commandline arguments for the
|
// New creates a new Debugger. ProcessArgs specify the commandline arguments for the
|
||||||
@ -102,10 +106,10 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
|||||||
switch d.config.Backend {
|
switch d.config.Backend {
|
||||||
case "rr":
|
case "rr":
|
||||||
d.log.Infof("opening trace %s", d.config.CoreFile)
|
d.log.Infof("opening trace %s", d.config.CoreFile)
|
||||||
p, err = gdbserial.Replay(d.config.CoreFile, false)
|
p, err = gdbserial.Replay(d.config.CoreFile, false, d.config.DebugInfoDirectories)
|
||||||
default:
|
default:
|
||||||
d.log.Infof("opening core file %s (executable %s)", d.config.CoreFile, d.processArgs[0])
|
d.log.Infof("opening core file %s (executable %s)", d.config.CoreFile, d.processArgs[0])
|
||||||
p, err = core.OpenCore(d.config.CoreFile, d.processArgs[0])
|
p, err = core.OpenCore(d.config.CoreFile, d.processArgs[0], d.config.DebugInfoDirectories)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = go11DecodeErrorCheck(err)
|
err = go11DecodeErrorCheck(err)
|
||||||
@ -132,17 +136,17 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
|||||||
func (d *Debugger) Launch(processArgs []string, wd string) (proc.Process, error) {
|
func (d *Debugger) Launch(processArgs []string, wd string) (proc.Process, error) {
|
||||||
switch d.config.Backend {
|
switch d.config.Backend {
|
||||||
case "native":
|
case "native":
|
||||||
return native.Launch(processArgs, wd, d.config.Foreground)
|
return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories)
|
||||||
case "lldb":
|
case "lldb":
|
||||||
return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground))
|
return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories))
|
||||||
case "rr":
|
case "rr":
|
||||||
p, _, err := gdbserial.RecordAndReplay(processArgs, wd, false)
|
p, _, err := gdbserial.RecordAndReplay(processArgs, wd, false, d.config.DebugInfoDirectories)
|
||||||
return p, err
|
return p, err
|
||||||
case "default":
|
case "default":
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground))
|
return betterGdbserialLaunchError(gdbserial.LLDBLaunch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories))
|
||||||
}
|
}
|
||||||
return native.Launch(processArgs, wd, d.config.Foreground)
|
return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown backend %q", d.config.Backend)
|
return nil, fmt.Errorf("unknown backend %q", d.config.Backend)
|
||||||
}
|
}
|
||||||
@ -157,14 +161,14 @@ var ErrNoAttachPath = errors.New("must specify executable path on macOS")
|
|||||||
func (d *Debugger) Attach(pid int, path string) (proc.Process, error) {
|
func (d *Debugger) Attach(pid int, path string) (proc.Process, error) {
|
||||||
switch d.config.Backend {
|
switch d.config.Backend {
|
||||||
case "native":
|
case "native":
|
||||||
return native.Attach(pid)
|
return native.Attach(pid, d.config.DebugInfoDirectories)
|
||||||
case "lldb":
|
case "lldb":
|
||||||
return betterGdbserialLaunchError(gdbserial.LLDBAttach(pid, path))
|
return betterGdbserialLaunchError(gdbserial.LLDBAttach(pid, path, d.config.DebugInfoDirectories))
|
||||||
case "default":
|
case "default":
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
return betterGdbserialLaunchError(gdbserial.LLDBAttach(pid, path))
|
return betterGdbserialLaunchError(gdbserial.LLDBAttach(pid, path, d.config.DebugInfoDirectories))
|
||||||
}
|
}
|
||||||
return native.Attach(pid)
|
return native.Attach(pid, d.config.DebugInfoDirectories)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown backend %q", d.config.Backend)
|
return nil, fmt.Errorf("unknown backend %q", d.config.Backend)
|
||||||
}
|
}
|
||||||
|
@ -121,11 +121,12 @@ func (s *ServerImpl) Run() error {
|
|||||||
|
|
||||||
// Create and start the debugger
|
// Create and start the debugger
|
||||||
if s.debugger, err = debugger.New(&debugger.Config{
|
if s.debugger, err = debugger.New(&debugger.Config{
|
||||||
AttachPid: s.config.AttachPid,
|
AttachPid: s.config.AttachPid,
|
||||||
WorkingDir: s.config.WorkingDir,
|
WorkingDir: s.config.WorkingDir,
|
||||||
CoreFile: s.config.CoreFile,
|
CoreFile: s.config.CoreFile,
|
||||||
Backend: s.config.Backend,
|
Backend: s.config.Backend,
|
||||||
Foreground: s.config.Foreground,
|
Foreground: s.config.Foreground,
|
||||||
|
DebugInfoDirectories: s.config.DebugInfoDirectories,
|
||||||
},
|
},
|
||||||
s.config.ProcessArgs); err != nil {
|
s.config.ProcessArgs); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -117,13 +117,13 @@ func withTestProcess(name string, t *testing.T, fn func(p proc.Process, fixture
|
|||||||
var tracedir string
|
var tracedir string
|
||||||
switch testBackend {
|
switch testBackend {
|
||||||
case "native":
|
case "native":
|
||||||
p, err = native.Launch([]string{fixture.Path}, ".", false)
|
p, err = native.Launch([]string{fixture.Path}, ".", false, []string{})
|
||||||
case "lldb":
|
case "lldb":
|
||||||
p, err = gdbserial.LLDBLaunch([]string{fixture.Path}, ".", false)
|
p, err = gdbserial.LLDBLaunch([]string{fixture.Path}, ".", false, []string{})
|
||||||
case "rr":
|
case "rr":
|
||||||
protest.MustHaveRecordingAllowed(t)
|
protest.MustHaveRecordingAllowed(t)
|
||||||
t.Log("recording")
|
t.Log("recording")
|
||||||
p, tracedir, err = gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true)
|
p, tracedir, err = gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true, []string{})
|
||||||
t.Logf("replaying %q", tracedir)
|
t.Logf("replaying %q", tracedir)
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unknown backend %q", testBackend)
|
t.Fatalf("unknown backend %q", testBackend)
|
||||||
|
Loading…
Reference in New Issue
Block a user