From c625f09a17f36b6035baca97c2d956114d315985 Mon Sep 17 00:00:00 2001 From: Derek Parker Date: Sat, 25 Oct 2014 09:17:05 -0500 Subject: [PATCH] Promote breakpoints back up to process --- proctl/proctl_linux_amd64.go | 108 +++++++++++++++++++++++++++------- proctl/proctl_test.go | 4 +- proctl/threads_linux_amd64.go | 100 ++++--------------------------- 3 files changed, 99 insertions(+), 113 deletions(-) diff --git a/proctl/proctl_linux_amd64.go b/proctl/proctl_linux_amd64.go index 6e70e72e..f8abf419 100644 --- a/proctl/proctl_linux_amd64.go +++ b/proctl/proctl_linux_amd64.go @@ -3,6 +3,7 @@ package proctl import ( + "bytes" "debug/gosym" "encoding/binary" "fmt" @@ -24,10 +25,32 @@ type DebuggedProcess struct { Symbols []elf.Symbol GoSymTable *gosym.Table FrameEntries *frame.FrameDescriptionEntries + BreakPoints map[uint64]*BreakPoint Threads map[int]*ThreadContext CurrentThread *ThreadContext } +// Represents a single breakpoint. Stores information on the break +// point including the byte of data that originally was stored at that +// address. +type BreakPoint struct { + FunctionName string + File string + Line int + Addr uint64 + OriginalData []byte +} + +type BreakPointExistsError struct { + file string + line int + addr uintptr +} + +func (bpe BreakPointExistsError) Error() string { + return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr) +} + func AttachBinary(name string) (*DebuggedProcess, error) { proc := exec.Command(name) proc.Stdout = os.Stdout @@ -48,8 +71,9 @@ func AttachBinary(name string) (*DebuggedProcess, error) { // Returns a new DebuggedProcess struct with sensible defaults. func NewDebugProcess(pid int) (*DebuggedProcess, error) { debuggedProc := DebuggedProcess{ - Pid: pid, - Threads: make(map[int]*ThreadContext), + Pid: pid, + Threads: make(map[int]*ThreadContext), + BreakPoints: make(map[uint64]*BreakPoint), } _, err := debuggedProc.AttachThread(pid) @@ -149,10 +173,9 @@ func (dbp *DebuggedProcess) addThread(tid int) (*ThreadContext, error) { } tctxt := &ThreadContext{ - Id: tid, - Process: dbp, - Regs: new(syscall.PtraceRegs), - BreakPoints: make(map[uint64]*BreakPoint), + Id: tid, + Process: dbp, + Regs: new(syscall.PtraceRegs), } if tid == dbp.Pid { @@ -164,16 +187,67 @@ func (dbp *DebuggedProcess) addThread(tid int) (*ThreadContext, error) { return tctxt, nil } +// Sets a breakpoint in the running process. +func (dbp *DebuggedProcess) Break(addr uintptr) (*BreakPoint, error) { + var ( + int3 = []byte{0xCC} + f, l, fn = dbp.GoSymTable.PCToLine(uint64(addr)) + originalData = make([]byte, 1) + ) + + if fn == nil { + return nil, InvalidAddressError{address: addr} + } + + _, err := syscall.PtracePeekData(dbp.CurrentThread.Id, addr, originalData) + if err != nil { + return nil, err + } + + if bytes.Equal(originalData, int3) { + return nil, BreakPointExistsError{f, l, addr} + } + + _, err = syscall.PtracePokeData(dbp.CurrentThread.Id, addr, int3) + if err != nil { + return nil, err + } + + breakpoint := &BreakPoint{ + FunctionName: fn.Name, + File: f, + Line: l, + Addr: uint64(addr), + OriginalData: originalData, + } + + dbp.BreakPoints[uint64(addr)] = breakpoint + + return breakpoint, nil +} + +// Clears a breakpoint. +func (dbp *DebuggedProcess) Clear(pc uint64) (*BreakPoint, error) { + bp, ok := dbp.BreakPoints[pc] + if !ok { + return nil, fmt.Errorf("No breakpoint currently set for %#v", pc) + } + + _, err := syscall.PtracePokeData(dbp.CurrentThread.Id, uintptr(bp.Addr), bp.OriginalData) + if err != nil { + return nil, err + } + + delete(dbp.BreakPoints, pc) + + return bp, nil +} + // Returns the status of the current main thread context. func (dbp *DebuggedProcess) Status() *syscall.WaitStatus { return dbp.CurrentThread.Status } -// Returns the breakpoints of the current main thread context. -func (dbp *DebuggedProcess) BreakPoints() map[uint64]*BreakPoint { - return dbp.CurrentThread.BreakPoints -} - // Finds the executable from /proc//exe and then // uses that to parse the following information: // * Dwarf .debug_frame section @@ -257,16 +331,6 @@ func (iae InvalidAddressError) Error() string { return fmt.Sprintf("Invalid address %#v\n", iae.address) } -// Sets a breakpoint in the running process. -func (dbp *DebuggedProcess) Break(addr uintptr) (*BreakPoint, error) { - return dbp.CurrentThread.Break(addr) -} - -// Clears a breakpoint. -func (dbp *DebuggedProcess) Clear(pc uint64) (*BreakPoint, error) { - return dbp.CurrentThread.Clear(pc) -} - func (dbp *DebuggedProcess) CurrentPC() (uint64, error) { return dbp.CurrentThread.CurrentPC() } @@ -429,7 +493,7 @@ func wait(dbp *DebuggedProcess, pid int, options int) (int, *syscall.WaitStatus, for _, th := range dbp.Threads { if th.Id == pid { pc, _ := th.CurrentPC() - _, ok := th.BreakPoints[pc-1] + _, ok := dbp.BreakPoints[pc-1] if ok { return pid, &status, nil } else { diff --git a/proctl/proctl_test.go b/proctl/proctl_test.go index f121490c..1d4558f7 100644 --- a/proctl/proctl_test.go +++ b/proctl/proctl_test.go @@ -169,7 +169,7 @@ func TestClearBreakPoint(t *testing.T) { t.Fatalf("Breakpoint was not cleared data: %#v, int3: %#v", data, int3) } - if len(p.BreakPoints()) != 0 { + if len(p.BreakPoints) != 0 { t.Fatal("Breakpoint not removed internally") } }) @@ -229,7 +229,7 @@ func TestNext(t *testing.T) { } } - if len(p.BreakPoints()) != 1 { + if len(p.BreakPoints) != 1 { t.Fatal("Not all breakpoints were cleaned up") } }) diff --git a/proctl/threads_linux_amd64.go b/proctl/threads_linux_amd64.go index 4cc71490..fbb872c7 100644 --- a/proctl/threads_linux_amd64.go +++ b/proctl/threads_linux_amd64.go @@ -18,22 +18,10 @@ import ( // ThreadContext represents a single thread of execution in the // traced program. type ThreadContext struct { - Id int - Process *DebuggedProcess - Status *syscall.WaitStatus - Regs *syscall.PtraceRegs - BreakPoints map[uint64]*BreakPoint -} - -// Represents a single breakpoint. Stores information on the break -// point including the byte of data that originally was stored at that -// address. -type BreakPoint struct { - FunctionName string - File string - Line int - Addr uint64 - OriginalData []byte + Id int + Process *DebuggedProcess + Status *syscall.WaitStatus + Regs *syscall.PtraceRegs } type Variable struct { @@ -42,16 +30,6 @@ type Variable struct { Type string } -type BreakPointExistsError struct { - file string - line int - addr uintptr -} - -func (bpe BreakPointExistsError) Error() string { - return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr) -} - // Obtains register values from the debugged process. func (thread *ThreadContext) Registers() (*syscall.PtraceRegs, error) { err := syscall.PtraceGetRegs(thread.Id, thread.Regs) @@ -80,62 +58,6 @@ func (thread *ThreadContext) CurrentPC() (uint64, error) { return regs.PC(), nil } -// Sets a breakpoint in the running process. -func (thread *ThreadContext) Break(addr uintptr) (*BreakPoint, error) { - var ( - int3 = []byte{0xCC} - f, l, fn = thread.Process.GoSymTable.PCToLine(uint64(addr)) - originalData = make([]byte, 1) - ) - - if fn == nil { - return nil, InvalidAddressError{address: addr} - } - - _, err := syscall.PtracePeekData(thread.Id, addr, originalData) - if err != nil { - return nil, err - } - - if bytes.Equal(originalData, int3) { - return nil, BreakPointExistsError{f, l, addr} - } - - _, err = syscall.PtracePokeData(thread.Id, addr, int3) - if err != nil { - return nil, err - } - - breakpoint := &BreakPoint{ - FunctionName: fn.Name, - File: f, - Line: l, - Addr: uint64(addr), - OriginalData: originalData, - } - - thread.BreakPoints[uint64(addr)] = breakpoint - - return breakpoint, nil -} - -// Clears a breakpoint. -func (thread *ThreadContext) Clear(pc uint64) (*BreakPoint, error) { - bp, ok := thread.BreakPoints[pc] - if !ok { - return nil, fmt.Errorf("No breakpoint currently set for %#v", pc) - } - - _, err := syscall.PtracePokeData(thread.Id, uintptr(bp.Addr), bp.OriginalData) - if err != nil { - return nil, err - } - - delete(thread.BreakPoints, pc) - - return bp, nil -} - func (thread *ThreadContext) Continue() error { // Stepping first will ensure we are able to continue // past a breakpoint if that's currently where we are stopped. @@ -154,10 +76,10 @@ func (thread *ThreadContext) Step() (err error) { return err } - bp, ok := thread.BreakPoints[regs.PC()-1] + bp, ok := thread.Process.BreakPoints[regs.PC()-1] if ok { // Clear the breakpoint so that we can continue execution. - _, err = thread.Clear(bp.Addr) + _, err = thread.Process.Clear(bp.Addr) if err != nil { return err } @@ -171,7 +93,7 @@ func (thread *ThreadContext) Step() (err error) { // Restore breakpoint now that we have passed it. defer func() { - _, err = thread.Break(uintptr(bp.Addr)) + _, err = thread.Process.Break(uintptr(bp.Addr)) }() } @@ -195,7 +117,7 @@ func (thread *ThreadContext) Next() (err error) { return err } - if _, ok := thread.BreakPoints[pc-1]; ok { + if _, ok := thread.Process.BreakPoints[pc-1]; ok { // Decrement the PC to be before // the breakpoint instruction. pc-- @@ -251,7 +173,7 @@ func (thread *ThreadContext) continueToReturnAddress(pc uint64, fde *frame.Frame // has not had a chance to modify its' stack // and change our offset. addr := thread.Process.ReturnAddressFromOffset(0) - bp, err := thread.Break(uintptr(addr)) + bp, err := thread.Process.Break(uintptr(addr)) if err != nil { if _, ok := err.(BreakPointExistsError); !ok { return err @@ -280,14 +202,14 @@ func (thread *ThreadContext) continueToReturnAddress(pc uint64, fde *frame.Frame } func (thread *ThreadContext) clearTempBreakpoint(pc uint64) error { - if bp, ok := thread.BreakPoints[pc]; ok { + if bp, ok := thread.Process.BreakPoints[pc]; ok { regs, err := thread.Registers() if err != nil { return err } // Reset program counter to our restored instruction. - bp, err = thread.Clear(bp.Addr) + bp, err = thread.Process.Clear(bp.Addr) if err != nil { return err } -- GitLab