mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 10:47:27 +08:00 
			
		
		
		
	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:
		 Derek Parker
					Derek Parker
				
			
				
					committed by
					
						 Derek Parker
						Derek Parker
					
				
			
			
				
	
			
			
			 Derek Parker
						Derek Parker
					
				
			
						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, | ||||||
| 		}) | 		}) | ||||||
|  | |||||||
| @ -47,6 +47,10 @@ type Config struct { | |||||||
| 	// 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) | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user