*: Misc refactors, and doc additions
Refactors some code, adds a bunch of docstrings and just generally fixes a bunch of linter complaints.
This commit is contained in:
parent
4fea15832f
commit
c3f50742b9
@ -533,7 +533,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
||||
}
|
||||
|
||||
if err := server.Run(); err != nil {
|
||||
if err == api.NotExecutableErr {
|
||||
if err == api.ErrNotExecutable {
|
||||
switch kind {
|
||||
case executingGeneratedFile:
|
||||
fmt.Fprintln(os.Stderr, "Can not debug non-main package")
|
||||
|
@ -56,11 +56,11 @@ func NewFrameIndex() FrameDescriptionEntries {
|
||||
return make(FrameDescriptionEntries, 0, 1000)
|
||||
}
|
||||
|
||||
type NoFDEForPCError struct {
|
||||
type ErrNoFDEForPC struct {
|
||||
PC uint64
|
||||
}
|
||||
|
||||
func (err *NoFDEForPCError) Error() string {
|
||||
func (err *ErrNoFDEForPC) Error() string {
|
||||
return fmt.Sprintf("could not find FDE for PC %#v", err.PC)
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry,
|
||||
return true
|
||||
})
|
||||
if idx == len(fdes) {
|
||||
return nil, &NoFDEForPCError{pc}
|
||||
return nil, &ErrNoFDEForPC{pc}
|
||||
}
|
||||
return fdes[idx], nil
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ func (a *AMD64) BreakpointSize() int {
|
||||
return a.breakInstructionLen
|
||||
}
|
||||
|
||||
// If DerefTLS returns true the value of regs.TLS()+GStructOffset() is a
|
||||
// DerefTLS returns true if the value of regs.TLS()+GStructOffset() is a
|
||||
// pointer to the G struct
|
||||
func (a *AMD64) DerefTLS() bool {
|
||||
return a.goos == "windows"
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"github.com/derekparker/delve/pkg/goversion"
|
||||
)
|
||||
|
||||
// BinaryInfo holds information on the binary being executed.
|
||||
type BinaryInfo struct {
|
||||
lastModified time.Time // Time the executable of this process was last modified
|
||||
|
||||
@ -72,9 +73,14 @@ type BinaryInfo struct {
|
||||
dwarfReader *dwarf.Reader
|
||||
}
|
||||
|
||||
var UnsupportedLinuxArchErr = errors.New("unsupported architecture - only linux/amd64 is supported")
|
||||
var UnsupportedWindowsArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
|
||||
var UnsupportedDarwinArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported")
|
||||
// ErrUnsupportedLinuxArch is returned when attempting to debug a binary compiled for an unsupported architecture.
|
||||
var ErrUnsupportedLinuxArch = errors.New("unsupported architecture - only linux/amd64 is supported")
|
||||
|
||||
// ErrUnsupportedWindowsArch is returned when attempting to debug a binary compiled for an unsupported architecture.
|
||||
var ErrUnsupportedWindowsArch = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
|
||||
|
||||
// ErrUnsupportedDarwinArch is returned when attempting to debug a binary compiled for an unsupported architecture.
|
||||
var ErrUnsupportedDarwinArch = errors.New("unsupported architecture - only darwin/amd64 is supported")
|
||||
|
||||
const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
|
||||
|
||||
@ -260,16 +266,17 @@ func (e *loclistEntry) BaseAddressSelection() bool {
|
||||
return e.lowpc == ^uint64(0)
|
||||
}
|
||||
|
||||
type buildIdHeader struct {
|
||||
type buildIDHeader struct {
|
||||
Namesz uint32
|
||||
Descsz uint32
|
||||
Type uint32
|
||||
}
|
||||
|
||||
func NewBinaryInfo(goos, goarch string) BinaryInfo {
|
||||
r := BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), typeCache: make(map[dwarf.Offset]godwarf.Type)}
|
||||
// NewBinaryInfo returns an initialized but unloaded BinaryInfo struct.
|
||||
func NewBinaryInfo(goos, goarch string) *BinaryInfo {
|
||||
r := &BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), typeCache: make(map[dwarf.Offset]godwarf.Type)}
|
||||
|
||||
// TODO: find better way to determine proc arch (perhaps use executable file info)
|
||||
// TODO: find better way to determine proc arch (perhaps use executable file info).
|
||||
switch goarch {
|
||||
case "amd64":
|
||||
r.Arch = AMD64Arch(goos)
|
||||
@ -278,19 +285,22 @@ func NewBinaryInfo(goos, goarch string) BinaryInfo {
|
||||
return r
|
||||
}
|
||||
|
||||
func (bininfo *BinaryInfo) LoadBinaryInfo(path string, wg *sync.WaitGroup) error {
|
||||
// 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, wg *sync.WaitGroup) error {
|
||||
fi, err := os.Stat(path)
|
||||
if err == nil {
|
||||
bininfo.lastModified = fi.ModTime()
|
||||
bi.lastModified = fi.ModTime()
|
||||
}
|
||||
|
||||
switch bininfo.GOOS {
|
||||
switch bi.GOOS {
|
||||
case "linux":
|
||||
return bininfo.LoadBinaryInfoElf(path, wg)
|
||||
return bi.LoadBinaryInfoElf(path, wg)
|
||||
case "windows":
|
||||
return bininfo.LoadBinaryInfoPE(path, wg)
|
||||
return bi.LoadBinaryInfoPE(path, wg)
|
||||
case "darwin":
|
||||
return bininfo.LoadBinaryInfoMacho(path, wg)
|
||||
return bi.LoadBinaryInfoMacho(path, wg)
|
||||
}
|
||||
return errors.New("unsupported operating system")
|
||||
}
|
||||
@ -301,6 +311,7 @@ func (bi *BinaryInfo) GStructOffset() uint64 {
|
||||
return bi.gStructOffset
|
||||
}
|
||||
|
||||
// LastModified returns the last modified time of the binary.
|
||||
func (bi *BinaryInfo) LastModified() time.Time {
|
||||
return bi.lastModified
|
||||
}
|
||||
@ -381,6 +392,7 @@ func (bi *BinaryInfo) PCToFunc(pc uint64) *Function {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes all internal readers.
|
||||
func (bi *BinaryInfo) Close() error {
|
||||
if bi.sepDebugCloser != nil {
|
||||
bi.sepDebugCloser.Close()
|
||||
@ -394,6 +406,7 @@ func (bi *BinaryInfo) setLoadError(fmtstr string, args ...interface{}) {
|
||||
bi.loadErrMu.Unlock()
|
||||
}
|
||||
|
||||
// LoadError returns any internal load error.
|
||||
func (bi *BinaryInfo) LoadError() error {
|
||||
return bi.loadErr
|
||||
}
|
||||
@ -506,6 +519,7 @@ func (bi *BinaryInfo) findCompileUnitForOffset(off dwarf.Offset) *compileUnit {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Producer returns the value of DW_AT_producer.
|
||||
func (bi *BinaryInfo) Producer() string {
|
||||
for _, cu := range bi.compileUnits {
|
||||
if cu.isgo && cu.producer != "" {
|
||||
@ -522,12 +536,12 @@ func (bi *BinaryInfo) Type(offset dwarf.Offset) (godwarf.Type, error) {
|
||||
|
||||
// ELF ///////////////////////////////////////////////////////////////
|
||||
|
||||
// This error is used in openSeparateDebugInfo to signal there's no
|
||||
// ErrNoBuildIDNote is used in openSeparateDebugInfo to signal there's no
|
||||
// build-id note on the binary, so LoadBinaryInfoElf will return
|
||||
// the error message coming from elfFile.DWARF() instead.
|
||||
type NoBuildIdNoteError struct{}
|
||||
type ErrNoBuildIDNote struct{}
|
||||
|
||||
func (e *NoBuildIdNoteError) Error() string {
|
||||
func (e *ErrNoBuildIDNote) Error() string {
|
||||
return "can't find build-id note on binary"
|
||||
}
|
||||
|
||||
@ -539,11 +553,11 @@ func (e *NoBuildIdNoteError) Error() string {
|
||||
func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File) (*os.File, *elf.File, error) {
|
||||
buildid := exe.Section(".note.gnu.build-id")
|
||||
if buildid == nil {
|
||||
return nil, nil, &NoBuildIdNoteError{}
|
||||
return nil, nil, &ErrNoBuildIDNote{}
|
||||
}
|
||||
|
||||
br := buildid.Open()
|
||||
bh := new(buildIdHeader)
|
||||
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())
|
||||
}
|
||||
@ -572,17 +586,18 @@ func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File) (*os.File, *elf.File,
|
||||
elfFile, err := elf.NewFile(sepFile)
|
||||
if err != nil {
|
||||
sepFile.Close()
|
||||
return nil, nil, errors.New(fmt.Sprintf("can't open separate debug file %q: %v", debugPath, err.Error()))
|
||||
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, errors.New(fmt.Sprintf("can't open separate debug file %q: %v", debugPath, UnsupportedLinuxArchErr.Error()))
|
||||
return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugPath, ErrUnsupportedLinuxArch.Error())
|
||||
}
|
||||
|
||||
return sepFile, elfFile, nil
|
||||
}
|
||||
|
||||
// LoadBinaryInfoElf specifically loads information from an ELF binary.
|
||||
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, wg *sync.WaitGroup) error {
|
||||
exe, err := os.OpenFile(path, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
@ -594,7 +609,7 @@ func (bi *BinaryInfo) LoadBinaryInfoElf(path string, wg *sync.WaitGroup) error {
|
||||
return err
|
||||
}
|
||||
if elfFile.Machine != elf.EM_X86_64 {
|
||||
return UnsupportedLinuxArchErr
|
||||
return ErrUnsupportedLinuxArch
|
||||
}
|
||||
dwarfFile := elfFile
|
||||
bi.dwarf, err = elfFile.DWARF()
|
||||
@ -603,7 +618,7 @@ func (bi *BinaryInfo) LoadBinaryInfoElf(path string, wg *sync.WaitGroup) error {
|
||||
var serr error
|
||||
sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(elfFile)
|
||||
if serr != nil {
|
||||
if _, ok := serr.(*NoBuildIdNoteError); ok {
|
||||
if _, ok := serr.(*ErrNoBuildIDNote); ok {
|
||||
return err
|
||||
}
|
||||
return serr
|
||||
@ -688,6 +703,7 @@ func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
|
||||
|
||||
// PE ////////////////////////////////////////////////////////////////
|
||||
|
||||
// LoadBinaryInfoPE specifically loads information from a PE binary.
|
||||
func (bi *BinaryInfo) LoadBinaryInfoPE(path string, wg *sync.WaitGroup) error {
|
||||
peFile, closer, err := openExecutablePathPE(path)
|
||||
if err != nil {
|
||||
@ -695,7 +711,7 @@ func (bi *BinaryInfo) LoadBinaryInfoPE(path string, wg *sync.WaitGroup) error {
|
||||
}
|
||||
bi.closer = closer
|
||||
if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
|
||||
return UnsupportedWindowsArchErr
|
||||
return ErrUnsupportedWindowsArch
|
||||
}
|
||||
bi.dwarf, err = peFile.DWARF()
|
||||
if err != nil {
|
||||
@ -772,6 +788,7 @@ func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
||||
|
||||
// MACH-O ////////////////////////////////////////////////////////////
|
||||
|
||||
// LoadBinaryInfoMacho specifically loads information from a Mach-O binary.
|
||||
func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, wg *sync.WaitGroup) error {
|
||||
exe, err := macho.Open(path)
|
||||
if err != nil {
|
||||
@ -779,7 +796,7 @@ func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, wg *sync.WaitGroup) error
|
||||
}
|
||||
bi.closer = exe
|
||||
if exe.Cpu != macho.CpuAmd64 {
|
||||
return UnsupportedDarwinArchErr
|
||||
return ErrUnsupportedDarwinArch
|
||||
}
|
||||
bi.dwarf, err = exe.DWARF()
|
||||
if err != nil {
|
||||
|
@ -59,7 +59,7 @@ type Breakpoint struct {
|
||||
returnInfo *returnBreakpointInfo
|
||||
}
|
||||
|
||||
// Breakpoint Kind determines the behavior of delve when the
|
||||
// BreakpointKind determines the behavior of delve when the
|
||||
// breakpoint is reached.
|
||||
type BreakpointKind uint16
|
||||
|
||||
@ -350,6 +350,7 @@ type BreakpointState struct {
|
||||
CondError error
|
||||
}
|
||||
|
||||
// Clear zeros the struct.
|
||||
func (bpstate *BreakpointState) Clear() {
|
||||
bpstate.Breakpoint = nil
|
||||
bpstate.Active = false
|
||||
|
@ -137,12 +137,14 @@ type OffsetReaderAt struct {
|
||||
offset uintptr
|
||||
}
|
||||
|
||||
// ReadMemory will read the memory at addr-offset.
|
||||
func (r *OffsetReaderAt) ReadMemory(buf []byte, addr uintptr) (n int, err error) {
|
||||
return r.reader.ReadAt(buf, int64(addr-r.offset))
|
||||
}
|
||||
|
||||
// Process represents a core file.
|
||||
type Process struct {
|
||||
bi proc.BinaryInfo
|
||||
bi *proc.BinaryInfo
|
||||
core *Core
|
||||
breakpoints proc.BreakpointMap
|
||||
currentThread *Thread
|
||||
@ -150,6 +152,7 @@ type Process struct {
|
||||
common proc.CommonProcess
|
||||
}
|
||||
|
||||
// Thread represents a thread in the core file being debugged.
|
||||
type Thread struct {
|
||||
th *LinuxPrStatus
|
||||
fpregs []proc.Register
|
||||
@ -157,10 +160,17 @@ type Thread struct {
|
||||
common proc.CommonThread
|
||||
}
|
||||
|
||||
var ErrWriteCore = errors.New("can not to core process")
|
||||
// ErrWriteCore is returned when attempting to write to the core
|
||||
// process memory.
|
||||
var ErrWriteCore = errors.New("can not write to core process")
|
||||
|
||||
// ErrShortRead is returned on a short read.
|
||||
var ErrShortRead = errors.New("short read")
|
||||
|
||||
// ErrContinueCore is returned when trying to continue execution of a core process.
|
||||
var ErrContinueCore = errors.New("can not continue execution of core process")
|
||||
|
||||
// OpenCore will open the core file and return a Process struct.
|
||||
func OpenCore(corePath, exePath string) (*Process, error) {
|
||||
core, err := readCore(corePath, exePath)
|
||||
if err != nil {
|
||||
@ -194,43 +204,69 @@ func OpenCore(corePath, exePath string) (*Process, error) {
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// BinInfo will return the binary info.
|
||||
func (p *Process) BinInfo() *proc.BinaryInfo {
|
||||
return &p.bi
|
||||
return p.bi
|
||||
}
|
||||
|
||||
func (p *Process) Recorded() (bool, string) { return true, "" }
|
||||
func (p *Process) Restart(string) error { return ErrContinueCore }
|
||||
func (p *Process) Direction(proc.Direction) error { return ErrContinueCore }
|
||||
func (p *Process) When() (string, error) { return "", nil }
|
||||
func (p *Process) Checkpoint(string) (int, error) { return -1, ErrContinueCore }
|
||||
func (p *Process) Checkpoints() ([]proc.Checkpoint, error) { return nil, nil }
|
||||
func (p *Process) ClearCheckpoint(int) error { return errors.New("checkpoint not found") }
|
||||
// Recorded returns whether this is a live or recorded process. Always returns true for core files.
|
||||
func (p *Process) Recorded() (bool, string) { return true, "" }
|
||||
|
||||
func (thread *Thread) ReadMemory(data []byte, addr uintptr) (n int, err error) {
|
||||
n, err = thread.p.core.ReadMemory(data, addr)
|
||||
// Restart will only return an error for core files, as they are not executing.
|
||||
func (p *Process) Restart(string) error { return ErrContinueCore }
|
||||
|
||||
// Direction will only return an error as you cannot continue a core process.
|
||||
func (p *Process) Direction(proc.Direction) error { return ErrContinueCore }
|
||||
|
||||
// When does not apply to core files, it is to support the Mozilla 'rr' backend.
|
||||
func (p *Process) When() (string, error) { return "", nil }
|
||||
|
||||
// Checkpoint for core files returns an error, there is no execution of a core file.
|
||||
func (p *Process) Checkpoint(string) (int, error) { return -1, ErrContinueCore }
|
||||
|
||||
// Checkpoints returns nil on core files, you cannot set checkpoints when debugging core files.
|
||||
func (p *Process) Checkpoints() ([]proc.Checkpoint, error) { return nil, nil }
|
||||
|
||||
// ClearCheckpoint clears a checkpoint, but will only return an error for core files.
|
||||
func (p *Process) ClearCheckpoint(int) error { return errors.New("checkpoint not found") }
|
||||
|
||||
// ReadMemory will return memory from the core file at the specified location and put the
|
||||
// read memory into `data`, returning the length read, and returning an error if
|
||||
// the length read is shorter than the length of the `data` buffer.
|
||||
func (t *Thread) ReadMemory(data []byte, addr uintptr) (n int, err error) {
|
||||
n, err = t.p.core.ReadMemory(data, addr)
|
||||
if err == nil && n != len(data) {
|
||||
err = ErrShortRead
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (thread *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
|
||||
// WriteMemory will only return an error for core files, you cannot write
|
||||
// to the memory of a core process.
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
|
||||
return 0, ErrWriteCore
|
||||
}
|
||||
|
||||
// Location returns the location of this thread based on
|
||||
// the value of the instruction pointer register.
|
||||
func (t *Thread) Location() (*proc.Location, error) {
|
||||
f, l, fn := t.p.bi.PCToLine(t.th.Reg.Rip)
|
||||
return &proc.Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil
|
||||
}
|
||||
|
||||
// Breakpoint returns the current breakpoint this thread is stopped at.
|
||||
// For core files this always returns an empty BreakpointState struct, as
|
||||
// there are no breakpoints when debugging core files.
|
||||
func (t *Thread) Breakpoint() proc.BreakpointState {
|
||||
return proc.BreakpointState{}
|
||||
}
|
||||
|
||||
// ThreadID returns the ID for this thread.
|
||||
func (t *Thread) ThreadID() int {
|
||||
return int(t.th.Pid)
|
||||
}
|
||||
|
||||
// Registers returns the current value of the registers for this thread.
|
||||
func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
|
||||
r := &Registers{&t.th.Reg, nil}
|
||||
if floatingPoint {
|
||||
@ -239,105 +275,152 @@ func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// RestoreRegisters will only return an error for core files,
|
||||
// you cannot change register values for core files.
|
||||
func (t *Thread) RestoreRegisters(proc.Registers) error {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
|
||||
// Arch returns the architecture the target is built for and executing on.
|
||||
func (t *Thread) Arch() proc.Arch {
|
||||
return t.p.bi.Arch
|
||||
}
|
||||
|
||||
// BinInfo returns information about the binary.
|
||||
func (t *Thread) BinInfo() *proc.BinaryInfo {
|
||||
return &t.p.bi
|
||||
return t.p.bi
|
||||
}
|
||||
|
||||
// StepInstruction will only return an error for core files,
|
||||
// you cannot execute a core file.
|
||||
func (t *Thread) StepInstruction() error {
|
||||
return ErrContinueCore
|
||||
}
|
||||
|
||||
// Blocked will return false always for core files as there is
|
||||
// no execution.
|
||||
func (t *Thread) Blocked() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetCurrentBreakpoint will always just return nil
|
||||
// for core files, as there are no breakpoints in core files.
|
||||
func (t *Thread) SetCurrentBreakpoint() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Common returns a struct containing common information
|
||||
// across thread implementations.
|
||||
func (t *Thread) Common() *proc.CommonThread {
|
||||
return &t.common
|
||||
}
|
||||
|
||||
// SetPC will always return an error, you cannot
|
||||
// change register values when debugging core files.
|
||||
func (t *Thread) SetPC(uint64) error {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
|
||||
// SetSP will always return an error, you cannot
|
||||
// change register values when debugging core files.
|
||||
func (t *Thread) SetSP(uint64) error {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
|
||||
// SetDX will always return an error, you cannot
|
||||
// change register values when debugging core files.
|
||||
func (t *Thread) SetDX(uint64) error {
|
||||
return errors.New("not supported")
|
||||
}
|
||||
|
||||
// Breakpoints will return all breakpoints for the process.
|
||||
func (p *Process) Breakpoints() *proc.BreakpointMap {
|
||||
return &p.breakpoints
|
||||
}
|
||||
|
||||
// ClearBreakpoint will always return an error as you cannot set or clear
|
||||
// breakpoints on core files.
|
||||
func (p *Process) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
|
||||
return nil, proc.NoBreakpointError{Addr: addr}
|
||||
}
|
||||
|
||||
// ClearInternalBreakpoints will always return nil and have no
|
||||
// effect since you cannot set breakpoints on core files.
|
||||
func (p *Process) ClearInternalBreakpoints() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContinueOnce will always return an error because you
|
||||
// cannot control execution of a core file.
|
||||
func (p *Process) ContinueOnce() (proc.Thread, error) {
|
||||
return nil, ErrContinueCore
|
||||
}
|
||||
|
||||
// StepInstruction will always return an error
|
||||
// as you cannot control execution of a core file.
|
||||
func (p *Process) StepInstruction() error {
|
||||
return ErrContinueCore
|
||||
}
|
||||
|
||||
// RequestManualStop will return nil and have no effect
|
||||
// as you cannot control execution of a core file.
|
||||
func (p *Process) RequestManualStop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckAndClearManualStopRequest will always return false and
|
||||
// have no effect since there are no manual stop requests as
|
||||
// there is no controlling execution of a core file.
|
||||
func (p *Process) CheckAndClearManualStopRequest() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// CurrentThread returns the current active thread.
|
||||
func (p *Process) CurrentThread() proc.Thread {
|
||||
return p.currentThread
|
||||
}
|
||||
|
||||
// Detach will always return nil and have no
|
||||
// effect as you cannot detach from a core file
|
||||
// and have it continue execution or exit.
|
||||
func (p *Process) Detach(bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Valid returns whether the process is active. Always returns true
|
||||
// for core files as it cannot exit or be otherwise detached from.
|
||||
func (p *Process) Valid() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Common returns common information across Process
|
||||
// implementations.
|
||||
func (p *Process) Common() *proc.CommonProcess {
|
||||
return &p.common
|
||||
}
|
||||
|
||||
// Pid returns the process ID of this process.
|
||||
func (p *Process) Pid() int {
|
||||
return p.core.Pid
|
||||
}
|
||||
|
||||
// ResumeNotify is a no-op on core files as we cannot
|
||||
// control execution.
|
||||
func (p *Process) ResumeNotify(chan<- struct{}) {
|
||||
}
|
||||
|
||||
// SelectedGoroutine returns the current active and selected
|
||||
// goroutine.
|
||||
func (p *Process) SelectedGoroutine() *proc.G {
|
||||
return p.selectedGoroutine
|
||||
}
|
||||
|
||||
// SetBreakpoint will always return an error for core files as you cannot write memory or control execution.
|
||||
func (p *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
|
||||
return nil, ErrWriteCore
|
||||
}
|
||||
|
||||
// SwitchGoroutine will change the selected and active goroutine.
|
||||
func (p *Process) SwitchGoroutine(gid int) error {
|
||||
g, err := proc.FindGoroutine(p, gid)
|
||||
if err != nil {
|
||||
@ -354,6 +437,7 @@ func (p *Process) SwitchGoroutine(gid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SwitchThread will change the selected and active thread.
|
||||
func (p *Process) SwitchThread(tid int) error {
|
||||
if th, ok := p.core.Threads[tid]; ok {
|
||||
p.currentThread = th
|
||||
@ -363,6 +447,7 @@ func (p *Process) SwitchThread(tid int) error {
|
||||
return fmt.Errorf("thread %d does not exist", tid)
|
||||
}
|
||||
|
||||
// ThreadList will return a list of all threads currently in the process.
|
||||
func (p *Process) ThreadList() []proc.Thread {
|
||||
r := make([]proc.Thread, 0, len(p.core.Threads))
|
||||
for _, v := range p.core.Threads {
|
||||
@ -371,16 +456,19 @@ func (p *Process) ThreadList() []proc.Thread {
|
||||
return r
|
||||
}
|
||||
|
||||
// FindThread will return the thread with the corresponding thread ID.
|
||||
func (p *Process) FindThread(threadID int) (proc.Thread, bool) {
|
||||
t, ok := p.core.Threads[threadID]
|
||||
return t, ok
|
||||
}
|
||||
|
||||
// Registers represents the CPU registers.
|
||||
type Registers struct {
|
||||
*LinuxCoreRegisters
|
||||
fpregs []proc.Register
|
||||
}
|
||||
|
||||
// Slice will return a slice containing all registers and their values.
|
||||
func (r *Registers) Slice() []proc.Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
@ -426,6 +514,8 @@ func (r *Registers) Slice() []proc.Register {
|
||||
return out
|
||||
}
|
||||
|
||||
// Copy will return a copy of the registers that is guarenteed
|
||||
// not to change.
|
||||
func (r *Registers) Copy() proc.Registers {
|
||||
return r
|
||||
}
|
||||
|
@ -52,33 +52,47 @@ type LinuxCoreTimeval struct {
|
||||
Usec int64
|
||||
}
|
||||
|
||||
const NT_FILE elf.NType = 0x46494c45 // "FILE".
|
||||
// NT_FILE is file mapping information, e.g. program text mappings. Desc is a LinuxNTFile.
|
||||
const NT_FILE elf.NType = 0x46494c45 // "FILE".
|
||||
|
||||
// NT_X86_XSTATE is other registers, including AVX and such.
|
||||
const NT_X86_XSTATE elf.NType = 0x202 // Note type for notes containing X86 XSAVE area.
|
||||
|
||||
// PC returns the value of RIP.
|
||||
func (r *LinuxCoreRegisters) PC() uint64 {
|
||||
return r.Rip
|
||||
}
|
||||
|
||||
// SP returns the value of RSP.
|
||||
func (r *LinuxCoreRegisters) SP() uint64 {
|
||||
return r.Rsp
|
||||
}
|
||||
|
||||
// BP returns the value of RBP.
|
||||
func (r *LinuxCoreRegisters) BP() uint64 {
|
||||
return r.Rbp
|
||||
}
|
||||
|
||||
// CX returns the value of RCX.
|
||||
func (r *LinuxCoreRegisters) CX() uint64 {
|
||||
return r.Rcx
|
||||
}
|
||||
|
||||
// TLS returns the location of the thread local storate,
|
||||
// which will be the value of Fs_base.
|
||||
func (r *LinuxCoreRegisters) TLS() uint64 {
|
||||
return r.Fs_base
|
||||
}
|
||||
|
||||
// GAddr returns the address of the G struct. Always returns 0
|
||||
// and false for core files.
|
||||
func (r *LinuxCoreRegisters) GAddr() (uint64, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Get returns the value of the register requested via the
|
||||
// register number, returning an error if that register
|
||||
// could not be found.
|
||||
func (r *LinuxCoreRegisters) Get(n int) (uint64, error) {
|
||||
reg := x86asm.Reg(n)
|
||||
const (
|
||||
@ -233,7 +247,7 @@ func (r *LinuxCoreRegisters) Get(n int) (uint64, error) {
|
||||
return r.R15, nil
|
||||
}
|
||||
|
||||
return 0, proc.UnknownRegisterError
|
||||
return 0, proc.ErrUnknownRegister
|
||||
}
|
||||
|
||||
// readCore reads a core file from corePath corresponding to the executable at
|
||||
@ -292,6 +306,7 @@ func readCore(corePath, exePath string) (*Core, error) {
|
||||
return core, nil
|
||||
}
|
||||
|
||||
// Core represents a core file.
|
||||
type Core struct {
|
||||
proc.MemoryReader
|
||||
Threads map[int]*Thread
|
||||
@ -456,7 +471,7 @@ func buildMemory(core, exeELF *elf.File, exe io.ReaderAt, notes []*Note) proc.Me
|
||||
return memory
|
||||
}
|
||||
|
||||
// Various structures from the ELF spec and the Linux kernel.
|
||||
// LinuxPrPsInfo has various structures from the ELF spec and the Linux kernel.
|
||||
// AMD64 specific primarily because of unix.PtraceRegs, but also
|
||||
// because some of the fields are word sized.
|
||||
// See http://lxr.free-electrons.com/source/include/uapi/linux/elfcore.h
|
||||
@ -473,6 +488,7 @@ type LinuxPrPsInfo struct {
|
||||
Args [80]uint8
|
||||
}
|
||||
|
||||
// LinuxPrStatus is a copy of the prstatus kernel struct.
|
||||
type LinuxPrStatus struct {
|
||||
Siginfo LinuxSiginfo
|
||||
Cursig uint16
|
||||
@ -485,29 +501,35 @@ type LinuxPrStatus struct {
|
||||
Fpvalid int32
|
||||
}
|
||||
|
||||
// LinuxSiginfo is a copy of the
|
||||
// siginfo kernel struct.
|
||||
type LinuxSiginfo struct {
|
||||
Signo int32
|
||||
Code int32
|
||||
Errno int32
|
||||
}
|
||||
|
||||
// LinuxNTFile contains information on mapped files.
|
||||
type LinuxNTFile struct {
|
||||
LinuxNTFileHdr
|
||||
entries []*LinuxNTFileEntry
|
||||
}
|
||||
|
||||
// LinuxNTFileHdr is a header struct for NTFile.
|
||||
type LinuxNTFileHdr struct {
|
||||
Count uint64
|
||||
PageSize uint64
|
||||
}
|
||||
|
||||
// LinuxNTFileEntry is an entry of an NT_FILE note.
|
||||
type LinuxNTFileEntry struct {
|
||||
Start uint64
|
||||
End uint64
|
||||
FileOfs uint64
|
||||
}
|
||||
|
||||
// ELF Notes header. Same size on 64 and 32-bit machines.
|
||||
// ELFNotesHdr is the ELF Notes header.
|
||||
// Same size on 64 and 32-bit machines.
|
||||
type ELFNotesHdr struct {
|
||||
Namesz uint32
|
||||
Descsz uint32
|
||||
|
@ -2,20 +2,25 @@ package proc
|
||||
|
||||
import "sort"
|
||||
|
||||
// AsmInstruction represents one assembly instruction.
|
||||
type AsmInstruction struct {
|
||||
Loc Location
|
||||
DestLoc *Location
|
||||
Bytes []byte
|
||||
Breakpoint bool
|
||||
AtPC bool
|
||||
Inst *ArchInst
|
||||
Inst *archInst
|
||||
}
|
||||
|
||||
// AssemblyFlavour is the assembly syntax to display.
|
||||
type AssemblyFlavour int
|
||||
|
||||
const (
|
||||
// GNUFlavour will display GNU assembly syntax.
|
||||
GNUFlavour = AssemblyFlavour(iota)
|
||||
// IntelFlavour will display Intel assembly syntax.
|
||||
IntelFlavour
|
||||
// GoFlavour will display Go assembly syntax.
|
||||
GoFlavour
|
||||
)
|
||||
|
||||
|
@ -8,19 +8,19 @@ import (
|
||||
|
||||
var maxInstructionLength uint64 = 15
|
||||
|
||||
type ArchInst x86asm.Inst
|
||||
type archInst x86asm.Inst
|
||||
|
||||
func asmDecode(mem []byte, pc uint64) (*ArchInst, error) {
|
||||
func asmDecode(mem []byte, pc uint64) (*archInst, error) {
|
||||
inst, err := x86asm.Decode(mem, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patchPCRel(pc, &inst)
|
||||
r := ArchInst(inst)
|
||||
r := archInst(inst)
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
func (inst *ArchInst) Size() int {
|
||||
func (inst *archInst) Size() int {
|
||||
return inst.Len
|
||||
}
|
||||
|
||||
@ -34,6 +34,8 @@ func patchPCRel(pc uint64, inst *x86asm.Inst) {
|
||||
}
|
||||
}
|
||||
|
||||
// Text will return the assembly instructions in human readable format according to
|
||||
// the flavour specified.
|
||||
func (inst *AsmInstruction) Text(flavour AssemblyFlavour, bi *BinaryInfo) string {
|
||||
if inst.Inst == nil {
|
||||
return "?"
|
||||
@ -55,6 +57,7 @@ func (inst *AsmInstruction) Text(flavour AssemblyFlavour, bi *BinaryInfo) string
|
||||
return text
|
||||
}
|
||||
|
||||
// IsCall returns true if the instruction is a CALL or LCALL instruction.
|
||||
func (inst *AsmInstruction) IsCall() bool {
|
||||
if inst.Inst == nil {
|
||||
return false
|
||||
@ -62,7 +65,7 @@ func (inst *AsmInstruction) IsCall() bool {
|
||||
return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL
|
||||
}
|
||||
|
||||
func resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
|
||||
func resolveCallArg(inst *archInst, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
|
||||
if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
|
||||
return nil
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ func fakeBinaryInfo(t *testing.T, dwb *dwarfbuilder.Builder) *proc.BinaryInfo {
|
||||
bi := proc.NewBinaryInfo("linux", "amd64")
|
||||
bi.LoadFromData(dwdata, frame, line, loc)
|
||||
|
||||
return &bi
|
||||
return bi
|
||||
}
|
||||
|
||||
// fakeMemory implements proc.MemoryReadWriter by reading from a byte slice.
|
||||
|
@ -19,7 +19,7 @@ import (
|
||||
"github.com/derekparker/delve/pkg/goversion"
|
||||
)
|
||||
|
||||
var OperationOnSpecialFloatError = errors.New("operations on non-finite floats not implemented")
|
||||
var errOperationOnSpecialFloat = errors.New("operations on non-finite floats not implemented")
|
||||
|
||||
// EvalExpression returns the value of the given expression.
|
||||
func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable, error) {
|
||||
@ -853,7 +853,7 @@ func (scope *EvalScope) evalUnary(node *ast.UnaryExpr) (*Variable, error) {
|
||||
return nil, xv.Unreadable
|
||||
}
|
||||
if xv.FloatSpecial != 0 {
|
||||
return nil, OperationOnSpecialFloatError
|
||||
return nil, errOperationOnSpecialFloat
|
||||
}
|
||||
if xv.Value == nil {
|
||||
return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.X))
|
||||
@ -971,7 +971,7 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) {
|
||||
}
|
||||
|
||||
if xv.FloatSpecial != 0 || yv.FloatSpecial != 0 {
|
||||
return nil, OperationOnSpecialFloatError
|
||||
return nil, errOperationOnSpecialFloat
|
||||
}
|
||||
|
||||
typ, err := negotiateType(node.Op, xv, yv)
|
||||
|
@ -39,17 +39,17 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFuncCallUnsupported = errors.New("function calls not supported by this version of Go")
|
||||
ErrFuncCallUnsupportedBackend = errors.New("backend does not support function calls")
|
||||
ErrFuncCallInProgress = errors.New("cannot call function while another function call is already in progress")
|
||||
ErrNotACallExpr = errors.New("not a function call")
|
||||
ErrNoGoroutine = errors.New("no goroutine selected")
|
||||
ErrGoroutineNotRunning = errors.New("selected goroutine not running")
|
||||
ErrNotEnoughStack = errors.New("not enough stack space")
|
||||
ErrTooManyArguments = errors.New("too many arguments")
|
||||
ErrNotEnoughArguments = errors.New("not enough arguments")
|
||||
ErrNoAddrUnsupported = errors.New("arguments to a function call must have an address")
|
||||
ErrNotAGoFunction = errors.New("not a Go function")
|
||||
errFuncCallUnsupported = errors.New("function calls not supported by this version of Go")
|
||||
errFuncCallUnsupportedBackend = errors.New("backend does not support function calls")
|
||||
errFuncCallInProgress = errors.New("cannot call function while another function call is already in progress")
|
||||
errNotACallExpr = errors.New("not a function call")
|
||||
errNoGoroutine = errors.New("no goroutine selected")
|
||||
errGoroutineNotRunning = errors.New("selected goroutine not running")
|
||||
errNotEnoughStack = errors.New("not enough stack space")
|
||||
errTooManyArguments = errors.New("too many arguments")
|
||||
errNotEnoughArguments = errors.New("not enough arguments")
|
||||
errNoAddrUnsupported = errors.New("arguments to a function call must have an address")
|
||||
errNotAGoFunction = errors.New("not a Go function")
|
||||
)
|
||||
|
||||
type functionCallState struct {
|
||||
@ -84,27 +84,27 @@ type functionCallState struct {
|
||||
func CallFunction(p Process, expr string, retLoadCfg *LoadConfig) error {
|
||||
bi := p.BinInfo()
|
||||
if !p.Common().fncallEnabled {
|
||||
return ErrFuncCallUnsupportedBackend
|
||||
return errFuncCallUnsupportedBackend
|
||||
}
|
||||
fncall := &p.Common().fncallState
|
||||
if fncall.inProgress {
|
||||
return ErrFuncCallInProgress
|
||||
return errFuncCallInProgress
|
||||
}
|
||||
|
||||
*fncall = functionCallState{}
|
||||
|
||||
dbgcallfn := bi.LookupFunc[debugCallFunctionName]
|
||||
if dbgcallfn == nil {
|
||||
return ErrFuncCallUnsupported
|
||||
return errFuncCallUnsupported
|
||||
}
|
||||
|
||||
// check that the selected goroutine is running
|
||||
g := p.SelectedGoroutine()
|
||||
if g == nil {
|
||||
return ErrNoGoroutine
|
||||
return errNoGoroutine
|
||||
}
|
||||
if g.Status != Grunning || g.Thread == nil {
|
||||
return ErrGoroutineNotRunning
|
||||
return errGoroutineNotRunning
|
||||
}
|
||||
|
||||
// check that there are at least 256 bytes free on the stack
|
||||
@ -114,11 +114,11 @@ func CallFunction(p Process, expr string, retLoadCfg *LoadConfig) error {
|
||||
}
|
||||
regs = regs.Copy()
|
||||
if regs.SP()-256 <= g.stacklo {
|
||||
return ErrNotEnoughStack
|
||||
return errNotEnoughStack
|
||||
}
|
||||
_, err = regs.Get(int(x86asm.RAX))
|
||||
if err != nil {
|
||||
return ErrFuncCallUnsupportedBackend
|
||||
return errFuncCallUnsupportedBackend
|
||||
}
|
||||
|
||||
fn, closureAddr, argvars, err := funcCallEvalExpr(p, expr)
|
||||
@ -208,7 +208,7 @@ func funcCallEvalExpr(p Process, expr string) (fn *Function, closureAddr uint64,
|
||||
}
|
||||
callexpr, iscall := t.(*ast.CallExpr)
|
||||
if !iscall {
|
||||
return nil, 0, nil, ErrNotACallExpr
|
||||
return nil, 0, nil, errNotACallExpr
|
||||
}
|
||||
|
||||
fnvar, err := scope.evalAST(callexpr.Fun)
|
||||
@ -230,7 +230,7 @@ func funcCallEvalExpr(p Process, expr string) (fn *Function, closureAddr uint64,
|
||||
return nil, 0, nil, fmt.Errorf("could not find DIE for function %q", exprToString(callexpr.Fun))
|
||||
}
|
||||
if !fn.cu.isgo {
|
||||
return nil, 0, nil, ErrNotAGoFunction
|
||||
return nil, 0, nil, errNotAGoFunction
|
||||
}
|
||||
|
||||
argvars = make([]*Variable, 0, len(callexpr.Args)+1)
|
||||
@ -265,10 +265,10 @@ func funcCallArgFrame(fn *Function, actualArgs []*Variable, g *G, bi *BinaryInfo
|
||||
return nil, err
|
||||
}
|
||||
if len(actualArgs) > len(formalArgs) {
|
||||
return nil, ErrTooManyArguments
|
||||
return nil, errTooManyArguments
|
||||
}
|
||||
if len(actualArgs) < len(formalArgs) {
|
||||
return nil, ErrNotEnoughArguments
|
||||
return nil, errNotEnoughArguments
|
||||
}
|
||||
|
||||
// constructs arguments frame
|
||||
|
@ -96,12 +96,14 @@ const (
|
||||
|
||||
const heartbeatInterval = 10 * time.Second
|
||||
|
||||
// ErrDirChange is returned when trying to change execution direction
|
||||
// while there are still internal breakpoints set.
|
||||
var ErrDirChange = errors.New("direction change with internal breakpoints")
|
||||
|
||||
// Process implements proc.Process using a connection to a debugger stub
|
||||
// that understands Gdb Remote Serial Protocol.
|
||||
type Process struct {
|
||||
bi proc.BinaryInfo
|
||||
bi *proc.BinaryInfo
|
||||
conn gdbConn
|
||||
|
||||
threads map[int]*Thread
|
||||
@ -374,7 +376,8 @@ func unusedPort() string {
|
||||
|
||||
const debugserverExecutable = "/Library/Developer/CommandLineTools/Library/PrivateFrameworks/LLDB.framework/Versions/A/Resources/debugserver"
|
||||
|
||||
var ErrUnsupportedOS = errors.New("lldb backend not supported on windows")
|
||||
// ErrUnsupportedOS is returned when trying to use the lldb backend on Windows.
|
||||
var ErrUnsupportedOS = errors.New("lldb backend not supported on Windows")
|
||||
|
||||
func getLdEnvVars() []string {
|
||||
var result []string
|
||||
@ -400,7 +403,7 @@ func LLDBLaunch(cmd []string, wd string, foreground bool) (*Process, error) {
|
||||
default:
|
||||
// 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.NotExecutableErr
|
||||
return nil, proc.ErrNotExecutable
|
||||
}
|
||||
}
|
||||
|
||||
@ -548,37 +551,47 @@ func (p *Process) loadProcessInfo(pid int) (int, string, error) {
|
||||
return pid, pi["name"], nil
|
||||
}
|
||||
|
||||
// BinInfo returns information on the binary.
|
||||
func (p *Process) BinInfo() *proc.BinaryInfo {
|
||||
return &p.bi
|
||||
return p.bi
|
||||
}
|
||||
|
||||
// Recorded returns whether or not we are debugging
|
||||
// a recorded "traced" program.
|
||||
func (p *Process) Recorded() (bool, string) {
|
||||
return p.tracedir != "", p.tracedir
|
||||
}
|
||||
|
||||
// Pid returns the process ID.
|
||||
func (p *Process) Pid() int {
|
||||
return int(p.conn.pid)
|
||||
}
|
||||
|
||||
// Valid returns true if we are not detached
|
||||
// and the process has not exited.
|
||||
func (p *Process) Valid() (bool, error) {
|
||||
if p.detached {
|
||||
return false, &proc.ProcessDetachedError{}
|
||||
}
|
||||
if p.exited {
|
||||
return false, &proc.ProcessExitedError{Pid: p.Pid()}
|
||||
return false, &proc.ErrProcessExited{Pid: p.Pid()}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ResumeNotify specifies a channel that will be closed the next time
|
||||
// ContinueOnce finishes resuming the target.
|
||||
func (p *Process) ResumeNotify(ch chan<- struct{}) {
|
||||
p.conn.resumeChan = ch
|
||||
}
|
||||
|
||||
// FindThread returns the thread with the given ID.
|
||||
func (p *Process) FindThread(threadID int) (proc.Thread, bool) {
|
||||
thread, ok := p.threads[threadID]
|
||||
return thread, ok
|
||||
}
|
||||
|
||||
// ThreadList returns all threads in the process.
|
||||
func (p *Process) ThreadList() []proc.Thread {
|
||||
r := make([]proc.Thread, 0, len(p.threads))
|
||||
for _, thread := range p.threads {
|
||||
@ -587,14 +600,18 @@ func (p *Process) ThreadList() []proc.Thread {
|
||||
return r
|
||||
}
|
||||
|
||||
// CurrentThread returns the current active
|
||||
// selected thread.
|
||||
func (p *Process) CurrentThread() proc.Thread {
|
||||
return p.currentThread
|
||||
}
|
||||
|
||||
// Common returns common information across Process implementations.
|
||||
func (p *Process) Common() *proc.CommonProcess {
|
||||
return &p.common
|
||||
}
|
||||
|
||||
// SelectedGoroutine returns the current actuve selected goroutine.
|
||||
func (p *Process) SelectedGoroutine() *proc.G {
|
||||
return p.selectedGoroutine
|
||||
}
|
||||
@ -606,9 +623,11 @@ const (
|
||||
stopSignal = 0x13
|
||||
)
|
||||
|
||||
// ContinueOnce will continue execution of the process until
|
||||
// a breakpoint is hit or signal is received.
|
||||
func (p *Process) ContinueOnce() (proc.Thread, error) {
|
||||
if p.exited {
|
||||
return nil, &proc.ProcessExitedError{Pid: p.conn.pid}
|
||||
return nil, &proc.ErrProcessExited{Pid: p.conn.pid}
|
||||
}
|
||||
|
||||
if p.conn.direction == proc.Forward {
|
||||
@ -639,7 +658,7 @@ continueLoop:
|
||||
tu.Reset()
|
||||
threadID, sig, err = p.conn.resume(sig, &tu)
|
||||
if err != nil {
|
||||
if _, exited := err.(proc.ProcessExitedError); exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); exited {
|
||||
p.exited = true
|
||||
}
|
||||
return nil, err
|
||||
@ -706,6 +725,7 @@ continueLoop:
|
||||
return nil, fmt.Errorf("could not find thread %s", threadID)
|
||||
}
|
||||
|
||||
// StepInstruction will step exactly one CPU instruction.
|
||||
func (p *Process) StepInstruction() error {
|
||||
thread := p.currentThread
|
||||
if p.selectedGoroutine != nil {
|
||||
@ -719,7 +739,7 @@ func (p *Process) StepInstruction() error {
|
||||
}
|
||||
p.common.ClearAllGCache()
|
||||
if p.exited {
|
||||
return &proc.ProcessExitedError{Pid: p.conn.pid}
|
||||
return &proc.ErrProcessExited{Pid: p.conn.pid}
|
||||
}
|
||||
thread.clearBreakpointState()
|
||||
err := thread.StepInstruction()
|
||||
@ -736,9 +756,10 @@ func (p *Process) StepInstruction() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SwitchThread will change the internal selected thread.
|
||||
func (p *Process) SwitchThread(tid int) error {
|
||||
if p.exited {
|
||||
return proc.ProcessExitedError{Pid: p.conn.pid}
|
||||
return proc.ErrProcessExited{Pid: p.conn.pid}
|
||||
}
|
||||
if th, ok := p.threads[tid]; ok {
|
||||
p.currentThread = th
|
||||
@ -748,6 +769,7 @@ func (p *Process) SwitchThread(tid int) error {
|
||||
return fmt.Errorf("thread %d does not exist", tid)
|
||||
}
|
||||
|
||||
// SwitchGoroutine will change the internal selected goroutine.
|
||||
func (p *Process) SwitchGoroutine(gid int) error {
|
||||
g, err := proc.FindGoroutine(p, gid)
|
||||
if err != nil {
|
||||
@ -764,6 +786,8 @@ func (p *Process) SwitchGoroutine(gid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestManualStop will attempt to stop the process
|
||||
// without a breakpoint or signal having been recieved.
|
||||
func (p *Process) RequestManualStop() error {
|
||||
p.conn.manualStopMutex.Lock()
|
||||
p.manualStopRequested = true
|
||||
@ -776,6 +800,8 @@ func (p *Process) RequestManualStop() error {
|
||||
return p.conn.sendCtrlC()
|
||||
}
|
||||
|
||||
// CheckAndClearManualStopRequest will check for a manual
|
||||
// stop and then clear that state.
|
||||
func (p *Process) CheckAndClearManualStopRequest() bool {
|
||||
p.conn.manualStopMutex.Lock()
|
||||
msr := p.manualStopRequested
|
||||
@ -796,11 +822,13 @@ func (p *Process) getCtrlC() bool {
|
||||
return p.ctrlC
|
||||
}
|
||||
|
||||
// Detach will detach from the target process,
|
||||
// if 'kill' is true it will also kill the process.
|
||||
func (p *Process) Detach(kill bool) error {
|
||||
if kill && !p.exited {
|
||||
err := p.conn.kill()
|
||||
if err != nil {
|
||||
if _, exited := err.(proc.ProcessExitedError); !exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); !exited {
|
||||
return err
|
||||
}
|
||||
p.exited = true
|
||||
@ -820,9 +848,10 @@ func (p *Process) Detach(kill bool) error {
|
||||
return p.bi.Close()
|
||||
}
|
||||
|
||||
// Restart will restart the process from the given position.
|
||||
func (p *Process) Restart(pos string) error {
|
||||
if p.tracedir == "" {
|
||||
return proc.NotRecordedErr
|
||||
return proc.ErrNotRecorded
|
||||
}
|
||||
|
||||
p.exited = false
|
||||
@ -859,9 +888,11 @@ func (p *Process) Restart(pos string) error {
|
||||
return p.setCurrentBreakpoints()
|
||||
}
|
||||
|
||||
// When executes the 'when' command for the Mozilla RR backend.
|
||||
// This command will return rr's internal event number.
|
||||
func (p *Process) When() (string, error) {
|
||||
if p.tracedir == "" {
|
||||
return "", proc.NotRecordedErr
|
||||
return "", proc.ErrNotRecorded
|
||||
}
|
||||
event, err := p.conn.qRRCmd("when")
|
||||
if err != nil {
|
||||
@ -874,9 +905,10 @@ const (
|
||||
checkpointPrefix = "Checkpoint "
|
||||
)
|
||||
|
||||
// Checkpoint creates a checkpoint from which you can restart the program.
|
||||
func (p *Process) Checkpoint(where string) (int, error) {
|
||||
if p.tracedir == "" {
|
||||
return -1, proc.NotRecordedErr
|
||||
return -1, proc.ErrNotRecorded
|
||||
}
|
||||
resp, err := p.conn.qRRCmd("checkpoint", where)
|
||||
if err != nil {
|
||||
@ -901,9 +933,10 @@ func (p *Process) Checkpoint(where string) (int, error) {
|
||||
return cpid, nil
|
||||
}
|
||||
|
||||
// Checkpoints returns a list of all checkpoints set.
|
||||
func (p *Process) Checkpoints() ([]proc.Checkpoint, error) {
|
||||
if p.tracedir == "" {
|
||||
return nil, proc.NotRecordedErr
|
||||
return nil, proc.ErrNotRecorded
|
||||
}
|
||||
resp, err := p.conn.qRRCmd("info checkpoints")
|
||||
if err != nil {
|
||||
@ -930,9 +963,10 @@ func (p *Process) Checkpoints() ([]proc.Checkpoint, error) {
|
||||
|
||||
const deleteCheckpointPrefix = "Deleted checkpoint "
|
||||
|
||||
// ClearCheckpoint clears the checkpoint for the given ID.
|
||||
func (p *Process) ClearCheckpoint(id int) error {
|
||||
if p.tracedir == "" {
|
||||
return proc.NotRecordedErr
|
||||
return proc.ErrNotRecorded
|
||||
}
|
||||
resp, err := p.conn.qRRCmd("delete checkpoint", strconv.Itoa(id))
|
||||
if err != nil {
|
||||
@ -944,12 +978,13 @@ func (p *Process) ClearCheckpoint(id int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Direction sets whether to run the program forwards or in reverse execution.
|
||||
func (p *Process) Direction(dir proc.Direction) error {
|
||||
if p.tracedir == "" {
|
||||
return proc.NotRecordedErr
|
||||
return proc.ErrNotRecorded
|
||||
}
|
||||
if p.conn.conn == nil {
|
||||
return proc.ProcessExitedError{Pid: p.conn.pid}
|
||||
return proc.ErrProcessExited{Pid: p.conn.pid}
|
||||
}
|
||||
if p.conn.direction == dir {
|
||||
return nil
|
||||
@ -961,10 +996,12 @@ func (p *Process) Direction(dir proc.Direction) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Breakpoints returns the list of breakpoints currently set.
|
||||
func (p *Process) Breakpoints() *proc.BreakpointMap {
|
||||
return &p.breakpoints
|
||||
}
|
||||
|
||||
// FindBreakpoint returns the breakpoint at the given address.
|
||||
func (p *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
|
||||
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
|
||||
if bp, ok := p.breakpoints.M[pc-uint64(p.bi.Arch.BreakpointSize())]; ok {
|
||||
@ -990,22 +1027,25 @@ func (p *Process) writeBreakpoint(addr uint64) (string, int, *proc.Function, []b
|
||||
return f, l, fn, nil, nil
|
||||
}
|
||||
|
||||
// SetBreakpoint creates a new breakpoint.
|
||||
func (p *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
|
||||
if p.exited {
|
||||
return nil, &proc.ProcessExitedError{Pid: p.conn.pid}
|
||||
return nil, &proc.ErrProcessExited{Pid: p.conn.pid}
|
||||
}
|
||||
return p.breakpoints.Set(addr, kind, cond, p.writeBreakpoint)
|
||||
}
|
||||
|
||||
// ClearBreakpoint clears a breakpoint at the given address.
|
||||
func (p *Process) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
|
||||
if p.exited {
|
||||
return nil, &proc.ProcessExitedError{Pid: p.conn.pid}
|
||||
return nil, &proc.ErrProcessExited{Pid: p.conn.pid}
|
||||
}
|
||||
return p.breakpoints.Clear(addr, func(bp *proc.Breakpoint) error {
|
||||
return p.conn.clearBreakpoint(bp.Addr)
|
||||
})
|
||||
}
|
||||
|
||||
// ClearInternalBreakpoints clear all internal use breakpoints like those set by 'next'.
|
||||
func (p *Process) ClearInternalBreakpoints() error {
|
||||
return p.breakpoints.ClearInternalBreakpoints(func(bp *proc.Breakpoint) error {
|
||||
if err := p.conn.clearBreakpoint(bp.Addr); err != nil {
|
||||
@ -1155,6 +1195,7 @@ func (p *Process) setCurrentBreakpoints() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadMemory will read into 'data' memory at the address provided.
|
||||
func (t *Thread) ReadMemory(data []byte, addr uintptr) (n int, err error) {
|
||||
err = t.p.conn.readMemory(data, addr)
|
||||
if err != nil {
|
||||
@ -1163,10 +1204,12 @@ func (t *Thread) ReadMemory(data []byte, addr uintptr) (n int, err error) {
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
// WriteMemory will write into the memory at 'addr' the data provided.
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (written int, err error) {
|
||||
return t.p.conn.writeMemory(addr, data)
|
||||
}
|
||||
|
||||
// Location returns the current location of this thread.
|
||||
func (t *Thread) Location() (*proc.Location, error) {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
@ -1177,31 +1220,38 @@ func (t *Thread) Location() (*proc.Location, error) {
|
||||
return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
|
||||
}
|
||||
|
||||
// Breakpoint returns the current active breakpoint for this thread.
|
||||
func (t *Thread) Breakpoint() proc.BreakpointState {
|
||||
return t.CurrentBreakpoint
|
||||
}
|
||||
|
||||
// ThreadID returns this threads ID.
|
||||
func (t *Thread) ThreadID() int {
|
||||
return t.ID
|
||||
}
|
||||
|
||||
// Registers returns the CPU registers for this thread.
|
||||
func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
|
||||
return &t.regs, nil
|
||||
}
|
||||
|
||||
// RestoreRegisters will set the CPU registers the value of those provided.
|
||||
func (t *Thread) RestoreRegisters(savedRegs proc.Registers) error {
|
||||
copy(t.regs.buf, savedRegs.(*gdbRegisters).buf)
|
||||
return t.writeRegisters()
|
||||
}
|
||||
|
||||
// Arch will return the CPU architecture for the target.
|
||||
func (t *Thread) Arch() proc.Arch {
|
||||
return t.p.bi.Arch
|
||||
}
|
||||
|
||||
// BinInfo will return information on the binary being debugged.
|
||||
func (t *Thread) BinInfo() *proc.BinaryInfo {
|
||||
return &t.p.bi
|
||||
return t.p.bi
|
||||
}
|
||||
|
||||
// Common returns common information across Process implementations.
|
||||
func (t *Thread) Common() *proc.CommonThread {
|
||||
return &t.common
|
||||
}
|
||||
@ -1219,6 +1269,7 @@ func (t *Thread) stepInstruction(tu *threadUpdater) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// StepInstruction will step exactly 1 CPU instruction.
|
||||
func (t *Thread) StepInstruction() error {
|
||||
if err := t.stepInstruction(&threadUpdater{p: t.p}); err != nil {
|
||||
return err
|
||||
@ -1226,6 +1277,7 @@ func (t *Thread) StepInstruction() error {
|
||||
return t.reloadRegisters()
|
||||
}
|
||||
|
||||
// Blocked returns true if the thread is blocked in runtime or kernel code.
|
||||
func (t *Thread) Blocked() bool {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
@ -1507,6 +1559,7 @@ func (t *Thread) clearBreakpointState() {
|
||||
t.CurrentBreakpoint.Clear()
|
||||
}
|
||||
|
||||
// SetCurrentBreakpoint will find and set the threads current breakpoint.
|
||||
func (thread *Thread) SetCurrentBreakpoint() error {
|
||||
thread.clearBreakpointState()
|
||||
regs, err := thread.Registers(false)
|
||||
@ -1732,9 +1785,10 @@ func (regs *gdbRegisters) Get(n int) (uint64, error) {
|
||||
return regs.byName("r15"), nil
|
||||
}
|
||||
|
||||
return 0, proc.UnknownRegisterError
|
||||
return 0, proc.ErrUnknownRegister
|
||||
}
|
||||
|
||||
// SetPC will set the value of the PC register to the given value.
|
||||
func (t *Thread) SetPC(pc uint64) error {
|
||||
t.regs.setPC(pc)
|
||||
if t.p.gcmdok {
|
||||
@ -1744,6 +1798,7 @@ func (t *Thread) SetPC(pc uint64) error {
|
||||
return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value)
|
||||
}
|
||||
|
||||
// SetSP will set the value of the SP register to the given value.
|
||||
func (t *Thread) SetSP(sp uint64) error {
|
||||
t.regs.setSP(sp)
|
||||
if t.p.gcmdok {
|
||||
@ -1753,6 +1808,7 @@ func (t *Thread) SetSP(sp uint64) error {
|
||||
return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value)
|
||||
}
|
||||
|
||||
// SetDX will set the value of the DX register to the given value.
|
||||
func (t *Thread) SetDX(dx uint64) error {
|
||||
t.regs.setDX(dx)
|
||||
if t.p.gcmdok {
|
||||
|
@ -426,7 +426,7 @@ func (conn *gdbConn) kill() error {
|
||||
// kill. This is not an error.
|
||||
conn.conn.Close()
|
||||
conn.conn = nil
|
||||
return proc.ProcessExitedError{Pid: conn.pid}
|
||||
return proc.ErrProcessExited{Pid: conn.pid}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
@ -684,7 +684,7 @@ func (conn *gdbConn) parseStopPacket(resp []byte, threadID string, tu *threadUpd
|
||||
semicolon = len(resp)
|
||||
}
|
||||
status, _ := strconv.ParseUint(string(resp[1:semicolon]), 16, 8)
|
||||
return false, stopPacket{}, proc.ProcessExitedError{Pid: conn.pid, Status: int(status)}
|
||||
return false, stopPacket{}, proc.ErrProcessExited{Pid: conn.pid, Status: int(status)}
|
||||
|
||||
case 'N':
|
||||
// we were singlestepping the thread and the thread exited
|
||||
|
@ -64,7 +64,7 @@ func TestRestartAfterExit(t *testing.T) {
|
||||
loc, err := p.CurrentThread().Location()
|
||||
assertNoError(err, t, "CurrentThread().Location()")
|
||||
err = proc.Continue(p)
|
||||
if _, isexited := err.(proc.ProcessExitedError); err == nil || !isexited {
|
||||
if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited {
|
||||
t.Fatalf("program did not exit: %v", err)
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ func TestRestartAfterExit(t *testing.T) {
|
||||
t.Fatalf("stopped at %d (expected %d)", loc2.Line, loc.Line)
|
||||
}
|
||||
err = proc.Continue(p)
|
||||
if _, isexited := err.(proc.ProcessExitedError); err == nil || !isexited {
|
||||
if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited {
|
||||
t.Fatalf("program did not exit (after exit): %v", err)
|
||||
}
|
||||
})
|
||||
@ -100,7 +100,7 @@ func TestRestartDuringStop(t *testing.T) {
|
||||
t.Fatalf("stopped at %d (expected %d)", loc2.Line, loc.Line)
|
||||
}
|
||||
err = proc.Continue(p)
|
||||
if _, isexited := err.(proc.ProcessExitedError); err == nil || !isexited {
|
||||
if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited {
|
||||
t.Fatalf("program did not exit (after exit): %v", err)
|
||||
}
|
||||
})
|
||||
|
@ -40,10 +40,13 @@ type RecordingManipulation interface {
|
||||
ClearCheckpoint(id int) error
|
||||
}
|
||||
|
||||
// Direction is the direction of execution for the target process.
|
||||
type Direction int8
|
||||
|
||||
const (
|
||||
Forward Direction = 0
|
||||
// Forward direction executes the target normally.
|
||||
Forward Direction = 0
|
||||
// Backward direction executes the target in reverse.
|
||||
Backward Direction = 1
|
||||
)
|
||||
|
||||
@ -62,7 +65,7 @@ type Info interface {
|
||||
ResumeNotify(chan<- struct{})
|
||||
// Valid returns true if this Process can be used. When it returns false it
|
||||
// also returns an error describing why the Process is invalid (either
|
||||
// ProcessExitedError or ProcessDetachedError).
|
||||
// ErrProcessExited or ProcessDetachedError).
|
||||
Valid() (bool, error)
|
||||
BinInfo() *BinaryInfo
|
||||
// Common returns a struct with fields common to all backends
|
||||
@ -114,6 +117,8 @@ type CommonProcess struct {
|
||||
fncallEnabled bool
|
||||
}
|
||||
|
||||
// NewCommonProcess returns a struct with fields common across
|
||||
// all process implementations.
|
||||
func NewCommonProcess(fncallEnabled bool) CommonProcess {
|
||||
return CommonProcess{fncallEnabled: fncallEnabled}
|
||||
}
|
||||
|
@ -17,6 +17,9 @@ type MemoryReader interface {
|
||||
ReadMemory(buf []byte, addr uintptr) (n int, err error)
|
||||
}
|
||||
|
||||
// MemoryReadWriter is an interface for reading or writing to
|
||||
// the targets memory. This allows us to read from the actual
|
||||
// target memory or possibly a cache.
|
||||
type MemoryReadWriter interface {
|
||||
MemoryReader
|
||||
WriteMemory(addr uintptr, data []byte) (written int, err error)
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
// Process represents all of the information the debugger
|
||||
// is holding onto regarding the process we are debugging.
|
||||
type Process struct {
|
||||
bi proc.BinaryInfo
|
||||
bi *proc.BinaryInfo
|
||||
pid int // Process Pid
|
||||
|
||||
// Breakpoint table, holds information on breakpoints.
|
||||
@ -61,17 +61,36 @@ func New(pid int) *Process {
|
||||
return dbp
|
||||
}
|
||||
|
||||
// BinInfo will return the binary info struct associated with this process.
|
||||
func (dbp *Process) BinInfo() *proc.BinaryInfo {
|
||||
return &dbp.bi
|
||||
return dbp.bi
|
||||
}
|
||||
|
||||
func (dbp *Process) Recorded() (bool, string) { return false, "" }
|
||||
func (dbp *Process) Restart(string) error { return proc.NotRecordedErr }
|
||||
func (dbp *Process) Direction(proc.Direction) error { return proc.NotRecordedErr }
|
||||
func (dbp *Process) When() (string, error) { return "", nil }
|
||||
func (dbp *Process) Checkpoint(string) (int, error) { return -1, proc.NotRecordedErr }
|
||||
func (dbp *Process) Checkpoints() ([]proc.Checkpoint, error) { return nil, proc.NotRecordedErr }
|
||||
func (dbp *Process) ClearCheckpoint(int) error { return proc.NotRecordedErr }
|
||||
// Recorded always returns false for the native proc backend.
|
||||
func (dbp *Process) Recorded() (bool, string) { return false, "" }
|
||||
|
||||
// Restart will always return an error in the native proc backend, only for
|
||||
// recorded traces.
|
||||
func (dbp *Process) Restart(string) error { return proc.ErrNotRecorded }
|
||||
|
||||
// Direction will always return an error in the native proc backend, only for
|
||||
// recorded traces.
|
||||
func (dbp *Process) Direction(proc.Direction) error { return proc.ErrNotRecorded }
|
||||
|
||||
// When will always return an empty string and nil, not supported on native proc backend.
|
||||
func (dbp *Process) When() (string, error) { return "", nil }
|
||||
|
||||
// Checkpoint will always return an error on the native proc backend,
|
||||
// only supported for recorded traces.
|
||||
func (dbp *Process) Checkpoint(string) (int, error) { return -1, proc.ErrNotRecorded }
|
||||
|
||||
// Checkpoints will always return an error on the native proc backend,
|
||||
// only supported for recorded traces.
|
||||
func (dbp *Process) Checkpoints() ([]proc.Checkpoint, error) { return nil, proc.ErrNotRecorded }
|
||||
|
||||
// ClearCheckpoint will always return an error on the native proc backend,
|
||||
// only supported in recorded traces.
|
||||
func (dbp *Process) ClearCheckpoint(int) error { return proc.ErrNotRecorded }
|
||||
|
||||
// Detach from the process being debugged, optionally killing it.
|
||||
func (dbp *Process) Detach(kill bool) (err error) {
|
||||
@ -111,28 +130,36 @@ func (dbp *Process) Detach(kill bool) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Valid returns whether the process is still attached to and
|
||||
// has not exited.
|
||||
func (dbp *Process) Valid() (bool, error) {
|
||||
if dbp.detached {
|
||||
return false, &proc.ProcessDetachedError{}
|
||||
}
|
||||
if dbp.exited {
|
||||
return false, &proc.ProcessExitedError{Pid: dbp.Pid()}
|
||||
return false, &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ResumeNotify specifies a channel that will be closed the next time
|
||||
// ContinueOnce finishes resuming the target.
|
||||
func (dbp *Process) ResumeNotify(ch chan<- struct{}) {
|
||||
dbp.resumeChan = ch
|
||||
}
|
||||
|
||||
// Pid returns the process ID.
|
||||
func (dbp *Process) Pid() int {
|
||||
return dbp.pid
|
||||
}
|
||||
|
||||
// SelectedGoroutine returns the current selected,
|
||||
// active goroutine.
|
||||
func (dbp *Process) SelectedGoroutine() *proc.G {
|
||||
return dbp.selectedGoroutine
|
||||
}
|
||||
|
||||
// ThreadList returns a list of threads in the process.
|
||||
func (dbp *Process) ThreadList() []proc.Thread {
|
||||
r := make([]proc.Thread, 0, len(dbp.threads))
|
||||
for _, v := range dbp.threads {
|
||||
@ -141,15 +168,18 @@ func (dbp *Process) ThreadList() []proc.Thread {
|
||||
return r
|
||||
}
|
||||
|
||||
// FindThread attempts to find the thread with the specified ID.
|
||||
func (dbp *Process) FindThread(threadID int) (proc.Thread, bool) {
|
||||
th, ok := dbp.threads[threadID]
|
||||
return th, ok
|
||||
}
|
||||
|
||||
// CurrentThread returns the current selected, active thread.
|
||||
func (dbp *Process) CurrentThread() proc.Thread {
|
||||
return dbp.currentThread
|
||||
}
|
||||
|
||||
// Breakpoints returns a list of breakpoints currently set.
|
||||
func (dbp *Process) Breakpoints() *proc.BreakpointMap {
|
||||
return &dbp.breakpoints
|
||||
}
|
||||
@ -179,7 +209,7 @@ func (dbp *Process) LoadInformation(path string) error {
|
||||
// sends SIGSTOP to all threads.
|
||||
func (dbp *Process) RequestManualStop() error {
|
||||
if dbp.exited {
|
||||
return &proc.ProcessExitedError{Pid: dbp.Pid()}
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
dbp.stopMu.Lock()
|
||||
defer dbp.stopMu.Unlock()
|
||||
@ -187,11 +217,15 @@ func (dbp *Process) RequestManualStop() error {
|
||||
return dbp.requestManualStop()
|
||||
}
|
||||
|
||||
// CheckAndClearManualStopRequest checks if a manual stop has
|
||||
// been requested, and then clears that state.
|
||||
func (dbp *Process) CheckAndClearManualStopRequest() bool {
|
||||
dbp.stopMu.Lock()
|
||||
defer dbp.stopMu.Unlock()
|
||||
|
||||
msr := dbp.manualStopRequested
|
||||
dbp.manualStopRequested = false
|
||||
dbp.stopMu.Unlock()
|
||||
|
||||
return msr
|
||||
}
|
||||
|
||||
@ -222,14 +256,16 @@ func (dbp *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond as
|
||||
// ClearBreakpoint clears the breakpoint at addr.
|
||||
func (dbp *Process) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
|
||||
if dbp.exited {
|
||||
return nil, &proc.ProcessExitedError{Pid: dbp.Pid()}
|
||||
return nil, &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
return dbp.breakpoints.Clear(addr, dbp.currentThread.ClearBreakpoint)
|
||||
}
|
||||
|
||||
// ContinueOnce will continue the target until it stops.
|
||||
// This could be the result of a breakpoint or signal.
|
||||
func (dbp *Process) ContinueOnce() (proc.Thread, error) {
|
||||
if dbp.exited {
|
||||
return nil, &proc.ProcessExitedError{Pid: dbp.Pid()}
|
||||
return nil, &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
|
||||
if err := dbp.resume(); err != nil {
|
||||
@ -274,7 +310,7 @@ func (dbp *Process) StepInstruction() (err error) {
|
||||
}
|
||||
dbp.common.ClearAllGCache()
|
||||
if dbp.exited {
|
||||
return &proc.ProcessExitedError{Pid: dbp.Pid()}
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
thread.CurrentBreakpoint.Clear()
|
||||
err = thread.StepInstruction()
|
||||
@ -294,7 +330,7 @@ func (dbp *Process) StepInstruction() (err error) {
|
||||
// SwitchThread changes from current thread to the thread specified by `tid`.
|
||||
func (dbp *Process) SwitchThread(tid int) error {
|
||||
if dbp.exited {
|
||||
return &proc.ProcessExitedError{Pid: dbp.Pid()}
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
if th, ok := dbp.threads[tid]; ok {
|
||||
dbp.currentThread = th
|
||||
@ -308,7 +344,7 @@ func (dbp *Process) SwitchThread(tid int) error {
|
||||
// running the specified goroutine.
|
||||
func (dbp *Process) SwitchGoroutine(gid int) error {
|
||||
if dbp.exited {
|
||||
return &proc.ProcessExitedError{Pid: dbp.Pid()}
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
g, err := proc.FindGoroutine(dbp, gid)
|
||||
if err != nil {
|
||||
@ -360,6 +396,8 @@ func initializeDebugProcess(dbp *Process, path string) (*Process, error) {
|
||||
return dbp, nil
|
||||
}
|
||||
|
||||
// ClearInternalBreakpoints will clear all non-user set breakpoints. These
|
||||
// breakpoints are set for internal operations such as 'next'.
|
||||
func (dbp *Process) ClearInternalBreakpoints() error {
|
||||
return dbp.breakpoints.ClearInternalBreakpoints(func(bp *proc.Breakpoint) error {
|
||||
if err := dbp.currentThread.ClearBreakpoint(bp); err != nil {
|
||||
@ -403,6 +441,8 @@ func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Common returns common information across Process
|
||||
// implementations
|
||||
func (dbp *Process) Common() *proc.CommonProcess {
|
||||
return &dbp.common
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ type OSProcessDetails struct {
|
||||
func Launch(cmd []string, wd string, foreground bool) (*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.NotExecutableErr
|
||||
return nil, proc.ErrNotExecutable
|
||||
}
|
||||
argv0Go, err := filepath.Abs(cmd[0])
|
||||
if err != nil {
|
||||
@ -305,7 +305,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
return nil, err
|
||||
}
|
||||
dbp.postExit()
|
||||
return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
|
||||
return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: status.ExitStatus()}
|
||||
|
||||
case C.MACH_RCV_INTERRUPTED:
|
||||
dbp.stopMu.Lock()
|
||||
@ -402,7 +402,7 @@ func (dbp *Process) exitGuard(err error) error {
|
||||
_, status, werr := dbp.wait(dbp.pid, sys.WNOHANG)
|
||||
if werr == nil && status.Exited() {
|
||||
dbp.postExit()
|
||||
return proc.ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
|
||||
return proc.ErrProcessExited{Pid: dbp.pid, Status: status.ExitStatus()}
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -429,7 +429,7 @@ func (dbp *Process) resume() error {
|
||||
// stop stops all running threads and sets breakpoints
|
||||
func (dbp *Process) stop(trapthread *Thread) (err error) {
|
||||
if dbp.exited {
|
||||
return &proc.ProcessExitedError{Pid: dbp.Pid()}
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
for _, th := range dbp.threads {
|
||||
if !th.Stopped() {
|
||||
|
@ -52,7 +52,7 @@ func Launch(cmd []string, wd string, foreground bool) (*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.NotExecutableErr
|
||||
return nil, proc.ErrNotExecutable
|
||||
}
|
||||
|
||||
if !isatty.IsTerminal(os.Stdin.Fd()) {
|
||||
@ -228,7 +228,7 @@ func (dbp *Process) trapWaitInternal(pid int, halt bool) (*Thread, error) {
|
||||
if status.Exited() {
|
||||
if wpid == dbp.pid {
|
||||
dbp.postExit()
|
||||
return nil, proc.ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
|
||||
return nil, proc.ErrProcessExited{Pid: wpid, Status: status.ExitStatus()}
|
||||
}
|
||||
delete(dbp.threads, wpid)
|
||||
continue
|
||||
@ -286,7 +286,7 @@ func (dbp *Process) trapWaitInternal(pid int, halt bool) (*Thread, error) {
|
||||
// TODO(dp) alert user about unexpected signals here.
|
||||
if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
|
||||
if err == sys.ESRCH {
|
||||
return nil, proc.ProcessExitedError{Pid: dbp.pid}
|
||||
return nil, proc.ErrProcessExited{Pid: dbp.pid}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@ -418,7 +418,7 @@ func (dbp *Process) resume() error {
|
||||
// stop stops all running threads threads and sets breakpoints
|
||||
func (dbp *Process) stop(trapthread *Thread) (err error) {
|
||||
if dbp.exited {
|
||||
return &proc.ProcessExitedError{Pid: dbp.Pid()}
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
for _, th := range dbp.threads {
|
||||
if !th.Stopped() {
|
||||
|
@ -51,7 +51,7 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
|
||||
|
||||
_, closer, err := openExecutablePathPE(argv0Go)
|
||||
if err != nil {
|
||||
return nil, proc.NotExecutableErr
|
||||
return nil, proc.ErrNotExecutable
|
||||
}
|
||||
closer.Close()
|
||||
|
||||
@ -96,7 +96,7 @@ func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
|
||||
}
|
||||
if tid == 0 {
|
||||
dbp.postExit()
|
||||
return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: exitCode}
|
||||
return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: exitCode}
|
||||
}
|
||||
// Suspend all threads so that the call to _ContinueDebugEvent will
|
||||
// not resume the target.
|
||||
@ -378,7 +378,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
}
|
||||
if tid == 0 {
|
||||
dbp.postExit()
|
||||
return nil, proc.ProcessExitedError{Pid: dbp.pid, Status: exitCode}
|
||||
return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: exitCode}
|
||||
}
|
||||
th := dbp.threads[tid]
|
||||
return th, nil
|
||||
@ -419,7 +419,7 @@ func (dbp *Process) resume() error {
|
||||
// stop stops all running threads threads and sets breakpoints
|
||||
func (dbp *Process) stop(trapthread *Thread) (err error) {
|
||||
if dbp.exited {
|
||||
return &proc.ProcessExitedError{Pid: dbp.Pid()}
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
|
||||
// While the debug event that stopped the target was being propagated
|
||||
|
@ -284,7 +284,7 @@ func (r *Regs) Get(n int) (uint64, error) {
|
||||
return r.r15, nil
|
||||
}
|
||||
|
||||
return 0, proc.UnknownRegisterError
|
||||
return 0, proc.ErrUnknownRegister
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
||||
|
@ -281,7 +281,7 @@ func (r *Regs) Get(n int) (uint64, error) {
|
||||
return r.regs.R15, nil
|
||||
}
|
||||
|
||||
return 0, proc.UnknownRegisterError
|
||||
return 0, proc.ErrUnknownRegister
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
||||
@ -324,6 +324,8 @@ func (thread *Thread) fpRegisters() (regs []proc.Register, fpregs proc.LinuxX86X
|
||||
return
|
||||
}
|
||||
|
||||
// Copy returns a copy of these registers that is
|
||||
// guarenteed not to change.
|
||||
func (r *Regs) Copy() proc.Registers {
|
||||
var rr Regs
|
||||
rr.regs = &sys.PtraceRegs{}
|
||||
|
@ -329,7 +329,7 @@ func (r *Regs) Get(n int) (uint64, error) {
|
||||
return r.r15, nil
|
||||
}
|
||||
|
||||
return 0, proc.UnknownRegisterError
|
||||
return 0, proc.ErrUnknownRegister
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
|
||||
|
@ -27,19 +27,19 @@ type Thread struct {
|
||||
// If we are currently at a breakpoint, we'll clear it
|
||||
// first and then resume execution. Thread will continue until
|
||||
// it hits a breakpoint or is signaled.
|
||||
func (thread *Thread) Continue() error {
|
||||
pc, err := thread.PC()
|
||||
func (t *Thread) Continue() error {
|
||||
pc, err := t.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check whether we are stopped at a breakpoint, and
|
||||
// if so, single step over it before continuing.
|
||||
if _, ok := thread.dbp.FindBreakpoint(pc); ok {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
if _, ok := t.dbp.FindBreakpoint(pc); ok {
|
||||
if err := t.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return thread.resume()
|
||||
return t.resume()
|
||||
}
|
||||
|
||||
// StepInstruction steps a single instruction.
|
||||
@ -48,33 +48,33 @@ func (thread *Thread) Continue() error {
|
||||
// If the thread is at a breakpoint, we first clear it,
|
||||
// execute the instruction, and then replace the breakpoint.
|
||||
// Otherwise we simply execute the next instruction.
|
||||
func (thread *Thread) StepInstruction() (err error) {
|
||||
thread.singleStepping = true
|
||||
func (t *Thread) StepInstruction() (err error) {
|
||||
t.singleStepping = true
|
||||
defer func() {
|
||||
thread.singleStepping = false
|
||||
t.singleStepping = false
|
||||
}()
|
||||
pc, err := thread.PC()
|
||||
pc, err := t.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bp, ok := thread.dbp.FindBreakpoint(pc)
|
||||
bp, ok := t.dbp.FindBreakpoint(pc)
|
||||
if ok {
|
||||
// Clear the breakpoint so that we can continue execution.
|
||||
err = thread.ClearBreakpoint(bp)
|
||||
err = t.ClearBreakpoint(bp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Restore breakpoint now that we have passed it.
|
||||
defer func() {
|
||||
err = thread.dbp.writeSoftwareBreakpoint(thread, bp.Addr)
|
||||
err = t.dbp.writeSoftwareBreakpoint(t, bp.Addr)
|
||||
}()
|
||||
}
|
||||
|
||||
err = thread.singleStep()
|
||||
err = t.singleStep()
|
||||
if err != nil {
|
||||
if _, exited := err.(proc.ProcessExitedError); exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); exited {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("step failed: %s", err.Error())
|
||||
@ -85,61 +85,69 @@ func (thread *Thread) StepInstruction() (err error) {
|
||||
// Location returns the threads location, including the file:line
|
||||
// of the corresponding source code, the function we're in
|
||||
// and the current instruction address.
|
||||
func (thread *Thread) Location() (*proc.Location, error) {
|
||||
pc, err := thread.PC()
|
||||
func (t *Thread) Location() (*proc.Location, error) {
|
||||
pc, err := t.PC()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, l, fn := thread.dbp.bi.PCToLine(pc)
|
||||
f, l, fn := t.dbp.bi.PCToLine(pc)
|
||||
return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) Arch() proc.Arch {
|
||||
return thread.dbp.bi.Arch
|
||||
// Arch returns the architecture the binary is
|
||||
// compiled for and executing on.
|
||||
func (t *Thread) Arch() proc.Arch {
|
||||
return t.dbp.bi.Arch
|
||||
}
|
||||
|
||||
func (thread *Thread) BinInfo() *proc.BinaryInfo {
|
||||
return &thread.dbp.bi
|
||||
// BinInfo returns information on the binary.
|
||||
func (t *Thread) BinInfo() *proc.BinaryInfo {
|
||||
return t.dbp.bi
|
||||
}
|
||||
|
||||
func (thread *Thread) Common() *proc.CommonThread {
|
||||
return &thread.common
|
||||
// Common returns information common across Process
|
||||
// implementations.
|
||||
func (t *Thread) Common() *proc.CommonThread {
|
||||
return &t.common
|
||||
}
|
||||
|
||||
// SetCurrentBreakpoint sets the current breakpoint that this
|
||||
// thread is stopped at as CurrentBreakpoint on the thread struct.
|
||||
func (thread *Thread) SetCurrentBreakpoint() error {
|
||||
thread.CurrentBreakpoint.Clear()
|
||||
pc, err := thread.PC()
|
||||
func (t *Thread) SetCurrentBreakpoint() error {
|
||||
t.CurrentBreakpoint.Clear()
|
||||
pc, err := t.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bp, ok := thread.dbp.FindBreakpoint(pc); ok {
|
||||
if err = thread.SetPC(bp.Addr); err != nil {
|
||||
if bp, ok := t.dbp.FindBreakpoint(pc); ok {
|
||||
if err = t.SetPC(bp.Addr); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.CurrentBreakpoint = bp.CheckCondition(thread)
|
||||
if thread.CurrentBreakpoint.Breakpoint != nil && thread.CurrentBreakpoint.Active {
|
||||
if g, err := proc.GetG(thread); err == nil {
|
||||
thread.CurrentBreakpoint.HitCount[g.ID]++
|
||||
t.CurrentBreakpoint = bp.CheckCondition(t)
|
||||
if t.CurrentBreakpoint.Breakpoint != nil && t.CurrentBreakpoint.Active {
|
||||
if g, err := proc.GetG(t); err == nil {
|
||||
t.CurrentBreakpoint.HitCount[g.ID]++
|
||||
}
|
||||
thread.CurrentBreakpoint.TotalHitCount++
|
||||
t.CurrentBreakpoint.TotalHitCount++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (th *Thread) Breakpoint() proc.BreakpointState {
|
||||
return th.CurrentBreakpoint
|
||||
// Breakpoint returns the current breakpoint that is active
|
||||
// on this thread.
|
||||
func (t *Thread) Breakpoint() proc.BreakpointState {
|
||||
return t.CurrentBreakpoint
|
||||
}
|
||||
|
||||
func (th *Thread) ThreadID() int {
|
||||
return th.ID
|
||||
// ThreadID returns the ID of this thread.
|
||||
func (t *Thread) ThreadID() int {
|
||||
return t.ID
|
||||
}
|
||||
|
||||
// ClearBreakpoint clears the specified breakpoint.
|
||||
func (thread *Thread) ClearBreakpoint(bp *proc.Breakpoint) error {
|
||||
if _, err := thread.WriteMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
|
||||
func (t *Thread) ClearBreakpoint(bp *proc.Breakpoint) error {
|
||||
if _, err := t.WriteMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
|
||||
return fmt.Errorf("could not clear breakpoint %s", err)
|
||||
}
|
||||
return nil
|
||||
@ -150,10 +158,13 @@ func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
|
||||
return registers(t, floatingPoint)
|
||||
}
|
||||
|
||||
// RestoreRegisters will set the value of the CPU registers to those
|
||||
// passed in via 'savedRegs'.
|
||||
func (t *Thread) RestoreRegisters(savedRegs proc.Registers) error {
|
||||
return t.restoreRegisters(savedRegs)
|
||||
}
|
||||
|
||||
// PC returns the current program counter value for this thread.
|
||||
func (t *Thread) PC() (uint64, error) {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
|
@ -110,7 +110,7 @@ func (t *Thread) Stopped() bool {
|
||||
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ProcessExitedError{Pid: t.dbp.pid}
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return 0, nil
|
||||
@ -128,7 +128,7 @@ func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
|
||||
|
||||
func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ProcessExitedError{Pid: t.dbp.pid}
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
return 0, nil
|
||||
|
@ -61,7 +61,7 @@ func (t *Thread) singleStep() (err error) {
|
||||
if status != nil {
|
||||
rs = status.ExitStatus()
|
||||
}
|
||||
return proc.ProcessExitedError{Pid: t.dbp.pid, Status: rs}
|
||||
return proc.ErrProcessExited{Pid: t.dbp.pid, Status: rs}
|
||||
}
|
||||
if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
|
||||
return nil
|
||||
@ -108,7 +108,7 @@ func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
|
||||
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (written int, err error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ProcessExitedError{Pid: t.dbp.pid}
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return
|
||||
@ -119,7 +119,7 @@ func (t *Thread) WriteMemory(addr uintptr, data []byte) (written int, err error)
|
||||
|
||||
func (t *Thread) ReadMemory(data []byte, addr uintptr) (n int, err error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ProcessExitedError{Pid: t.dbp.pid}
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return
|
||||
|
@ -50,7 +50,7 @@ func (t *Thread) singleStep() error {
|
||||
}
|
||||
if tid == 0 {
|
||||
t.dbp.postExit()
|
||||
return proc.ProcessExitedError{Pid: t.dbp.pid, Status: exitCode}
|
||||
return proc.ErrProcessExited{Pid: t.dbp.pid, Status: exitCode}
|
||||
}
|
||||
|
||||
if t.dbp.os.breakThread == t.ID {
|
||||
@ -123,7 +123,7 @@ func (t *Thread) Stopped() bool {
|
||||
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ProcessExitedError{Pid: t.dbp.pid}
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return 0, nil
|
||||
@ -140,7 +140,7 @@ var ErrShortRead = errors.New("short read")
|
||||
|
||||
func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ProcessExitedError{Pid: t.dbp.pid}
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
return 0, nil
|
||||
|
@ -11,19 +11,25 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var NotExecutableErr = errors.New("not an executable file")
|
||||
var NotRecordedErr = errors.New("not a recording")
|
||||
// ErrNotExecutable is returned after attempting to execute a non-executable file
|
||||
// to begin a debug session.
|
||||
var ErrNotExecutable = errors.New("not an executable file")
|
||||
|
||||
// ErrNotRecorded is returned when an action is requested that is
|
||||
// only possible on recorded (traced) programs.
|
||||
var ErrNotRecorded = errors.New("not a recording")
|
||||
|
||||
// UnrecoveredPanic is the name given to the unrecovered panic breakpoint.
|
||||
const UnrecoveredPanic = "unrecovered-panic"
|
||||
|
||||
// ProcessExitedError indicates that the process has exited and contains both
|
||||
// ErrProcessExited indicates that the process has exited and contains both
|
||||
// process id and exit status.
|
||||
type ProcessExitedError struct {
|
||||
type ErrProcessExited struct {
|
||||
Pid int
|
||||
Status int
|
||||
}
|
||||
|
||||
func (pe ProcessExitedError) Error() string {
|
||||
func (pe ErrProcessExited) Error() string {
|
||||
return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status)
|
||||
}
|
||||
|
||||
@ -48,11 +54,13 @@ func FindFileLocation(p Process, fileName string, lineno int) (uint64, error) {
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
type FunctionNotFoundError struct {
|
||||
// ErrFunctionNotFound is returned when failing to find the
|
||||
// function named 'FuncName' within the binary.
|
||||
type ErrFunctionNotFound struct {
|
||||
FuncName string
|
||||
}
|
||||
|
||||
func (err *FunctionNotFoundError) Error() string {
|
||||
func (err *ErrFunctionNotFound) Error() string {
|
||||
return fmt.Sprintf("Could not find function %s\n", err.FuncName)
|
||||
}
|
||||
|
||||
@ -66,7 +74,7 @@ func FindFunctionLocation(p Process, funcName string, firstLine bool, lineOffset
|
||||
bi := p.BinInfo()
|
||||
origfn := bi.LookupFunc[funcName]
|
||||
if origfn == nil {
|
||||
return 0, &FunctionNotFoundError{funcName}
|
||||
return 0, &ErrFunctionNotFound{funcName}
|
||||
}
|
||||
|
||||
if firstLine {
|
||||
@ -290,7 +298,7 @@ func Step(dbp Process) (err error) {
|
||||
|
||||
if err = next(dbp, true, false); err != nil {
|
||||
switch err.(type) {
|
||||
case ThreadBlockedError: // Noop
|
||||
case ErrThreadBlocked: // Noop
|
||||
default:
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return
|
||||
@ -378,7 +386,7 @@ func StepOut(dbp Process) error {
|
||||
sameGCond := SameGoroutineCondition(selg)
|
||||
retFrameCond := andFrameoffCondition(sameGCond, retframe.FrameOffset())
|
||||
|
||||
var deferpc uint64 = 0
|
||||
var deferpc uint64
|
||||
if filepath.Ext(topframe.Current.File) == ".go" {
|
||||
if topframe.TopmostDefer != nil && topframe.TopmostDefer.DeferredPC != 0 {
|
||||
deferfn := dbp.BinInfo().PCToFunc(topframe.TopmostDefer.DeferredPC)
|
||||
@ -593,11 +601,11 @@ func FrameToScope(bi *BinaryInfo, thread MemoryReadWriter, g *G, frames ...Stack
|
||||
return s
|
||||
}
|
||||
|
||||
// CreateUnrecoverablePanicBreakpoint creates the unrecoverable-panic breakpoint.
|
||||
// CreateUnrecoveredPanicBreakpoint creates the unrecoverable-panic breakpoint.
|
||||
// This function is meant to be called by implementations of the Process interface.
|
||||
func CreateUnrecoveredPanicBreakpoint(p Process, writeBreakpoint writeBreakpointFn, breakpoints *BreakpointMap) {
|
||||
panicpc, err := FindFunctionLocation(p, "runtime.startpanic", true, 0)
|
||||
if _, isFnNotFound := err.(*FunctionNotFoundError); isFnNotFound {
|
||||
if _, isFnNotFound := err.(*ErrFunctionNotFound); isFnNotFound {
|
||||
panicpc, err = FindFunctionLocation(p, "runtime.fatalpanic", true, 0)
|
||||
}
|
||||
if err == nil {
|
||||
@ -619,11 +627,10 @@ func FirstPCAfterPrologue(p Process, fn *Function, sameline bool) (uint64, error
|
||||
if ok {
|
||||
if !sameline {
|
||||
return pc, nil
|
||||
} else {
|
||||
_, entryLine := fn.cu.lineInfo.PCToLine(fn.Entry, fn.Entry)
|
||||
if entryLine == line {
|
||||
return pc, nil
|
||||
}
|
||||
}
|
||||
_, entryLine := fn.cu.lineInfo.PCToLine(fn.Entry, fn.Entry)
|
||||
if entryLine == line {
|
||||
return pc, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ func TestExit(t *testing.T) {
|
||||
protest.AllowRecording(t)
|
||||
withTestProcess("continuetestprog", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
err := proc.Continue(p)
|
||||
pe, ok := err.(proc.ProcessExitedError)
|
||||
pe, ok := err.(proc.ErrProcessExited)
|
||||
if !ok {
|
||||
t.Fatalf("Continue() returned unexpected error type %s", err)
|
||||
}
|
||||
@ -155,7 +155,7 @@ func TestExitAfterContinue(t *testing.T) {
|
||||
assertNoError(err, t, "setFunctionBreakpoint()")
|
||||
assertNoError(proc.Continue(p), t, "First Continue()")
|
||||
err = proc.Continue(p)
|
||||
pe, ok := err.(proc.ProcessExitedError)
|
||||
pe, ok := err.(proc.ErrProcessExited)
|
||||
if !ok {
|
||||
t.Fatalf("Continue() returned unexpected error type %s", pe)
|
||||
}
|
||||
@ -1068,7 +1068,7 @@ func TestProcessReceivesSIGCHLD(t *testing.T) {
|
||||
protest.AllowRecording(t)
|
||||
withTestProcess("sigchldprog", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
err := proc.Continue(p)
|
||||
_, ok := err.(proc.ProcessExitedError)
|
||||
_, ok := err.(proc.ErrProcessExited)
|
||||
if !ok {
|
||||
t.Fatalf("Continue() returned unexpected error type %v", err)
|
||||
}
|
||||
@ -1384,7 +1384,7 @@ func TestBreakpointCounts(t *testing.T) {
|
||||
|
||||
for {
|
||||
if err := proc.Continue(p); err != nil {
|
||||
if _, exited := err.(proc.ProcessExitedError); exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); exited {
|
||||
break
|
||||
}
|
||||
assertNoError(err, t, "Continue()")
|
||||
@ -1436,7 +1436,7 @@ func TestBreakpointCountsWithDetection(t *testing.T) {
|
||||
|
||||
for {
|
||||
if err := proc.Continue(p); err != nil {
|
||||
if _, exited := err.(proc.ProcessExitedError); exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); exited {
|
||||
break
|
||||
}
|
||||
assertNoError(err, t, "Continue()")
|
||||
@ -1536,7 +1536,7 @@ func TestIssue262(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatalf("No error on second continue")
|
||||
}
|
||||
_, exited := err.(proc.ProcessExitedError)
|
||||
_, exited := err.(proc.ErrProcessExited)
|
||||
if !exited {
|
||||
t.Fatalf("Process did not exit after second continue: %v", err)
|
||||
}
|
||||
@ -1645,7 +1645,7 @@ func TestCondBreakpointError(t *testing.T) {
|
||||
|
||||
err = proc.Continue(p)
|
||||
if err != nil {
|
||||
if _, exited := err.(proc.ProcessExitedError); !exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); !exited {
|
||||
t.Fatalf("Unexpected error on second Continue(): %v", err)
|
||||
}
|
||||
} else {
|
||||
@ -1785,7 +1785,7 @@ func TestIssue332_Part2(t *testing.T) {
|
||||
assertNoError(proc.Next(p), t, "second Next()")
|
||||
assertNoError(proc.Next(p), t, "third Next()")
|
||||
err = proc.Continue(p)
|
||||
if _, exited := err.(proc.ProcessExitedError); !exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); !exited {
|
||||
assertNoError(err, t, "final Continue()")
|
||||
}
|
||||
})
|
||||
@ -1810,7 +1810,7 @@ func TestIssue414(t *testing.T) {
|
||||
for {
|
||||
err := proc.Step(p)
|
||||
if err != nil {
|
||||
if _, exited := err.(proc.ProcessExitedError); exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); exited {
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -1871,7 +1871,7 @@ func TestCmdLineArgs(t *testing.T) {
|
||||
if bp.Breakpoint != nil && bp.Name == proc.UnrecoveredPanic {
|
||||
t.Fatalf("testing args failed on unrecovered-panic breakpoint: %v", bp)
|
||||
}
|
||||
exit, exited := err.(proc.ProcessExitedError)
|
||||
exit, exited := err.(proc.ErrProcessExited)
|
||||
if !exited {
|
||||
t.Fatalf("Process did not exit: %v", err)
|
||||
} else {
|
||||
@ -1940,7 +1940,7 @@ func TestNextParked(t *testing.T) {
|
||||
var parkedg *proc.G
|
||||
for parkedg == nil {
|
||||
err := proc.Continue(p)
|
||||
if _, exited := err.(proc.ProcessExitedError); exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); exited {
|
||||
t.Log("could not find parked goroutine")
|
||||
return
|
||||
}
|
||||
@ -1992,7 +1992,7 @@ func TestStepParked(t *testing.T) {
|
||||
LookForParkedG:
|
||||
for {
|
||||
err := proc.Continue(p)
|
||||
if _, exited := err.(proc.ProcessExitedError); exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); exited {
|
||||
t.Log("could not find parked goroutine")
|
||||
return
|
||||
}
|
||||
@ -2040,8 +2040,8 @@ func TestIssue509(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatalf("expected error but none was generated")
|
||||
}
|
||||
if err != proc.NotExecutableErr {
|
||||
t.Fatalf("expected error \"%v\" got \"%v\"", proc.NotExecutableErr, err)
|
||||
if err != proc.ErrNotExecutable {
|
||||
t.Fatalf("expected error \"%v\" got \"%v\"", proc.ErrNotExecutable, err)
|
||||
}
|
||||
os.Remove(exepath)
|
||||
}
|
||||
@ -2072,7 +2072,7 @@ func TestUnsupportedArch(t *testing.T) {
|
||||
|
||||
p, err := native.Launch([]string{outfile}, ".", false)
|
||||
switch err {
|
||||
case proc.UnsupportedLinuxArchErr, proc.UnsupportedWindowsArchErr, proc.UnsupportedDarwinArchErr:
|
||||
case proc.ErrUnsupportedLinuxArch, proc.ErrUnsupportedWindowsArch, proc.ErrUnsupportedDarwinArch:
|
||||
// all good
|
||||
case nil:
|
||||
p.Detach(true)
|
||||
@ -2370,7 +2370,7 @@ func TestStepConcurrentPtr(t *testing.T) {
|
||||
count := 0
|
||||
for {
|
||||
err := proc.Continue(p)
|
||||
_, exited := err.(proc.ProcessExitedError)
|
||||
_, exited := err.(proc.ErrProcessExited)
|
||||
if exited {
|
||||
break
|
||||
}
|
||||
@ -2698,7 +2698,7 @@ func TestStacktraceWithBarriers(t *testing.T) {
|
||||
stackBarrierGoids := []int{}
|
||||
for len(stackBarrierGoids) == 0 {
|
||||
err := proc.Continue(p)
|
||||
if _, exited := err.(proc.ProcessExitedError); exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); exited {
|
||||
t.Logf("Could not run test")
|
||||
return
|
||||
}
|
||||
@ -2957,16 +2957,16 @@ func TestIssue893(t *testing.T) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if _, ok := err.(*frame.NoFDEForPCError); ok {
|
||||
if _, ok := err.(*frame.ErrNoFDEForPC); ok {
|
||||
return
|
||||
}
|
||||
if _, ok := err.(proc.ThreadBlockedError); ok {
|
||||
if _, ok := err.(proc.ErrThreadBlocked); ok {
|
||||
return
|
||||
}
|
||||
if _, ok := err.(*proc.NoSourceForPCError); ok {
|
||||
if _, ok := err.(*proc.ErrNoSourceForPC); ok {
|
||||
return
|
||||
}
|
||||
if _, ok := err.(proc.ProcessExitedError); ok {
|
||||
if _, ok := err.(proc.ErrProcessExited); ok {
|
||||
return
|
||||
}
|
||||
assertNoError(err, t, "Next")
|
||||
@ -3519,7 +3519,7 @@ func TestIssue1101(t *testing.T) {
|
||||
lastCmd = "final Continue()"
|
||||
exitErr = proc.Continue(p)
|
||||
}
|
||||
if pexit, exited := exitErr.(proc.ProcessExitedError); exited {
|
||||
if pexit, exited := exitErr.(proc.ErrProcessExited); exited {
|
||||
if pexit.Status != 2 && testBackend != "lldb" {
|
||||
// looks like there's a bug with debugserver on macOS that sometimes
|
||||
// will report exit status 0 instead of the proper exit status.
|
||||
|
@ -65,7 +65,7 @@ func TestIssue419(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, exited := err.(proc.ProcessExitedError); !exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); !exited {
|
||||
t.Fatalf("Unexpected error after Continue(): %v\n", err)
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ type Registers interface {
|
||||
Copy() Registers
|
||||
}
|
||||
|
||||
// Register represents a CPU register.
|
||||
type Register struct {
|
||||
Name string
|
||||
Bytes []byte
|
||||
@ -169,7 +170,9 @@ func AppendSSEReg(regs []Register, name string, xmm []byte) []Register {
|
||||
return append(regs, Register{name, xmm, out.String()})
|
||||
}
|
||||
|
||||
var UnknownRegisterError = errors.New("unknown register")
|
||||
// ErrUnknownRegister is returned when the value of an unknown
|
||||
// register is requested.
|
||||
var ErrUnknownRegister = errors.New("unknown register")
|
||||
|
||||
type flagRegisterDescr []flagDescr
|
||||
type flagDescr struct {
|
||||
@ -248,7 +251,7 @@ func (descr flagRegisterDescr) Describe(reg uint64, bitsize int) string {
|
||||
return fmt.Sprintf("%#0*x\t[%s]", bitsize/4, reg, strings.Join(r, " "))
|
||||
}
|
||||
|
||||
// tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
|
||||
// PtraceFpRegs tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
|
||||
type PtraceFpRegs struct {
|
||||
Cwd uint16
|
||||
Swd uint16
|
||||
|
@ -66,7 +66,7 @@ var dwarfToName = map[int]string{
|
||||
66: "SW",
|
||||
}
|
||||
|
||||
// getDwarfRegister maps between DWARF register numbers and architecture
|
||||
// GetDwarfRegister maps between DWARF register numbers and architecture
|
||||
// registers.
|
||||
// The mapping is specified in the System V ABI AMD64 Architecture Processor
|
||||
// Supplement page 57, figure 3.36
|
||||
|
@ -80,7 +80,7 @@ func TestScope(t *testing.T) {
|
||||
|
||||
for {
|
||||
if err := proc.Continue(p); err != nil {
|
||||
if _, exited := err.(proc.ProcessExitedError); exited {
|
||||
if _, exited := err.(proc.ErrProcessExited); exited {
|
||||
break
|
||||
}
|
||||
assertNoError(err, t, "Continue()")
|
||||
|
@ -489,7 +489,7 @@ func (it *stackIterator) appendInlineCalls(frames []Stackframe, frame Stackframe
|
||||
func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uint64, retaddr uint64) {
|
||||
fde, err := it.bi.frameEntries.FDEForPC(it.pc)
|
||||
var framectx *frame.FrameContext
|
||||
if _, nofde := err.(*frame.NoFDEForPCError); nofde {
|
||||
if _, nofde := err.(*frame.ErrNoFDEForPC); nofde {
|
||||
framectx = it.bi.Arch.FixFrameUnwindContext(nil, it.pc, it.bi)
|
||||
} else {
|
||||
framectx = it.bi.Arch.FixFrameUnwindContext(fde.EstablishFrame(it.pc), it.pc, it.bi)
|
||||
@ -573,10 +573,9 @@ func (it *stackIterator) executeFrameRegRule(regnum uint64, rule frame.DWRule, c
|
||||
}
|
||||
if curReg.Uint64Val <= uint64(cfa) {
|
||||
return it.readRegisterAt(regnum, curReg.Uint64Val)
|
||||
} else {
|
||||
newReg := *curReg
|
||||
return &newReg, nil
|
||||
}
|
||||
newReg := *curReg
|
||||
return &newReg, nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -668,12 +667,12 @@ func (d *Defer) load() {
|
||||
}
|
||||
}
|
||||
|
||||
// spDecreasedErr is used when (*Defer).Next detects a corrupted linked
|
||||
// errSPDecreased is used when (*Defer).Next detects a corrupted linked
|
||||
// list, specifically when after followin a link pointer the value of SP
|
||||
// decreases rather than increasing or staying the same (the defer list is a
|
||||
// FIFO list, nodes further down the list have been added by function calls
|
||||
// further down the call stack and therefore the SP should always increase).
|
||||
var spDecreasedErr = errors.New("corrupted defer list: SP decreased")
|
||||
var errSPDecreased = errors.New("corrupted defer list: SP decreased")
|
||||
|
||||
// Next returns the next defer in the linked list
|
||||
func (d *Defer) Next() *Defer {
|
||||
@ -682,7 +681,7 @@ func (d *Defer) Next() *Defer {
|
||||
}
|
||||
d.link.load()
|
||||
if d.link.SP < d.SP {
|
||||
d.link.Unreadable = spDecreasedErr
|
||||
d.link.Unreadable = errSPDecreased
|
||||
}
|
||||
return d.link
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/derekparker/delve/pkg/goversion"
|
||||
)
|
||||
|
||||
// EnableRace allows to configure whether the race detector is enabled on target process.
|
||||
var EnableRace = flag.Bool("racetarget", false, "Enables race detector on inferior process")
|
||||
|
||||
var runningWithFixtures bool
|
||||
@ -30,14 +31,17 @@ type Fixture struct {
|
||||
Source string
|
||||
}
|
||||
|
||||
// FixtureKey holds the name and builds flags used for a test fixture.
|
||||
type FixtureKey struct {
|
||||
Name string
|
||||
Flags BuildFlags
|
||||
}
|
||||
|
||||
// Fixtures is a map of fixtureKey{ Fixture.Name, buildFlags } to Fixture.
|
||||
var Fixtures map[FixtureKey]Fixture = make(map[FixtureKey]Fixture)
|
||||
var Fixtures = make(map[FixtureKey]Fixture)
|
||||
|
||||
// FindFixturesDir will search for the directory holding all test fixtures
|
||||
// beginning with the current directory and searching up 10 directories.
|
||||
func FindFixturesDir() string {
|
||||
parent := ".."
|
||||
fixturesDir := "_fixtures"
|
||||
@ -50,16 +54,23 @@ func FindFixturesDir() string {
|
||||
return fixturesDir
|
||||
}
|
||||
|
||||
// BuildFlags used to build fixture.
|
||||
type BuildFlags uint32
|
||||
|
||||
const (
|
||||
// LinkStrip enables '-ldflas="-s"'.
|
||||
LinkStrip BuildFlags = 1 << iota
|
||||
// EnableCGOOptimization will build CGO code with optimizations.
|
||||
EnableCGOOptimization
|
||||
// EnableInlining will build a binary with inline optimizations turned on.
|
||||
EnableInlining
|
||||
// EnableOptimization will build a binary with default optimizations.
|
||||
EnableOptimization
|
||||
// EnableDWZCompression will enable DWZ compression of DWARF sections.
|
||||
EnableDWZCompression
|
||||
)
|
||||
|
||||
// BuildFixture will compile the fixture 'name' using the provided build flags.
|
||||
func BuildFixture(name string, flags BuildFlags) Fixture {
|
||||
if !runningWithFixtures {
|
||||
panic("RunTestsWithFixtures not called")
|
||||
|
@ -58,11 +58,11 @@ type Location struct {
|
||||
Fn *Function
|
||||
}
|
||||
|
||||
// ThreadBlockedError is returned when the thread
|
||||
// ErrThreadBlocked is returned when the thread
|
||||
// is blocked in the scheduler.
|
||||
type ThreadBlockedError struct{}
|
||||
type ErrThreadBlocked struct{}
|
||||
|
||||
func (tbe ThreadBlockedError) Error() string {
|
||||
func (tbe ErrThreadBlocked) Error() string {
|
||||
return "thread blocked"
|
||||
}
|
||||
|
||||
@ -72,6 +72,8 @@ type CommonThread struct {
|
||||
returnValues []*Variable
|
||||
}
|
||||
|
||||
// ReturnValues reads the return values from the function executing on
|
||||
// this thread using the provided LoadConfig.
|
||||
func (t *CommonThread) ReturnValues(cfg LoadConfig) []*Variable {
|
||||
loadValues(t.returnValues, cfg)
|
||||
return t.returnValues
|
||||
@ -84,7 +86,7 @@ func topframe(g *G, thread Thread) (Stackframe, Stackframe, error) {
|
||||
|
||||
if g == nil {
|
||||
if thread.Blocked() {
|
||||
return Stackframe{}, Stackframe{}, ThreadBlockedError{}
|
||||
return Stackframe{}, Stackframe{}, ErrThreadBlocked{}
|
||||
}
|
||||
frames, err = ThreadStacktrace(thread, 1)
|
||||
} else {
|
||||
@ -103,12 +105,14 @@ func topframe(g *G, thread Thread) (Stackframe, Stackframe, error) {
|
||||
}
|
||||
}
|
||||
|
||||
type NoSourceForPCError struct {
|
||||
// ErrNoSourceForPC is returned when the given address
|
||||
// does not correspond with a source file location.
|
||||
type ErrNoSourceForPC struct {
|
||||
pc uint64
|
||||
}
|
||||
|
||||
func (err *NoSourceForPCError) Error() string {
|
||||
return fmt.Sprintf("no source for pc %#x", err.pc)
|
||||
func (err *ErrNoSourceForPC) Error() string {
|
||||
return fmt.Sprintf("no source for PC %#x", err.pc)
|
||||
}
|
||||
|
||||
// Set breakpoints at every line, and the return address. Also look for
|
||||
@ -147,7 +151,7 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
|
||||
}
|
||||
|
||||
if topframe.Current.Fn == nil {
|
||||
return &NoSourceForPCError{topframe.Current.PC}
|
||||
return &ErrNoSourceForPC{topframe.Current.PC}
|
||||
}
|
||||
|
||||
// sanity check
|
||||
@ -232,7 +236,7 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
|
||||
}
|
||||
|
||||
// Set breakpoint on the most recently deferred function (if any)
|
||||
var deferpc uint64 = 0
|
||||
var deferpc uint64
|
||||
if topframe.TopmostDefer != nil && topframe.TopmostDefer.DeferredPC != 0 {
|
||||
deferfn := dbp.BinInfo().PCToFunc(topframe.TopmostDefer.DeferredPC)
|
||||
var err error
|
||||
@ -430,7 +434,15 @@ func newGVariable(thread Thread, gaddr uintptr, deref bool) (*Variable, error) {
|
||||
name := ""
|
||||
|
||||
if deref {
|
||||
typ = &godwarf.PtrType{godwarf.CommonType{int64(thread.Arch().PtrSize()), "", reflect.Ptr, 0}, typ}
|
||||
typ = &godwarf.PtrType{
|
||||
CommonType: godwarf.CommonType{
|
||||
ByteSize: int64(thread.Arch().PtrSize()),
|
||||
Name: "",
|
||||
ReflectKind: reflect.Ptr,
|
||||
Offset: 0,
|
||||
},
|
||||
Type: typ,
|
||||
}
|
||||
} else {
|
||||
name = "runtime.curg"
|
||||
}
|
||||
|
@ -65,7 +65,15 @@ func (bi *BinaryInfo) findType(name string) (godwarf.Type, error) {
|
||||
}
|
||||
|
||||
func pointerTo(typ godwarf.Type, arch Arch) godwarf.Type {
|
||||
return &godwarf.PtrType{godwarf.CommonType{int64(arch.PtrSize()), "*" + typ.Common().Name, reflect.Ptr, 0}, typ}
|
||||
return &godwarf.PtrType{
|
||||
CommonType: godwarf.CommonType{
|
||||
ByteSize: int64(arch.PtrSize()),
|
||||
Name: "*" + typ.Common().Name,
|
||||
ReflectKind: reflect.Ptr,
|
||||
Offset: 0,
|
||||
},
|
||||
Type: typ,
|
||||
}
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (godwarf.Type, error) {
|
||||
@ -185,6 +193,12 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
|
||||
if wg != nil {
|
||||
defer wg.Done()
|
||||
}
|
||||
|
||||
var cu *compileUnit
|
||||
var pu *partialUnit
|
||||
var partialUnits = make(map[dwarf.Offset]*partialUnit)
|
||||
var lastOffset dwarf.Offset
|
||||
|
||||
bi.types = make(map[string]dwarf.Offset)
|
||||
bi.packageVars = []packageVar{}
|
||||
bi.Functions = []Function{}
|
||||
@ -193,11 +207,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
|
||||
bi.runtimeTypeToDIE = make(map[uint64]runtimeTypeDIE)
|
||||
reader := bi.DwarfReader()
|
||||
ardr := bi.DwarfReader()
|
||||
var cu *compileUnit = nil
|
||||
var pu *partialUnit = nil
|
||||
var partialUnits = make(map[dwarf.Offset]*partialUnit)
|
||||
abstractOriginNameTable := make(map[dwarf.Offset]string)
|
||||
var lastOffset dwarf.Offset
|
||||
|
||||
outer:
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
@ -649,14 +659,10 @@ func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error)
|
||||
if tflag&tflagNamed != 0 {
|
||||
typename, err = nameOfNamedRuntimeType(_type, kind, tflag)
|
||||
return typename, kind, err
|
||||
} else {
|
||||
typename, err = nameOfUnnamedRuntimeType(_type, kind, tflag)
|
||||
return typename, kind, err
|
||||
}
|
||||
|
||||
_type.bi.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename, kind}
|
||||
|
||||
return typename, kind, nil
|
||||
typename, err = nameOfUnnamedRuntimeType(_type, kind, tflag)
|
||||
return typename, kind, err
|
||||
}
|
||||
|
||||
// The layout of a runtime._type struct is as follows:
|
||||
@ -878,9 +884,8 @@ func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, err
|
||||
if len(methods.Children) == 0 {
|
||||
buf.WriteString("}")
|
||||
return buf.String(), nil
|
||||
} else {
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
buf.WriteString(" ")
|
||||
|
||||
for i, im := range methods.Children {
|
||||
var methodname, methodtype string
|
||||
@ -940,9 +945,8 @@ func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error)
|
||||
if len(fields.Children) == 0 {
|
||||
buf.WriteString("}")
|
||||
return buf.String(), nil
|
||||
} else {
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
buf.WriteString(" ")
|
||||
|
||||
for i, field := range fields.Children {
|
||||
var fieldname, fieldtypename string
|
||||
@ -1086,11 +1090,16 @@ func constructTypeForKind(kind int64, bi *BinaryInfo) (*godwarf.StructType, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uint32typ := &godwarf.UintType{godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 4, Name: "uint32"}}}
|
||||
uint16typ := &godwarf.UintType{godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 2, Name: "uint16"}}}
|
||||
uint32typ := &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 4, Name: "uint32"}}}
|
||||
uint16typ := &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 2, Name: "uint16"}}}
|
||||
|
||||
newStructType := func(name string, sz uintptr) *godwarf.StructType {
|
||||
return &godwarf.StructType{godwarf.CommonType{Name: name, ByteSize: int64(sz)}, name, "struct", nil, false}
|
||||
return &godwarf.StructType{
|
||||
CommonType: godwarf.CommonType{Name: name, ByteSize: int64(sz)},
|
||||
StructName: name,
|
||||
Kind: "struct",
|
||||
Field: nil, Incomplete: false,
|
||||
}
|
||||
}
|
||||
|
||||
appendField := func(typ *godwarf.StructType, name string, fieldtype godwarf.Type, off uintptr) {
|
||||
|
@ -34,16 +34,20 @@ const (
|
||||
maxFramePrefetchSize = 1 * 1024 * 1024 // Maximum prefetch size for a stack frame
|
||||
)
|
||||
|
||||
type FloatSpecial uint8
|
||||
type floatSpecial uint8
|
||||
|
||||
const (
|
||||
FloatIsNormal FloatSpecial = iota
|
||||
// FloatIsNormal means the value is a normal float.
|
||||
FloatIsNormal floatSpecial = iota
|
||||
// FloatIsNaN means the float is a special NaN value.
|
||||
FloatIsNaN
|
||||
// FloatIsPosInf means the float is a special positive inifitiy value.
|
||||
FloatIsPosInf
|
||||
// FloatIsNegInf means the float is a special negative infinity value.
|
||||
FloatIsNegInf
|
||||
)
|
||||
|
||||
type VariableFlags uint16
|
||||
type variableFlags uint16
|
||||
|
||||
const (
|
||||
// VariableEscaped is set for local variables that escaped to the heap
|
||||
@ -52,7 +56,7 @@ const (
|
||||
// that may outlive the stack frame are allocated on the heap instead and
|
||||
// only the address is recorded on the stack. These variables will be
|
||||
// marked with this flag.
|
||||
VariableEscaped VariableFlags = (1 << iota)
|
||||
VariableEscaped variableFlags = (1 << iota)
|
||||
// VariableShadowed is set for local variables that are shadowed by a
|
||||
// variable with the same name in another scope
|
||||
VariableShadowed
|
||||
@ -79,12 +83,12 @@ type Variable struct {
|
||||
bi *BinaryInfo
|
||||
|
||||
Value constant.Value
|
||||
FloatSpecial FloatSpecial
|
||||
FloatSpecial floatSpecial
|
||||
|
||||
Len int64
|
||||
Cap int64
|
||||
|
||||
Flags VariableFlags
|
||||
Flags variableFlags
|
||||
|
||||
// Base address of arrays, Base address of the backing array for slices (0 for nil slices)
|
||||
// Base address of the backing byte array for strings
|
||||
@ -106,6 +110,7 @@ type Variable struct {
|
||||
DeclLine int64 // line number of this variable's declaration
|
||||
}
|
||||
|
||||
// LoadConfig controls how variables are loaded from the targets memory.
|
||||
type LoadConfig struct {
|
||||
// FollowPointers requests pointers to be automatically dereferenced.
|
||||
FollowPointers bool
|
||||
@ -405,13 +410,13 @@ func (ng NoGError) Error() string {
|
||||
return fmt.Sprintf("no G executing on thread %d", ng.tid)
|
||||
}
|
||||
|
||||
func (gvar *Variable) parseG() (*G, error) {
|
||||
mem := gvar.mem
|
||||
gaddr := uint64(gvar.Addr)
|
||||
_, deref := gvar.RealType.(*godwarf.PtrType)
|
||||
func (v *Variable) parseG() (*G, error) {
|
||||
mem := v.mem
|
||||
gaddr := uint64(v.Addr)
|
||||
_, deref := v.RealType.(*godwarf.PtrType)
|
||||
|
||||
if deref {
|
||||
gaddrbytes := make([]byte, gvar.bi.Arch.PtrSize())
|
||||
gaddrbytes := make([]byte, v.bi.Arch.PtrSize())
|
||||
_, err := mem.ReadMemory(gaddrbytes, uintptr(gaddr))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error derefing *G %s", err)
|
||||
@ -426,27 +431,27 @@ func (gvar *Variable) parseG() (*G, error) {
|
||||
return nil, NoGError{tid: id}
|
||||
}
|
||||
for {
|
||||
if _, isptr := gvar.RealType.(*godwarf.PtrType); !isptr {
|
||||
if _, isptr := v.RealType.(*godwarf.PtrType); !isptr {
|
||||
break
|
||||
}
|
||||
gvar = gvar.maybeDereference()
|
||||
v = v.maybeDereference()
|
||||
}
|
||||
gvar.loadValue(LoadConfig{false, 2, 64, 0, -1})
|
||||
if gvar.Unreadable != nil {
|
||||
return nil, gvar.Unreadable
|
||||
v.loadValue(LoadConfig{false, 2, 64, 0, -1})
|
||||
if v.Unreadable != nil {
|
||||
return nil, v.Unreadable
|
||||
}
|
||||
schedVar := gvar.fieldVariable("sched")
|
||||
schedVar := v.fieldVariable("sched")
|
||||
pc, _ := constant.Int64Val(schedVar.fieldVariable("pc").Value)
|
||||
sp, _ := constant.Int64Val(schedVar.fieldVariable("sp").Value)
|
||||
var bp int64
|
||||
if bpvar := schedVar.fieldVariable("bp"); bpvar != nil && bpvar.Value != nil {
|
||||
bp, _ = constant.Int64Val(bpvar.Value)
|
||||
}
|
||||
id, _ := constant.Int64Val(gvar.fieldVariable("goid").Value)
|
||||
gopc, _ := constant.Int64Val(gvar.fieldVariable("gopc").Value)
|
||||
startpc, _ := constant.Int64Val(gvar.fieldVariable("startpc").Value)
|
||||
id, _ := constant.Int64Val(v.fieldVariable("goid").Value)
|
||||
gopc, _ := constant.Int64Val(v.fieldVariable("gopc").Value)
|
||||
startpc, _ := constant.Int64Val(v.fieldVariable("startpc").Value)
|
||||
waitReason := ""
|
||||
if wrvar := gvar.fieldVariable("waitreason"); wrvar.Value != nil {
|
||||
if wrvar := v.fieldVariable("waitreason"); wrvar.Value != nil {
|
||||
switch wrvar.Kind {
|
||||
case reflect.String:
|
||||
waitReason = constant.StringVal(wrvar.Value)
|
||||
@ -456,7 +461,7 @@ func (gvar *Variable) parseG() (*G, error) {
|
||||
|
||||
}
|
||||
var stackhi, stacklo uint64
|
||||
if stackVar := gvar.fieldVariable("stack"); stackVar != nil {
|
||||
if stackVar := v.fieldVariable("stack"); stackVar != nil {
|
||||
if stackhiVar := stackVar.fieldVariable("hi"); stackhiVar != nil {
|
||||
stackhi, _ = constant.Uint64Val(stackhiVar.Value)
|
||||
}
|
||||
@ -465,15 +470,15 @@ func (gvar *Variable) parseG() (*G, error) {
|
||||
}
|
||||
}
|
||||
|
||||
stkbarVar, _ := gvar.structMember("stkbar")
|
||||
stkbarVarPosFld := gvar.fieldVariable("stkbarPos")
|
||||
stkbarVar, _ := v.structMember("stkbar")
|
||||
stkbarVarPosFld := v.fieldVariable("stkbarPos")
|
||||
var stkbarPos int64
|
||||
if stkbarVarPosFld != nil { // stack barriers were removed in Go 1.9
|
||||
stkbarPos, _ = constant.Int64Val(stkbarVarPosFld.Value)
|
||||
}
|
||||
|
||||
status, _ := constant.Int64Val(gvar.fieldVariable("atomicstatus").Value)
|
||||
f, l, fn := gvar.bi.PCToLine(uint64(pc))
|
||||
status, _ := constant.Int64Val(v.fieldVariable("atomicstatus").Value)
|
||||
f, l, fn := v.bi.PCToLine(uint64(pc))
|
||||
g := &G{
|
||||
ID: int(id),
|
||||
GoPC: uint64(gopc),
|
||||
@ -484,7 +489,7 @@ func (gvar *Variable) parseG() (*G, error) {
|
||||
WaitReason: waitReason,
|
||||
Status: uint64(status),
|
||||
CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn},
|
||||
variable: gvar,
|
||||
variable: v,
|
||||
stkbarVar: stkbarVar,
|
||||
stkbarPos: int(stkbarPos),
|
||||
stackhi: stackhi,
|
||||
@ -563,7 +568,7 @@ func (g *G) Go() Location {
|
||||
// Backup to CALL instruction.
|
||||
// Mimics runtime/traceback.go:677.
|
||||
if g.GoPC > fn.Entry {
|
||||
pc -= 1
|
||||
pc--
|
||||
}
|
||||
}
|
||||
f, l, fn := g.variable.bi.PCToLine(pc)
|
||||
@ -583,7 +588,7 @@ func (g *G) stkbar() ([]savedLR, error) {
|
||||
}
|
||||
g.stkbarVar.loadValue(LoadConfig{false, 1, 0, int(g.stkbarVar.Len), 3})
|
||||
if g.stkbarVar.Unreadable != nil {
|
||||
return nil, fmt.Errorf("unreadable stkbar: %v\n", g.stkbarVar.Unreadable)
|
||||
return nil, fmt.Errorf("unreadable stkbar: %v", g.stkbarVar.Unreadable)
|
||||
}
|
||||
r := make([]savedLR, len(g.stkbarVar.Children))
|
||||
for i, child := range g.stkbarVar.Children {
|
||||
@ -1026,13 +1031,13 @@ func (v *Variable) loadValueInternal(recurseLevel int, cfg LoadConfig) {
|
||||
// is performed.
|
||||
// * If srcv and dstv have the same type and are both addressable then the
|
||||
// contents of srcv are copied byte-by-byte into dstv
|
||||
func (dstv *Variable) setValue(srcv *Variable, srcExpr string) error {
|
||||
func (v *Variable) setValue(srcv *Variable, srcExpr string) error {
|
||||
srcv.loadValue(loadSingleValue)
|
||||
|
||||
typerr := srcv.isType(dstv.RealType, dstv.Kind)
|
||||
typerr := srcv.isType(v.RealType, v.Kind)
|
||||
if _, isTypeConvErr := typerr.(*typeConvErr); isTypeConvErr {
|
||||
// attempt iface -> eface and ptr-shaped -> eface conversions.
|
||||
return convertToEface(srcv, dstv)
|
||||
return convertToEface(srcv, v)
|
||||
}
|
||||
if typerr != nil {
|
||||
return typerr
|
||||
@ -1043,51 +1048,51 @@ func (dstv *Variable) setValue(srcv *Variable, srcExpr string) error {
|
||||
}
|
||||
|
||||
// Numerical types
|
||||
switch dstv.Kind {
|
||||
switch v.Kind {
|
||||
case reflect.Float32, reflect.Float64:
|
||||
f, _ := constant.Float64Val(srcv.Value)
|
||||
return dstv.writeFloatRaw(f, dstv.RealType.Size())
|
||||
return v.writeFloatRaw(f, v.RealType.Size())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n, _ := constant.Int64Val(srcv.Value)
|
||||
return dstv.writeUint(uint64(n), dstv.RealType.Size())
|
||||
return v.writeUint(uint64(n), v.RealType.Size())
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
n, _ := constant.Uint64Val(srcv.Value)
|
||||
return dstv.writeUint(n, dstv.RealType.Size())
|
||||
return v.writeUint(n, v.RealType.Size())
|
||||
case reflect.Bool:
|
||||
return dstv.writeBool(constant.BoolVal(srcv.Value))
|
||||
return v.writeBool(constant.BoolVal(srcv.Value))
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
real, _ := constant.Float64Val(constant.Real(srcv.Value))
|
||||
imag, _ := constant.Float64Val(constant.Imag(srcv.Value))
|
||||
return dstv.writeComplex(real, imag, dstv.RealType.Size())
|
||||
return v.writeComplex(real, imag, v.RealType.Size())
|
||||
}
|
||||
|
||||
// nilling nillable variables
|
||||
if srcv == nilVariable {
|
||||
return dstv.writeZero()
|
||||
return v.writeZero()
|
||||
}
|
||||
|
||||
// set a string to ""
|
||||
if srcv.Kind == reflect.String && srcv.Len == 0 {
|
||||
return dstv.writeZero()
|
||||
return v.writeZero()
|
||||
}
|
||||
|
||||
// slice assignment (this is not handled by the writeCopy below so that
|
||||
// results of a reslice operation can be used here).
|
||||
if srcv.Kind == reflect.Slice {
|
||||
return dstv.writeSlice(srcv.Len, srcv.Cap, srcv.Base)
|
||||
return v.writeSlice(srcv.Len, srcv.Cap, srcv.Base)
|
||||
}
|
||||
|
||||
// allow any integer to be converted to any pointer
|
||||
if t, isptr := dstv.RealType.(*godwarf.PtrType); isptr {
|
||||
return dstv.writeUint(uint64(srcv.Children[0].Addr), int64(t.ByteSize))
|
||||
if t, isptr := v.RealType.(*godwarf.PtrType); isptr {
|
||||
return v.writeUint(uint64(srcv.Children[0].Addr), int64(t.ByteSize))
|
||||
}
|
||||
|
||||
// byte-by-byte copying for everything else, but the source must be addressable
|
||||
if srcv.Addr != 0 {
|
||||
return dstv.writeCopy(srcv)
|
||||
return v.writeCopy(srcv)
|
||||
}
|
||||
|
||||
return fmt.Errorf("can not set variables of type %s (not implemented)", dstv.Kind.String())
|
||||
return fmt.Errorf("can not set variables of type %s (not implemented)", v.Kind.String())
|
||||
}
|
||||
|
||||
// convertToEface converts srcv into an "interface {}" and writes it to
|
||||
@ -1504,13 +1509,13 @@ func (v *Variable) writeSlice(len, cap int64, base uintptr) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dstv *Variable) writeCopy(srcv *Variable) error {
|
||||
func (v *Variable) writeCopy(srcv *Variable) error {
|
||||
buf := make([]byte, srcv.RealType.Size())
|
||||
_, err := srcv.mem.ReadMemory(buf, srcv.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = dstv.mem.WriteMemory(dstv.Addr, buf)
|
||||
_, err = v.mem.WriteMemory(v.Addr, buf)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1656,16 +1661,16 @@ func (v *Variable) mapIterator() *mapIterator {
|
||||
}
|
||||
|
||||
if it.buckets.Kind != reflect.Struct || it.oldbuckets.Kind != reflect.Struct {
|
||||
v.Unreadable = mapBucketsNotStructErr
|
||||
v.Unreadable = errMapBucketsNotStruct
|
||||
return nil
|
||||
}
|
||||
|
||||
return it
|
||||
}
|
||||
|
||||
var mapBucketContentsNotArrayErr = errors.New("malformed map type: keys, values or tophash of a bucket is not an array")
|
||||
var mapBucketContentsInconsistentLenErr = errors.New("malformed map type: inconsistent array length in bucket")
|
||||
var mapBucketsNotStructErr = errors.New("malformed map type: buckets, oldbuckets or overflow field not a struct")
|
||||
var errMapBucketContentsNotArray = errors.New("malformed map type: keys, values or tophash of a bucket is not an array")
|
||||
var errMapBucketContentsInconsistentLen = errors.New("malformed map type: inconsistent array length in bucket")
|
||||
var errMapBucketsNotStruct = errors.New("malformed map type: buckets, oldbuckets or overflow field not a struct")
|
||||
|
||||
func (it *mapIterator) nextBucket() bool {
|
||||
if it.overflow != nil && it.overflow.Addr > 0 {
|
||||
@ -1755,24 +1760,24 @@ func (it *mapIterator) nextBucket() bool {
|
||||
}
|
||||
|
||||
if it.tophashes.Kind != reflect.Array || it.keys.Kind != reflect.Array || it.values.Kind != reflect.Array {
|
||||
it.v.Unreadable = mapBucketContentsNotArrayErr
|
||||
it.v.Unreadable = errMapBucketContentsNotArray
|
||||
return false
|
||||
}
|
||||
|
||||
if it.tophashes.Len != it.keys.Len {
|
||||
it.v.Unreadable = mapBucketContentsInconsistentLenErr
|
||||
it.v.Unreadable = errMapBucketContentsInconsistentLen
|
||||
return false
|
||||
}
|
||||
|
||||
if it.values.fieldType.Size() > 0 && it.tophashes.Len != it.values.Len {
|
||||
// if the type of the value is zero-sized (i.e. struct{}) then the values
|
||||
// array's length is zero.
|
||||
it.v.Unreadable = mapBucketContentsInconsistentLenErr
|
||||
it.v.Unreadable = errMapBucketContentsInconsistentLen
|
||||
return false
|
||||
}
|
||||
|
||||
if it.overflow.Kind != reflect.Struct {
|
||||
it.v.Unreadable = mapBucketsNotStructErr
|
||||
it.v.Unreadable = errMapBucketsNotStruct
|
||||
return false
|
||||
}
|
||||
|
||||
@ -2031,7 +2036,7 @@ func (v *variablesByDepth) Swap(i int, j int) {
|
||||
v.vars[i], v.vars[j] = v.vars[j], v.vars[i]
|
||||
}
|
||||
|
||||
// Fetches all variables of a specific type in the current function scope
|
||||
// Locals fetches all variables of a specific type in the current function scope.
|
||||
func (scope *EvalScope) Locals() ([]*Variable, error) {
|
||||
if scope.Fn == nil {
|
||||
return nil, errors.New("unable to find function context")
|
||||
|
@ -81,10 +81,18 @@ type Commands struct {
|
||||
}
|
||||
|
||||
var (
|
||||
LongLoadConfig = api.LoadConfig{true, 1, 64, 64, -1}
|
||||
// LongLoadConfig loads more information:
|
||||
// * Follows pointers
|
||||
// * Loads more array values
|
||||
// * Does not limit struct fields
|
||||
LongLoadConfig = api.LoadConfig{true, 1, 64, 64, -1}
|
||||
// ShortLoadConfig loads less information, not following pointers
|
||||
// and limiting struct fields loaded to 3.
|
||||
ShortLoadConfig = api.LoadConfig{false, 0, 64, 0, 3}
|
||||
)
|
||||
|
||||
// ByFirstAlias will sort by the first
|
||||
// alias of a command.
|
||||
type ByFirstAlias []command
|
||||
|
||||
func (a ByFirstAlias) Len() int { return len(a) }
|
||||
@ -405,6 +413,7 @@ func (c *Commands) Find(cmdstr string, prefix cmdPrefix) cmdfunc {
|
||||
return noCmdAvailable
|
||||
}
|
||||
|
||||
// CallWithContext takes a command and a context that command should be executed in.
|
||||
func (c *Commands) CallWithContext(cmdstr string, t *Term, ctx callContext) error {
|
||||
vals := strings.SplitN(strings.TrimSpace(cmdstr), " ", 2)
|
||||
cmdname := vals[0]
|
||||
@ -415,6 +424,7 @@ func (c *Commands) CallWithContext(cmdstr string, t *Term, ctx callContext) erro
|
||||
return c.Find(cmdname, ctx.Prefix)(t, ctx, args)
|
||||
}
|
||||
|
||||
// Call takes a command to execute.
|
||||
func (c *Commands) Call(cmdstr string, t *Term) error {
|
||||
ctx := callContext{Prefix: noPrefix, Scope: api.EvalScope{GoroutineID: -1, Frame: c.frame}}
|
||||
return c.CallWithContext(cmdstr, t, ctx)
|
||||
|
@ -47,7 +47,7 @@ type Term struct {
|
||||
func New(client service.Client, conf *config.Config) *Term {
|
||||
if client != nil && client.IsMulticlient() {
|
||||
state, _ := client.GetStateNonBlocking()
|
||||
// The error return of GetState will usually be the ProcessExitedError,
|
||||
// The error return of GetState will usually be the ErrProcessExited,
|
||||
// which we don't care about. If there are other errors they will show up
|
||||
// later, here we are only concerned about stopping a running target so
|
||||
// that we can initialize our connection.
|
||||
|
@ -250,6 +250,7 @@ func ConvertLocation(loc proc.Location) Location {
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertAsmInstruction converts from proc.AsmInstruction to api.AsmInstruction.
|
||||
func ConvertAsmInstruction(inst proc.AsmInstruction, text string) AsmInstruction {
|
||||
var destloc *Location
|
||||
if inst.DestLoc != nil {
|
||||
@ -266,6 +267,7 @@ func ConvertAsmInstruction(inst proc.AsmInstruction, text string) AsmInstruction
|
||||
}
|
||||
}
|
||||
|
||||
// LoadConfigToProc converts an api.LoadConfig to proc.LoadConfig.
|
||||
func LoadConfigToProc(cfg *LoadConfig) *proc.LoadConfig {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
@ -279,6 +281,7 @@ func LoadConfigToProc(cfg *LoadConfig) *proc.LoadConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// LoadConfigFromProc converts a proc.LoadConfig to api.LoadConfig.
|
||||
func LoadConfigFromProc(cfg *proc.LoadConfig) *LoadConfig {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
@ -292,6 +295,7 @@ func LoadConfigFromProc(cfg *proc.LoadConfig) *LoadConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertRegisters converts proc.Register to api.Register for a slice.
|
||||
func ConvertRegisters(in []proc.Register) (out []Register) {
|
||||
out = make([]Register, len(in))
|
||||
for i := range in {
|
||||
@ -300,6 +304,7 @@ func ConvertRegisters(in []proc.Register) (out []Register) {
|
||||
return
|
||||
}
|
||||
|
||||
// ConvertCheckpoint converts proc.Chekcpoint to api.Checkpoint.
|
||||
func ConvertCheckpoint(in proc.Checkpoint) (out Checkpoint) {
|
||||
return Checkpoint(in)
|
||||
}
|
||||
|
@ -11,7 +11,9 @@ import (
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
var NotExecutableErr = proc.NotExecutableErr
|
||||
// ErrNotExecutable is an error returned when trying
|
||||
// to debug a non-executable file.
|
||||
var ErrNotExecutable = proc.ErrNotExecutable
|
||||
|
||||
// DebuggerState represents the current context of the debugger.
|
||||
type DebuggerState struct {
|
||||
@ -75,6 +77,10 @@ type Breakpoint struct {
|
||||
TotalHitCount uint64 `json:"totalHitCount"`
|
||||
}
|
||||
|
||||
// ValidBreakpointName returns an error if
|
||||
// the name to be chosen for a breakpoint is invalid.
|
||||
// The name can not be just a number, and must contain a series
|
||||
// of letters or numbers.
|
||||
func ValidBreakpointName(name string) error {
|
||||
if _, err := strconv.Atoi(name); err == nil {
|
||||
return errors.New("breakpoint name can not be a number")
|
||||
@ -114,6 +120,7 @@ type Thread struct {
|
||||
ReturnValues []Variable
|
||||
}
|
||||
|
||||
// Location holds program location information.
|
||||
type Location struct {
|
||||
PC uint64 `json:"pc"`
|
||||
File string `json:"file"`
|
||||
@ -121,6 +128,7 @@ type Location struct {
|
||||
Function *Function `json:"function,omitempty"`
|
||||
}
|
||||
|
||||
// Stackframe describes one frame in a stack trace.
|
||||
type Stackframe struct {
|
||||
Location
|
||||
Locals []Variable
|
||||
@ -136,6 +144,7 @@ type Stackframe struct {
|
||||
Err string
|
||||
}
|
||||
|
||||
// Defer describes a deferred function.
|
||||
type Defer struct {
|
||||
DeferredLoc Location // deferred function
|
||||
DeferLoc Location // location of the defer statement
|
||||
@ -143,6 +152,8 @@ type Defer struct {
|
||||
Unreadable string
|
||||
}
|
||||
|
||||
// Var will return the variable described by 'name' within
|
||||
// this stack frame.
|
||||
func (frame *Stackframe) Var(name string) *Variable {
|
||||
for i := range frame.Locals {
|
||||
if frame.Locals[i].Name == name {
|
||||
@ -168,6 +179,7 @@ type Function struct {
|
||||
Optimized bool `json:"optimized"`
|
||||
}
|
||||
|
||||
// Name will return the function name.
|
||||
func (fn *Function) Name() string {
|
||||
if fn == nil {
|
||||
return "???"
|
||||
@ -297,7 +309,7 @@ type DebuggerCommand struct {
|
||||
Expr string `json:"expr,omitempty"`
|
||||
}
|
||||
|
||||
// Informations about the current breakpoint
|
||||
// BreakpointInfo contains informations about the current breakpoint
|
||||
type BreakpointInfo struct {
|
||||
Stacktrace []Stackframe `json:"stacktrace,omitempty"`
|
||||
Goroutine *Goroutine `json:"goroutine,omitempty"`
|
||||
@ -306,6 +318,8 @@ type BreakpointInfo struct {
|
||||
Locals []Variable `json:"locals,omitempty"`
|
||||
}
|
||||
|
||||
// EvalScope is the scope a command should
|
||||
// be evaluated in. Describes the goroutine and frame number.
|
||||
type EvalScope struct {
|
||||
GoroutineID int
|
||||
Frame int
|
||||
@ -320,7 +334,7 @@ const (
|
||||
Step = "step"
|
||||
// StepOut continues to the return address of the current function
|
||||
StepOut = "stepOut"
|
||||
// SingleStep continues for exactly 1 cpu instruction.
|
||||
// StepInstruction continues for exactly 1 cpu instruction.
|
||||
StepInstruction = "stepInstruction"
|
||||
// Next continues to the next source line, not entering function calls.
|
||||
Next = "next"
|
||||
@ -334,10 +348,14 @@ const (
|
||||
Call = "call"
|
||||
)
|
||||
|
||||
// AssemblyFlavour describes the output
|
||||
// of disassembled code.
|
||||
type AssemblyFlavour int
|
||||
|
||||
const (
|
||||
GNUFlavour = AssemblyFlavour(proc.GNUFlavour)
|
||||
// GNUFlavour will disassemble using GNU assembly syntax.
|
||||
GNUFlavour = AssemblyFlavour(proc.GNUFlavour)
|
||||
// IntelFlavour will disassemble using Intel assembly syntax.
|
||||
IntelFlavour = AssemblyFlavour(proc.IntelFlavour)
|
||||
)
|
||||
|
||||
@ -357,28 +375,35 @@ type AsmInstruction struct {
|
||||
AtPC bool
|
||||
}
|
||||
|
||||
// AsmInstructions is a slice of single instructions.
|
||||
type AsmInstructions []AsmInstruction
|
||||
|
||||
// GetVersionIn is the argument for GetVersion.
|
||||
type GetVersionIn struct {
|
||||
}
|
||||
|
||||
// GetVersionOut is the result of GetVersion.
|
||||
type GetVersionOut struct {
|
||||
DelveVersion string
|
||||
APIVersion int
|
||||
}
|
||||
|
||||
// SetAPIVersionIn is the input for SetAPIVersion.
|
||||
type SetAPIVersionIn struct {
|
||||
APIVersion int
|
||||
}
|
||||
|
||||
// SetAPIVersionOut is the output for SetAPIVersion.
|
||||
type SetAPIVersionOut struct {
|
||||
}
|
||||
|
||||
// Register holds information on a CPU register.
|
||||
type Register struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
// Registers is a list of CPU registers.
|
||||
type Registers []Register
|
||||
|
||||
func (regs Registers) String() string {
|
||||
@ -396,11 +421,15 @@ func (regs Registers) String() string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// DiscardedBreakpoint is a breakpoint that is not
|
||||
// reinstated during a restart.
|
||||
type DiscardedBreakpoint struct {
|
||||
Breakpoint *Breakpoint
|
||||
Reason string
|
||||
}
|
||||
|
||||
// Checkpoint is a point in the program that
|
||||
// can be returned to in certain execution modes.
|
||||
type Checkpoint struct {
|
||||
ID int
|
||||
When string
|
||||
|
@ -117,7 +117,7 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
||||
d.log.Infof("launching process with args: %v", d.processArgs)
|
||||
p, err := d.Launch(d.processArgs, d.config.WorkingDir)
|
||||
if err != nil {
|
||||
if err != proc.NotExecutableErr && err != proc.UnsupportedLinuxArchErr && err != proc.UnsupportedWindowsArchErr && err != proc.UnsupportedDarwinArchErr {
|
||||
if err != proc.ErrNotExecutable && err != proc.ErrUnsupportedLinuxArch && err != proc.ErrUnsupportedWindowsArch && err != proc.ErrUnsupportedDarwinArch {
|
||||
err = go11DecodeErrorCheck(err)
|
||||
err = fmt.Errorf("could not launch process: %s", err)
|
||||
}
|
||||
@ -128,6 +128,7 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// Launch will start a process with the given args and working directory.
|
||||
func (d *Debugger) Launch(processArgs []string, wd string) (proc.Process, error) {
|
||||
switch d.config.Backend {
|
||||
case "native":
|
||||
@ -152,6 +153,7 @@ func (d *Debugger) Launch(processArgs []string, wd string) (proc.Process, error)
|
||||
// the target's executable.
|
||||
var ErrNoAttachPath = errors.New("must specify executable path on macOS")
|
||||
|
||||
// Attach will attach to the process specified by 'pid'.
|
||||
func (d *Debugger) Attach(pid int, path string) (proc.Process, error) {
|
||||
switch d.config.Backend {
|
||||
case "native":
|
||||
@ -168,7 +170,7 @@ func (d *Debugger) Attach(pid int, path string) (proc.Process, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var macOSBackendUnavailableErr = errors.New("debugserver or lldb-server not found: install XCode's command line tools or lldb-server")
|
||||
var errMacOSBackendUnavailable = errors.New("debugserver or lldb-server not found: install XCode's command line tools or lldb-server")
|
||||
|
||||
func betterGdbserialLaunchError(p proc.Process, err error) (proc.Process, error) {
|
||||
if runtime.GOOS != "darwin" {
|
||||
@ -178,7 +180,7 @@ func betterGdbserialLaunchError(p proc.Process, err error) (proc.Process, error)
|
||||
return p, err
|
||||
}
|
||||
|
||||
return p, macOSBackendUnavailableErr
|
||||
return p, errMacOSBackendUnavailable
|
||||
}
|
||||
|
||||
// ProcessPid returns the PID of the process
|
||||
@ -224,7 +226,7 @@ func (d *Debugger) Restart(pos string, resetArgs bool, newArgs []string) ([]api.
|
||||
}
|
||||
|
||||
if pos != "" {
|
||||
return nil, proc.NotRecordedErr
|
||||
return nil, proc.ErrNotRecorded
|
||||
}
|
||||
|
||||
if valid, _ := d.target.Valid(); valid {
|
||||
@ -252,7 +254,7 @@ func (d *Debugger) Restart(pos string, resetArgs bool, newArgs []string) ([]api.
|
||||
var err error
|
||||
oldBp.Addr, err = proc.FindFileLocation(p, oldBp.File, oldBp.Line)
|
||||
if err != nil {
|
||||
discarded = append(discarded, api.DiscardedBreakpoint{oldBp, err.Error()})
|
||||
discarded = append(discarded, api.DiscardedBreakpoint{Breakpoint: oldBp, Reason: err.Error()})
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -295,7 +297,7 @@ func (d *Debugger) state(retLoadCfg *proc.LoadConfig) (*api.DebuggerState, error
|
||||
|
||||
exited := false
|
||||
if _, err := d.target.Valid(); err != nil {
|
||||
_, exited = err.(*proc.ProcessExitedError)
|
||||
_, exited = err.(*proc.ErrProcessExited)
|
||||
}
|
||||
|
||||
state = &api.DebuggerState{
|
||||
@ -388,6 +390,7 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
|
||||
return createdBp, nil
|
||||
}
|
||||
|
||||
// AmendBreakpoint will update the breakpoint with the matching ID.
|
||||
func (d *Debugger) AmendBreakpoint(amend *api.Breakpoint) error {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
@ -402,6 +405,8 @@ func (d *Debugger) AmendBreakpoint(amend *api.Breakpoint) error {
|
||||
return copyBreakpointInfo(original, amend)
|
||||
}
|
||||
|
||||
// CancelNext will clear internal breakpoints, thus cancelling the 'next',
|
||||
// 'step' or 'stepout' operation.
|
||||
func (d *Debugger) CancelNext() error {
|
||||
return d.target.ClearInternalBreakpoints()
|
||||
}
|
||||
@ -596,7 +601,7 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if exitedErr, exited := err.(proc.ProcessExitedError); command.Name != api.SwitchGoroutine && command.Name != api.SwitchThread && exited {
|
||||
if exitedErr, exited := err.(proc.ErrProcessExited); command.Name != api.SwitchGoroutine && command.Name != api.SwitchThread && exited {
|
||||
state := &api.DebuggerState{}
|
||||
state.Exited = true
|
||||
state.ExitStatus = exitedErr.Status
|
||||
@ -667,7 +672,7 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
||||
bpi.Variables = make([]api.Variable, len(bp.Variables))
|
||||
}
|
||||
for i := range bp.Variables {
|
||||
v, err := s.EvalVariable(bp.Variables[i], proc.LoadConfig{true, 1, 64, 64, -1})
|
||||
v, err := s.EvalVariable(bp.Variables[i], proc.LoadConfig{FollowPointers: true, MaxVariableRecurse: 1, MaxStringLen: 64, MaxArrayValues: 64, MaxStructFields: -1})
|
||||
if err != nil {
|
||||
bpi.Variables[i] = api.Variable{Name: bp.Variables[i], Unreadable: fmt.Sprintf("eval error: %v", err)}
|
||||
} else {
|
||||
@ -716,6 +721,7 @@ func (d *Debugger) Functions(filter string) ([]string, error) {
|
||||
return regexFilterFuncs(filter, d.target.BinInfo().Functions)
|
||||
}
|
||||
|
||||
// Types returns all type information in the binary.
|
||||
func (d *Debugger) Types(filter string) ([]string, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
@ -1059,12 +1065,14 @@ func (d *Debugger) Recorded() (recorded bool, tracedir string) {
|
||||
return d.target.Recorded()
|
||||
}
|
||||
|
||||
// Checkpoint will set a checkpoint specified by the locspec.
|
||||
func (d *Debugger) Checkpoint(where string) (int, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
return d.target.Checkpoint(where)
|
||||
}
|
||||
|
||||
// Checkpoints will return a list of checkpoints.
|
||||
func (d *Debugger) Checkpoints() ([]api.Checkpoint, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
@ -1079,6 +1087,7 @@ func (d *Debugger) Checkpoints() ([]api.Checkpoint, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// ClearCheckpoint will clear the checkpoint of the given ID.
|
||||
func (d *Debugger) ClearCheckpoint(id int) error {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
Loading…
Reference in New Issue
Block a user