*: 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:
Derek Parker 2018-08-31 11:08:18 -07:00 committed by Alessandro Arzilli
parent 4fea15832f
commit c3f50742b9
44 changed files with 691 additions and 337 deletions

@ -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()