提交 c625f09a 编写于 作者: D Derek Parker

Promote breakpoints back up to process

上级 4c95bf73
......@@ -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/<pid>/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 {
......
......@@ -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")
}
})
......
......@@ -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
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册