Improve 'next' impl for non-go next'ing

This commit is contained in:
Derek Parker 2015-04-20 13:03:22 -05:00
parent 58db8322ef
commit 48d0703b4f
5 changed files with 72 additions and 35 deletions

@ -105,6 +105,25 @@ func (dbl *DebugLineInfo) AllPCsForFileLine(f string, l int) (pcs []uint64) {
return
}
func (dbl *DebugLineInfo) AllPCsBetween(begin, end uint64) []uint64 {
var (
pcs []uint64
sm = newStateMachine(dbl)
buf = bytes.NewBuffer(dbl.Instructions)
)
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
findAndExecOpcode(sm, buf, b)
if sm.address > end {
break
}
if sm.address >= begin && sm.address <= end {
pcs = append(pcs, sm.address)
}
}
return pcs
}
func findAndExecOpcode(sm *StateMachine, buf *bytes.Buffer, b byte) {
switch {
case b == 0:

@ -130,6 +130,14 @@ func (dbp *DebuggedProcess) setBreakpoint(tid int, addr uint64, temp bool) (*Bre
return dbp.BreakPoints[addr], nil
}
type NoBreakPointError struct {
addr uint64
}
func (nbp NoBreakPointError) Error() string {
return fmt.Sprintf("no breakpoint at %#v", nbp.addr)
}
func (dbp *DebuggedProcess) clearBreakpoint(tid int, addr uint64) (*BreakPoint, error) {
thread := dbp.Threads[tid]
// Check for hardware breakpoint
@ -154,5 +162,5 @@ func (dbp *DebuggedProcess) clearBreakpoint(tid int, addr uint64) (*BreakPoint,
delete(dbp.BreakPoints, addr)
return bp, nil
}
return nil, fmt.Errorf("no breakpoint at %#v", addr)
return nil, NoBreakPointError{addr: addr}
}

@ -31,7 +31,6 @@ type DebuggedProcess struct {
HWBreakPoints [4]*BreakPoint
BreakPoints map[uint64]*BreakPoint
Threads map[int]*ThreadContext
CurrentBreakpoint *BreakPoint
CurrentThread *ThreadContext
dwarf *dwarf.Data
goSymTable *gosym.Table
@ -305,12 +304,9 @@ func (dbp *DebuggedProcess) next() error {
if goroutineExiting {
break
}
if dbp.CurrentBreakpoint != nil {
bp, err := dbp.Clear(dbp.CurrentBreakpoint.Addr)
if err != nil {
return err
}
if !bp.hardware {
if thread.CurrentBreakpoint != nil {
bp := thread.CurrentBreakpoint
if bp != nil && !bp.hardware {
if err = thread.SetPC(bp.Addr); err != nil {
return err
}
@ -322,7 +318,7 @@ func (dbp *DebuggedProcess) next() error {
return err
}
// Make sure we're on the same goroutine.
// TODO(dp) take into account goroutine exit.
// TODO(dp) better take into account goroutine exit.
if tg.Id == curg.Id {
if dbp.CurrentThread != thread {
dbp.SwitchThread(thread.Id)
@ -450,6 +446,11 @@ func (dbp *DebuggedProcess) CurrentPC() (uint64, error) {
return dbp.CurrentThread.CurrentPC()
}
// Returns the PC of the current thread.
func (dbp *DebuggedProcess) CurrentBreakpoint() *BreakPoint {
return dbp.CurrentThread.CurrentBreakpoint
}
// Returns the value of the named symbol.
func (dbp *DebuggedProcess) EvalSymbol(name string) (*Variable, error) {
return dbp.CurrentThread.EvalSymbol(name)
@ -527,6 +528,7 @@ func newDebugProcess(pid int, attach bool) (*DebuggedProcess, error) {
return &dbp, nil
}
func (dbp *DebuggedProcess) clearTempBreakpoints() error {
for _, bp := range dbp.HWBreakPoints {
if bp != nil && bp.Temp {
@ -545,6 +547,7 @@ func (dbp *DebuggedProcess) clearTempBreakpoints() error {
}
return nil
}
func (dbp *DebuggedProcess) handleBreakpointOnThread(id int) (*ThreadContext, error) {
thread, ok := dbp.Threads[id]
if !ok {
@ -557,13 +560,13 @@ func (dbp *DebuggedProcess) handleBreakpointOnThread(id int) (*ThreadContext, er
// Check for hardware breakpoint
for _, bp := range dbp.HWBreakPoints {
if bp != nil && bp.Addr == pc {
dbp.CurrentBreakpoint = bp
thread.CurrentBreakpoint = bp
return thread, nil
}
}
// Check to see if we have hit a software breakpoint.
if bp, ok := dbp.BreakPoints[pc-1]; ok {
dbp.CurrentBreakpoint = bp
thread.CurrentBreakpoint = bp
return thread, nil
}
return thread, nil
@ -575,7 +578,9 @@ func (dbp *DebuggedProcess) run(fn func() error) error {
}
dbp.running = true
dbp.halt = false
dbp.CurrentBreakpoint = nil
for _, th := range dbp.Threads {
th.CurrentBreakpoint = nil
}
defer func() { dbp.running = false }()
if err := fn(); err != nil {
if _, ok := err.(ManualStopError); !ok {

@ -14,11 +14,12 @@ import (
// 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
running bool
os *OSSpecificDetails
Id int
Process *DebuggedProcess
Status *sys.WaitStatus
CurrentBreakpoint *BreakPoint
running bool
os *OSSpecificDetails
}
// An interface for a generic register type. The
@ -182,7 +183,7 @@ func (thread *ThreadContext) Next() (err error) {
return err
}
} else {
if err = thread.cnext(curpc, fde, f, l); err != nil {
if err = thread.cnext(curpc, fde); err != nil {
return err
}
}
@ -233,20 +234,30 @@ func (thread *ThreadContext) next(curpc uint64, fde *frame.FrameDescriptionEntry
}
return GoroutineExitingError{goid: g.Id}
}
pcs = append(pcs, ret)
}
pcs = append(pcs, ret)
return thread.setNextTempBreakpoints(curpc, pcs)
}
// Set a breakpoint at every line reachable from our location.
for _, pc := range pcs {
// Do not set breakpoint at our current location.
if pc == curpc {
// Set a breakpoint at every reachable location, as well as the return address. Without
// the benefit of an AST we can't be sure we're not at a branching statement and thus
// cannot accurately predict where we may end up.
func (thread *ThreadContext) cnext(curpc uint64, fde *frame.FrameDescriptionEntry) error {
pcs := thread.Process.lineInfo.AllPCsBetween(fde.Begin(), fde.End())
ret, err := thread.ReturnAddress()
if err != nil {
return err
}
pcs = append(pcs, ret)
return thread.setNextTempBreakpoints(curpc, pcs)
}
func (thread *ThreadContext) setNextTempBreakpoints(curpc uint64, pcs []uint64) error {
for i := range pcs {
if pcs[i] == curpc || pcs[i] == curpc-1 {
continue
}
// If the PC is not covered by our frame, set breakpoint at return address.
if !fde.Cover(pc) {
pc = ret
}
if _, err := thread.Process.TempBreak(pc); err != nil {
if _, err := thread.Process.TempBreak(pcs[i]); err != nil {
if err, ok := err.(BreakPointExistsError); !ok {
return err
}
@ -255,12 +266,6 @@ func (thread *ThreadContext) next(curpc uint64, fde *frame.FrameDescriptionEntry
return nil
}
func (thread *ThreadContext) cnext(curpc uint64, fde *frame.FrameDescriptionEntry, file string, line int) error {
// TODO(dp) We are not in a Go source file, we should fall back on DWARF line information
// and use that to set breakpoints.
return nil
}
func (thread *ThreadContext) SetPC(pc uint64) error {
regs, err := thread.Registers()
if err != nil {

@ -50,7 +50,7 @@ func (t *ThreadContext) blocked() bool {
// TODO(dp) cache the func pc to remove this lookup
pc, _ := t.CurrentPC()
fn := t.Process.goSymTable.PCToFunc(pc)
if fn != nil && (fn.Name == "runtime.mach_semaphore_wait") {
if fn != nil && (fn.Name == "runtime.mach_semaphore_wait" || fn.Name == "runtime.usleep") {
return true
}
return false