Improve 'next' impl for non-go next'ing
This commit is contained in:
parent
58db8322ef
commit
48d0703b4f
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user