diff --git a/proctl/proctl_linux_amd64.go b/proctl/proctl_linux_amd64.go index 72b914e5..be4fe48a 100644 --- a/proctl/proctl_linux_amd64.go +++ b/proctl/proctl_linux_amd64.go @@ -20,6 +20,13 @@ type DebuggedProcess struct { Executable *elf.File Symbols []elf.Symbol GoSymTable *gosym.Table + BreakPoints map[string]*BreakPoint +} + +type BreakPoint struct { + FunctionName string + Line int + Addr uint64 } // Returns a new DebuggedProcess struct with sensible defaults. @@ -44,6 +51,7 @@ func NewDebugProcess(pid int) (*DebuggedProcess, error) { Regs: &syscall.PtraceRegs{}, Process: proc, ProcessState: ps, + BreakPoints: make(map[string]*BreakPoint), } err = debuggedProc.LoadInformation() @@ -79,19 +87,32 @@ func (dbp *DebuggedProcess) Registers() (*syscall.PtraceRegs, error) { } // Sets a breakpoint in the running process. -func (dbp *DebuggedProcess) Break(fname string) error { +func (dbp *DebuggedProcess) Break(fname string) (*BreakPoint, error) { var ( - breakpoint = []byte{'0', 'x', 'C', 'C'} - fn = dbp.GoSymTable.LookupFunc(fname) - addr = uintptr(fn.LineTable.PC) + int3 = []byte{'0', 'x', 'C', 'C'} + fn = dbp.GoSymTable.LookupFunc(fname) + addr = uintptr(fn.LineTable.PC) ) - _, err := syscall.PtracePokeData(dbp.Pid, addr, breakpoint) - if err != nil { - return err + _, ok := dbp.BreakPoints[fname] + if ok { + return nil, fmt.Errorf("Breakpoint already set") } - return nil + _, err := syscall.PtracePokeData(dbp.Pid, addr, int3) + if err != nil { + return nil, err + } + + breakpoint := &BreakPoint{ + FunctionName: fn.Name, + Line: fn.LineTable.Line, + Addr: fn.LineTable.PC, + } + + dbp.BreakPoints[fname] = breakpoint + + return breakpoint, nil } // Steps through process. diff --git a/proctl/proctl_test.go b/proctl/proctl_test.go index c84c8d06..0b944d04 100644 --- a/proctl/proctl_test.go +++ b/proctl/proctl_test.go @@ -110,7 +110,7 @@ func TestBreakPoint(t *testing.T) { t.Fatal("NewDebugProcess():", err) } - err = p.Break("main.sleepytime") + _, err = p.Break("main.sleepytime") if err != nil { t.Fatal("Break():", err) } @@ -135,3 +135,26 @@ func TestBreakPoint(t *testing.T) { cmd.Process.Kill() } + +func TestBreakPointIsSetOnlyOnce(t *testing.T) { + cmd, err := StartTestProcess("testprog") + if err != nil { + t.Fatal("Starting test process:", err) + } + + pid := cmd.Process.Pid + p, err := NewDebugProcess(pid) + if err != nil { + t.Fatal("NewDebugProcess():", err) + } + + _, err = p.Break("main.sleepytime") + if err != nil { + t.Fatal("Break():", err) + } + + _, err = p.Break("main.sleepytime") + if err == nil { + t.Fatal("Should not be able to add breakpoint twice") + } +}