提交 51c342c6 编写于 作者: D Derek Parker 提交者: Derek Parker

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
上级 a2346ef6
......@@ -563,15 +563,16 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
switch APIVersion {
case 1, 2:
server = rpccommon.NewServer(&service.Config{
Listener: listener,
ProcessArgs: processArgs,
AttachPid: attachPid,
AcceptMulti: AcceptMulti,
APIVersion: APIVersion,
WorkingDir: WorkingDir,
Backend: Backend,
CoreFile: coreFile,
Foreground: Headless,
Listener: listener,
ProcessArgs: processArgs,
AttachPid: attachPid,
AcceptMulti: AcceptMulti,
APIVersion: APIVersion,
WorkingDir: WorkingDir,
Backend: Backend,
CoreFile: coreFile,
Foreground: Headless,
DebugInfoDirectories: conf.DebugInfoDirectories,
DisconnectChan: disconnectChan,
})
......
......@@ -43,10 +43,14 @@ type Config struct {
// If ShowLocationExpr is true whatis will print the DWARF location
// expression for its argument.
ShowLocationExpr bool `yaml:"show-location-expr"`
// Source list line-number color (3/4 bit color codes as defined
// here: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
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.
......@@ -160,6 +164,9 @@ substitute-path:
# Uncomment the following line to make the whatis command also print the DWARF location expression of its argument.
# 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
}
......
......@@ -12,6 +12,7 @@ import (
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
"sync"
......@@ -27,6 +28,8 @@ import (
// BinaryInfo holds information on the binary being executed.
type BinaryInfo struct {
// Path on disk of the binary being executed.
Path string
// Architecture of this binary.
Arch Arch
......@@ -91,6 +94,9 @@ var ErrUnsupportedDarwinArch = errors.New("unsupported architecture - only darwi
// position independant executable.
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)
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'.
// It is expected this will be called in parallel with other initialization steps
// 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)
if err == nil {
bi.lastModified = fi.ModTime()
}
bi.Path = path
switch bi.GOOS {
case "linux":
return bi.LoadBinaryInfoElf(path, entryPoint, wg)
return bi.LoadBinaryInfoElf(path, entryPoint, debugInfoDirs, wg)
case "windows":
return bi.LoadBinaryInfoPE(path, entryPoint, wg)
case "darwin":
return bi.LoadBinaryInfoMacho(path, entryPoint, wg)
}
return errors.New("unsupported operating system")
return nil
}
// GStructOffset returns the offset of the G
......@@ -563,55 +569,81 @@ func (e *ErrNoBuildIDNote) Error() string {
// in GDB's documentation [1], and if found returns two handles, one
// for the bare file, and another for its corresponding elf.File.
// [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File) (*os.File, *elf.File, error) {
//
// Alternatively, if the debug file cannot be found be the build-id, Delve
// will look in directories specified by the debug-info-directories config value.
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 == "" {
return nil, nil, ErrNoDebugInfoFound
}
sepFile, err := os.OpenFile(debugFilePath, 0, os.ModePerm)
if err != nil {
return nil, nil, errors.New("can't open separate debug file: " + err.Error())
}
elfFile, err := elf.NewFile(sepFile)
if err != nil {
sepFile.Close()
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, err.Error())
}
if elfFile.Machine != elf.EM_X86_64 {
sepFile.Close()
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, ErrUnsupportedLinuxArch.Error())
}
return sepFile, elfFile, nil
}
func parseBuildID(exe *elf.File) (string, string, error) {
buildid := exe.Section(".note.gnu.build-id")
if buildid == nil {
return nil, nil, &ErrNoBuildIDNote{}
return "", "", &ErrNoBuildIDNote{}
}
br := buildid.Open()
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())
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 nil, nil, errors.New("can't read build-id name: " + err.Error())
return "", "", 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")
return "", "", 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())
return "", "", 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 {
return nil, nil, errors.New("can't open separate debug file: " + err.Error())
}
elfFile, err := elf.NewFile(sepFile)
if err != nil {
sepFile.Close()
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugPath, err.Error())
}
if elfFile.Machine != elf.EM_X86_64 {
sepFile.Close()
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugPath, ErrUnsupportedLinuxArch.Error())
}
return sepFile, elfFile, nil
return desc[:2], desc[2:], nil
}
// 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)
if err != nil {
return err
......@@ -639,7 +671,7 @@ func (bi *BinaryInfo) LoadBinaryInfoElf(path string, entryPoint uint64, wg *sync
if err != nil {
var sepFile *os.File
var serr error
sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(elfFile)
sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(elfFile, debugInfoDirectories)
if serr != nil {
if _, ok := serr.(*ErrNoBuildIDNote); ok {
return err
......
......@@ -185,14 +185,16 @@ var (
)
// 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)
if err != nil {
return nil, err
}
var wg sync.WaitGroup
err = p.bi.LoadBinaryInfo(exePath, p.entryPoint, &wg)
err = p.bi.LoadBinaryInfo(exePath, p.entryPoint, debugInfoDirs, &wg)
wg.Wait()
if err == nil {
err = p.bi.LoadError()
......
......@@ -172,7 +172,7 @@ func withCoreFile(t *testing.T, name, args string) *Process {
}
corePath := cores[0]
p, err := OpenCore(corePath, fix.Path)
p, err := OpenCore(corePath, fix.Path, []string{})
if err != nil {
t.Errorf("ReadCore(%q) failed: %v", corePath, err)
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.
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)
go func() {
......@@ -219,7 +219,7 @@ func (p *Process) Listen(listener net.Listener, path string, pid int) error {
if conn == nil {
return errors.New("could not connect")
}
return p.Connect(conn, path, pid)
return p.Connect(conn, path, pid, debugInfoDirs)
case status := <-p.waitChan:
listener.Close()
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.
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 {
conn, err := net.Dial("tcp", addr)
if err == nil {
return p.Connect(conn, path, pid)
return p.Connect(conn, path, pid, debugInfoDirs)
}
select {
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
// some stubs do not provide ways to determine path and pid automatically
// 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.pid = pid
......@@ -312,7 +312,7 @@ func (p *Process) Connect(conn net.Conn, path string, pid int) error {
}
var wg sync.WaitGroup
err = p.bi.LoadBinaryInfo(path, entryPoint, &wg)
err = p.bi.LoadBinaryInfo(path, entryPoint, debugInfoDirs, &wg)
wg.Wait()
if err == nil {
err = p.bi.LoadError()
......@@ -405,7 +405,7 @@ func getLdEnvVars() []string {
// LLDBLaunch starts an instance of lldb-server and connects to it, asking
// it to launch the specified target program with the specified arguments
// (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 {
case "windows":
return nil, ErrUnsupportedOS
......@@ -482,9 +482,9 @@ func LLDBLaunch(cmd []string, wd string, foreground bool) (*Process, error) {
p.conn.isDebugserver = isDebugserver
if listener != nil {
err = p.Listen(listener, cmd[0], 0)
err = p.Listen(listener, cmd[0], 0, debugInfoDirs)
} else {
err = p.Dial(port, cmd[0], 0)
err = p.Dial(port, cmd[0], 0, debugInfoDirs)
}
if err != nil {
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
// for some stubs that do not provide an automated way of determining it
// (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" {
return nil, ErrUnsupportedOS
}
isDebugserver := false
var proc *exec.Cmd
var process *exec.Cmd
var listener net.Listener
var port string
if _, err := os.Stat(debugserverExecutable); err == nil {
......@@ -512,32 +512,32 @@ func LLDBAttach(pid int, path string) (*Process, error) {
if err != nil {
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 {
if _, err := exec.LookPath("lldb-server"); err != nil {
return nil, &ErrBackendUnavailable{}
}
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
proc.Stderr = os.Stderr
process.Stdout = os.Stdout
process.Stderr = os.Stderr
proc.SysProcAttr = sysProcAttr(false)
process.SysProcAttr = sysProcAttr(false)
err := proc.Start()
err := process.Start()
if err != nil {
return nil, err
}
p := New(proc.Process)
p := New(process.Process)
p.conn.isDebugserver = isDebugserver
if listener != nil {
err = p.Listen(listener, path, pid)
err = p.Listen(listener, path, pid, debugInfoDirs)
} else {
err = p.Dial(port, path, pid)
err = p.Dial(port, path, pid, debugInfoDirs)
}
if err != nil {
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
// 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 {
return nil, err
}
......@@ -82,7 +82,7 @@ func Replay(tracedir string, quiet bool) (*Process, error) {
p := New(rrcmd.Process)
p.tracedir = tracedir
err = p.Dial(init.port, init.exe, 0)
err = p.Dial(init.port, init.exe, 0, debugInfoDirs)
if err != nil {
rrcmd.Process.Kill()
return nil, err
......@@ -257,11 +257,11 @@ func splitQuotedFields(in string) []string {
}
// 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)
if tracedir == "" {
return nil, "", err
}
p, err = Replay(tracedir, quiet)
p, err = Replay(tracedir, quiet, debugInfoDirs)
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.Log("recording")
p, tracedir, err := gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true)
p, tracedir, err := gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true, []string{})
if err != nil {
t.Fatal("Launch():", err)
}
......
......@@ -12,12 +12,12 @@ import (
var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")
// 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
}
// Attach returns ErrNativeBackendDisabled.
func Attach(pid int) (*Process, error) {
func Attach(pid int, _ []string) (*Process, error) {
return nil, ErrNativeBackendDisabled
}
......
......@@ -189,7 +189,7 @@ func (dbp *Process) Breakpoints() *proc.BreakpointMap {
// * Dwarf .debug_frame section
// * Dwarf .debug_line section
// * Go symbol table.
func (dbp *Process) LoadInformation(path string) error {
func (dbp *Process) LoadInformation(path string, debugInfoDirs []string) error {
var wg sync.WaitGroup
path = findExecutable(path, dbp.pid)
......@@ -201,7 +201,7 @@ func (dbp *Process) LoadInformation(path string) error {
wg.Add(1)
go dbp.loadProcessInformation(&wg)
err = dbp.bi.LoadBinaryInfo(path, entryPoint, &wg)
err = dbp.bi.LoadBinaryInfo(path, entryPoint, debugInfoDirs, &wg)
wg.Wait()
if err == nil {
err = dbp.bi.LoadError()
......@@ -380,8 +380,8 @@ func (dbp *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
}
// Returns a new Process struct.
func initializeDebugProcess(dbp *Process, path string) (*Process, error) {
err := dbp.LoadInformation(path)
func initializeDebugProcess(dbp *Process, path string, debugInfoDirs []string) (*Process, error) {
err := dbp.LoadInformation(path, debugInfoDirs)
if err != nil {
return dbp, err
}
......
......@@ -38,7 +38,7 @@ type OSProcessDetails struct {
// custom fork/exec process in order to take advantage of
// PT_SIGEXC on Darwin which will turn Unix signals into
// 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
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
return nil, proc.ErrNotExecutable
......@@ -119,7 +119,7 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
}
dbp.os.initialized = true
dbp, err = initializeDebugProcess(dbp, argv0Go)
dbp, err = initializeDebugProcess(dbp, argv0Go, []string{})
if err != nil {
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.
func Attach(pid int) (*Process, error) {
func Attach(pid int, _ []string) (*Process, error) {
dbp := New(pid)
kret := C.acquire_mach_task(C.int(pid),
......@@ -155,7 +155,7 @@ func Attach(pid int) (*Process, error) {
return nil, err
}
dbp, err = initializeDebugProcess(dbp, "")
dbp, err = initializeDebugProcess(dbp, "", []string{})
if err != nil {
dbp.Detach(false)
return nil, err
......
......@@ -46,7 +46,9 @@ type OSProcessDetails struct {
// Launch creates and begins debugging a new process. First entry in
// `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.
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 (
process *exec.Cmd
err error
......@@ -88,11 +90,13 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
if err != nil {
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.
func Attach(pid int) (*Process, error) {
// Attach to an existing process with the given PID. Once attached, if
// 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.common = proc.NewCommonProcess(true)
......@@ -106,7 +110,7 @@ func Attach(pid int) (*Process, error) {
return nil, err
}
dbp, err = initializeDebugProcess(dbp, "")
dbp, err = initializeDebugProcess(dbp, "", debugInfoDirs)
if err != nil {
dbp.Detach(false)
return nil, err
......
......@@ -37,7 +37,7 @@ func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
}
// 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])
if err != nil {
return nil, err
......@@ -115,7 +115,7 @@ func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
return nil, err
}
return initializeDebugProcess(dbp, exepath)
return initializeDebugProcess(dbp, exepath, []string{})
}
// 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.
func Attach(pid int) (*Process, error) {
func Attach(pid int, _ []string) (*Process, error) {
// TODO: Probably should have SeDebugPrivilege before starting here.
err := _DebugActiveProcess(uint32(pid))
if err != nil {
......
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 {
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":
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":
protest.MustHaveRecordingAllowed(t)
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)
default:
t.Fatal("unknown backend")
......@@ -2048,9 +2048,9 @@ func TestIssue509(t *testing.T) {
switch testBackend {
case "native":
_, err = native.Launch([]string{exepath}, ".", false)
_, err = native.Launch([]string{exepath}, ".", false, []string{})
case "lldb":
_, err = gdbserial.LLDBLaunch([]string{exepath}, ".", false)
_, err = gdbserial.LLDBLaunch([]string{exepath}, ".", false, []string{})
default:
t.Skip("test not valid for this backend")
}
......@@ -2090,9 +2090,9 @@ func TestUnsupportedArch(t *testing.T) {
switch testBackend {
case "native":
p, err = native.Launch([]string{outfile}, ".", false)
p, err = native.Launch([]string{outfile}, ".", false, []string{})
case "lldb":
p, err = gdbserial.LLDBLaunch([]string{outfile}, ".", false)
p, err = gdbserial.LLDBLaunch([]string{outfile}, ".", false, []string{})
default:
t.Skip("test not valid for this backend")
}
......@@ -2839,13 +2839,13 @@ func TestAttachDetach(t *testing.T) {
switch testBackend {
case "native":
p, err = native.Attach(cmd.Process.Pid)
p, err = native.Attach(cmd.Process.Pid, []string{})
case "lldb":
path := ""
if runtime.GOOS == "darwin" {
path = fixture.Path
}
p, err = gdbserial.LLDBAttach(cmd.Process.Pid, path)
p, err = gdbserial.LLDBAttach(cmd.Process.Pid, path, []string{})
default:
err = fmt.Errorf("unknown backend %q", testBackend)
}
......@@ -3141,13 +3141,13 @@ func TestAttachStripped(t *testing.T) {
switch testBackend {
case "native":
p, err = native.Attach(cmd.Process.Pid)
p, err = native.Attach(cmd.Process.Pid, []string{})
case "lldb":
path := ""
if runtime.GOOS == "darwin" {
path = fixture.Path
}
p, err = gdbserial.LLDBAttach(cmd.Process.Pid, path)
p, err = gdbserial.LLDBAttach(cmd.Process.Pid, path, []string{})
default:
t.Fatalf("unknown backend %q", testBackend)
}
......
......@@ -29,6 +29,10 @@ type Config struct {
// CoreFile specifies the path to the core dump to open.
CoreFile string
// DebugInfoDirectories is the list of directories to look for
// when resolving external debug info files.
DebugInfoDirectories []string
// Selects server backend.
Backend string
......
......@@ -65,6 +65,10 @@ type Config struct {
// Foreground lets target process access stdin.
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
......@@ -102,10 +106,10 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
switch d.config.Backend {
case "rr":
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:
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 {
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) {
switch d.config.Backend {
case "native":
return native.Launch(processArgs, wd, d.config.Foreground)
return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories)
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":
p, _, err := gdbserial.RecordAndReplay(processArgs, wd, false)
p, _, err := gdbserial.RecordAndReplay(processArgs, wd, false, d.config.DebugInfoDirectories)
return p, err
case "default":
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:
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) {
switch d.config.Backend {
case "native":
return native.Attach(pid)
return native.Attach(pid, d.config.DebugInfoDirectories)
case "lldb":
return betterGdbserialLaunchError(gdbserial.LLDBAttach(pid, path))
return betterGdbserialLaunchError(gdbserial.LLDBAttach(pid, path, d.config.DebugInfoDirectories))
case "default":
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:
return nil, fmt.Errorf("unknown backend %q", d.config.Backend)
}
......
......@@ -121,11 +121,12 @@ func (s *ServerImpl) Run() error {
// Create and start the debugger
if s.debugger, err = debugger.New(&debugger.Config{
AttachPid: s.config.AttachPid,
WorkingDir: s.config.WorkingDir,
CoreFile: s.config.CoreFile,
Backend: s.config.Backend,
Foreground: s.config.Foreground,
AttachPid: s.config.AttachPid,
WorkingDir: s.config.WorkingDir,
CoreFile: s.config.CoreFile,
Backend: s.config.Backend,
Foreground: s.config.Foreground,
DebugInfoDirectories: s.config.DebugInfoDirectories,
},
s.config.ProcessArgs); err != nil {
return err
......
......@@ -117,13 +117,13 @@ func withTestProcess(name string, t *testing.T, fn func(p proc.Process, fixture
var tracedir string
switch testBackend {
case "native":
p, err = native.Launch([]string{fixture.Path}, ".", false)
p, err = native.Launch([]string{fixture.Path}, ".", false, []string{})
case "lldb":
p, err = gdbserial.LLDBLaunch([]string{fixture.Path}, ".", false)
p, err = gdbserial.LLDBLaunch([]string{fixture.Path}, ".", false, []string{})
case "rr":
protest.MustHaveRecordingAllowed(t)
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)
default:
t.Fatalf("unknown backend %q", testBackend)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册