diff --git a/proctl/breakpoints_linux_amd64.go b/proctl/breakpoints_linux_amd64.go index 6f99abac..2b972aa3 100644 --- a/proctl/breakpoints_linux_amd64.go +++ b/proctl/breakpoints_linux_amd64.go @@ -36,6 +36,8 @@ type BreakPoint struct { temp bool } +// Returned when trying to set a breakpoint at +// an address that already has a breakpoint set for it. type BreakPointExistsError struct { file string line int @@ -46,6 +48,16 @@ func (bpe BreakPointExistsError) Error() string { return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr) } +// InvalidAddressError represents the result of +// attempting to set a breakpoint at an invalid address. +type InvalidAddressError struct { + address uint64 +} + +func (iae InvalidAddressError) Error() string { + return fmt.Sprintf("Invalid address %#v\n", iae.address) +} + func PtracePokeUser(tid int, off, addr uintptr) error { _, _, err := sys.Syscall6(sys.SYS_PTRACE, sys.PTRACE_POKEUSR, uintptr(tid), uintptr(off), uintptr(addr), 0, 0) if err != syscall.Errno(0) { @@ -63,6 +75,7 @@ func PtracePeekUser(tid int, off uintptr) (uintptr, error) { return val, nil } +// Returns whether or not a breakpoint has been set for the given address. func (dbp *DebuggedProcess) BreakpointExists(addr uint64) bool { for _, bp := range dbp.HWBreakPoints { if bp != nil && bp.Addr == addr { diff --git a/proctl/proctl.go b/proctl/proctl.go index f71c103e..b8f32c43 100644 --- a/proctl/proctl.go +++ b/proctl/proctl.go @@ -37,12 +37,15 @@ type DebuggedProcess struct { halt bool } +// A ManualStopError happens when the user triggers a +// manual stop via SIGERM. type ManualStopError struct{} func (mse ManualStopError) Error() string { return "Manual stop requested" } +// Attach to an existing process with the given PID. func Attach(pid int) (*DebuggedProcess, error) { dbp, err := newDebugProcess(pid, true) if err != nil { @@ -65,6 +68,9 @@ func Attach(pid int) (*DebuggedProcess, error) { return dbp, nil } +// Create and begin debugging a new process. First entry in +// `cmd` is the program to run, and then rest are the arguments +// to be supplied to that process. func Launch(cmd []string) (*DebuggedProcess, error) { proc := exec.Command(cmd[0]) proc.Args = cmd @@ -120,6 +126,8 @@ func newDebugProcess(pid int, attach bool) (*DebuggedProcess, error) { return &dbp, nil } +// Attach to a newly created thread, and store that thread in our list of +// known threads. func (dbp *DebuggedProcess) AttachThread(tid int) (*ThreadContext, error) { if thread, ok := dbp.Threads[tid]; ok { return thread, nil @@ -146,6 +154,8 @@ func (dbp *DebuggedProcess) AttachThread(tid int) (*ThreadContext, error) { return dbp.addThread(tid) } +// Returns whether or not Delve thinks the debugged +// process is currently executing. func (dbp *DebuggedProcess) Running() bool { return dbp.running } @@ -204,6 +214,8 @@ func (dbp *DebuggedProcess) FindLocation(str string) (uint64, error) { } } +// Sends out a request that the debugged process halt +// execution. Sends SIGSTOP to all threads. func (dbp *DebuggedProcess) RequestManualStop() { dbp.halt = true for _, th := range dbp.Threads { @@ -215,7 +227,8 @@ func (dbp *DebuggedProcess) RequestManualStop() { dbp.running = false } -// Sets a breakpoint in the current thread. +// Sets a breakpoint, adding it to our list of known breakpoints. Uses +// the "current thread" when setting the breakpoint. func (dbp *DebuggedProcess) Break(addr uint64) (*BreakPoint, error) { return dbp.CurrentThread.Break(addr) } @@ -357,14 +370,6 @@ func (dbp *DebuggedProcess) Registers() (Registers, error) { return dbp.CurrentThread.Registers() } -type InvalidAddressError struct { - address uint64 -} - -func (iae InvalidAddressError) Error() string { - return fmt.Sprintf("Invalid address %#v\n", iae.address) -} - func (dbp *DebuggedProcess) CurrentPC() (uint64, error) { return dbp.CurrentThread.CurrentPC() } diff --git a/proctl/threads.go b/proctl/threads.go index 049736de..c8b2a172 100644 --- a/proctl/threads.go +++ b/proctl/threads.go @@ -3,19 +3,27 @@ package proctl import ( "encoding/binary" "fmt" + sys "golang.org/x/sys/unix" "github.com/derekparker/delve/dwarf/frame" ) -// ThreadContext represents a single thread of execution in the -// traced program. +// ThreadContext represents a single thread in the traced process +// Id represents the thread id, Process holds a reference to the +// DebuggedProcess struct that contains info on the process as +// a whole, and Status represents the last result of a `wait` call +// on this thread. type ThreadContext struct { Id int Process *DebuggedProcess Status *sys.WaitStatus } +// An interface for a generic register type. The +// interface encapsulates the generic values / actions +// we need independant of arch. The concrete register types +// will be different depending on OS/Arch. type Registers interface { PC() uint64 SP() uint64 @@ -32,7 +40,7 @@ func (thread *ThreadContext) Registers() (Registers, error) { return regs, nil } -// Returns the current PC for this thread id. +// Returns the current PC for this thread. func (thread *ThreadContext) CurrentPC() (uint64, error) { regs, err := thread.Registers() if err != nil { @@ -76,6 +84,10 @@ func (thread *ThreadContext) Clear(addr uint64) (*BreakPoint, error) { return thread.Process.clearBreakpoint(thread.Id, addr) } +// Continue the execution of this thread. This method takes +// software breakpoints into consideration and ensures that +// we step over any breakpoints. It will restore the instruction, +// step, and then restore the breakpoint and continue. func (thread *ThreadContext) Continue() error { // Check whether we are stopped at a breakpoint, and // if so, single step over it before continuing.