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
|
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) {
|
func findAndExecOpcode(sm *StateMachine, buf *bytes.Buffer, b byte) {
|
||||||
switch {
|
switch {
|
||||||
case b == 0:
|
case b == 0:
|
||||||
|
@ -130,6 +130,14 @@ func (dbp *DebuggedProcess) setBreakpoint(tid int, addr uint64, temp bool) (*Bre
|
|||||||
return dbp.BreakPoints[addr], nil
|
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) {
|
func (dbp *DebuggedProcess) clearBreakpoint(tid int, addr uint64) (*BreakPoint, error) {
|
||||||
thread := dbp.Threads[tid]
|
thread := dbp.Threads[tid]
|
||||||
// Check for hardware breakpoint
|
// Check for hardware breakpoint
|
||||||
@ -154,5 +162,5 @@ func (dbp *DebuggedProcess) clearBreakpoint(tid int, addr uint64) (*BreakPoint,
|
|||||||
delete(dbp.BreakPoints, addr)
|
delete(dbp.BreakPoints, addr)
|
||||||
return bp, nil
|
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
|
HWBreakPoints [4]*BreakPoint
|
||||||
BreakPoints map[uint64]*BreakPoint
|
BreakPoints map[uint64]*BreakPoint
|
||||||
Threads map[int]*ThreadContext
|
Threads map[int]*ThreadContext
|
||||||
CurrentBreakpoint *BreakPoint
|
|
||||||
CurrentThread *ThreadContext
|
CurrentThread *ThreadContext
|
||||||
dwarf *dwarf.Data
|
dwarf *dwarf.Data
|
||||||
goSymTable *gosym.Table
|
goSymTable *gosym.Table
|
||||||
@ -305,12 +304,9 @@ func (dbp *DebuggedProcess) next() error {
|
|||||||
if goroutineExiting {
|
if goroutineExiting {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if dbp.CurrentBreakpoint != nil {
|
if thread.CurrentBreakpoint != nil {
|
||||||
bp, err := dbp.Clear(dbp.CurrentBreakpoint.Addr)
|
bp := thread.CurrentBreakpoint
|
||||||
if err != nil {
|
if bp != nil && !bp.hardware {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !bp.hardware {
|
|
||||||
if err = thread.SetPC(bp.Addr); err != nil {
|
if err = thread.SetPC(bp.Addr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -322,7 +318,7 @@ func (dbp *DebuggedProcess) next() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Make sure we're on the same goroutine.
|
// 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 tg.Id == curg.Id {
|
||||||
if dbp.CurrentThread != thread {
|
if dbp.CurrentThread != thread {
|
||||||
dbp.SwitchThread(thread.Id)
|
dbp.SwitchThread(thread.Id)
|
||||||
@ -450,6 +446,11 @@ func (dbp *DebuggedProcess) CurrentPC() (uint64, error) {
|
|||||||
return dbp.CurrentThread.CurrentPC()
|
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.
|
// Returns the value of the named symbol.
|
||||||
func (dbp *DebuggedProcess) EvalSymbol(name string) (*Variable, error) {
|
func (dbp *DebuggedProcess) EvalSymbol(name string) (*Variable, error) {
|
||||||
return dbp.CurrentThread.EvalSymbol(name)
|
return dbp.CurrentThread.EvalSymbol(name)
|
||||||
@ -527,6 +528,7 @@ func newDebugProcess(pid int, attach bool) (*DebuggedProcess, error) {
|
|||||||
|
|
||||||
return &dbp, nil
|
return &dbp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *DebuggedProcess) clearTempBreakpoints() error {
|
func (dbp *DebuggedProcess) clearTempBreakpoints() error {
|
||||||
for _, bp := range dbp.HWBreakPoints {
|
for _, bp := range dbp.HWBreakPoints {
|
||||||
if bp != nil && bp.Temp {
|
if bp != nil && bp.Temp {
|
||||||
@ -545,6 +547,7 @@ func (dbp *DebuggedProcess) clearTempBreakpoints() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *DebuggedProcess) handleBreakpointOnThread(id int) (*ThreadContext, error) {
|
func (dbp *DebuggedProcess) handleBreakpointOnThread(id int) (*ThreadContext, error) {
|
||||||
thread, ok := dbp.Threads[id]
|
thread, ok := dbp.Threads[id]
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -557,13 +560,13 @@ func (dbp *DebuggedProcess) handleBreakpointOnThread(id int) (*ThreadContext, er
|
|||||||
// Check for hardware breakpoint
|
// Check for hardware breakpoint
|
||||||
for _, bp := range dbp.HWBreakPoints {
|
for _, bp := range dbp.HWBreakPoints {
|
||||||
if bp != nil && bp.Addr == pc {
|
if bp != nil && bp.Addr == pc {
|
||||||
dbp.CurrentBreakpoint = bp
|
thread.CurrentBreakpoint = bp
|
||||||
return thread, nil
|
return thread, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check to see if we have hit a software breakpoint.
|
// Check to see if we have hit a software breakpoint.
|
||||||
if bp, ok := dbp.BreakPoints[pc-1]; ok {
|
if bp, ok := dbp.BreakPoints[pc-1]; ok {
|
||||||
dbp.CurrentBreakpoint = bp
|
thread.CurrentBreakpoint = bp
|
||||||
return thread, nil
|
return thread, nil
|
||||||
}
|
}
|
||||||
return thread, nil
|
return thread, nil
|
||||||
@ -575,7 +578,9 @@ func (dbp *DebuggedProcess) run(fn func() error) error {
|
|||||||
}
|
}
|
||||||
dbp.running = true
|
dbp.running = true
|
||||||
dbp.halt = false
|
dbp.halt = false
|
||||||
dbp.CurrentBreakpoint = nil
|
for _, th := range dbp.Threads {
|
||||||
|
th.CurrentBreakpoint = nil
|
||||||
|
}
|
||||||
defer func() { dbp.running = false }()
|
defer func() { dbp.running = false }()
|
||||||
if err := fn(); err != nil {
|
if err := fn(); err != nil {
|
||||||
if _, ok := err.(ManualStopError); !ok {
|
if _, ok := err.(ManualStopError); !ok {
|
||||||
|
@ -14,11 +14,12 @@ import (
|
|||||||
// a whole, and Status represents the last result of a `wait` call
|
// a whole, and Status represents the last result of a `wait` call
|
||||||
// on this thread.
|
// on this thread.
|
||||||
type ThreadContext struct {
|
type ThreadContext struct {
|
||||||
Id int
|
Id int
|
||||||
Process *DebuggedProcess
|
Process *DebuggedProcess
|
||||||
Status *sys.WaitStatus
|
Status *sys.WaitStatus
|
||||||
running bool
|
CurrentBreakpoint *BreakPoint
|
||||||
os *OSSpecificDetails
|
running bool
|
||||||
|
os *OSSpecificDetails
|
||||||
}
|
}
|
||||||
|
|
||||||
// An interface for a generic register type. The
|
// An interface for a generic register type. The
|
||||||
@ -182,7 +183,7 @@ func (thread *ThreadContext) Next() (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err = thread.cnext(curpc, fde, f, l); err != nil {
|
if err = thread.cnext(curpc, fde); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,20 +234,30 @@ func (thread *ThreadContext) next(curpc uint64, fde *frame.FrameDescriptionEntry
|
|||||||
}
|
}
|
||||||
return GoroutineExitingError{goid: g.Id}
|
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.
|
// Set a breakpoint at every reachable location, as well as the return address. Without
|
||||||
for _, pc := range pcs {
|
// the benefit of an AST we can't be sure we're not at a branching statement and thus
|
||||||
// Do not set breakpoint at our current location.
|
// cannot accurately predict where we may end up.
|
||||||
if pc == curpc {
|
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
|
continue
|
||||||
}
|
}
|
||||||
// If the PC is not covered by our frame, set breakpoint at return address.
|
if _, err := thread.Process.TempBreak(pcs[i]); err != nil {
|
||||||
if !fde.Cover(pc) {
|
|
||||||
pc = ret
|
|
||||||
}
|
|
||||||
if _, err := thread.Process.TempBreak(pc); err != nil {
|
|
||||||
if err, ok := err.(BreakPointExistsError); !ok {
|
if err, ok := err.(BreakPointExistsError); !ok {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -255,12 +266,6 @@ func (thread *ThreadContext) next(curpc uint64, fde *frame.FrameDescriptionEntry
|
|||||||
return nil
|
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 {
|
func (thread *ThreadContext) SetPC(pc uint64) error {
|
||||||
regs, err := thread.Registers()
|
regs, err := thread.Registers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -50,7 +50,7 @@ func (t *ThreadContext) blocked() bool {
|
|||||||
// TODO(dp) cache the func pc to remove this lookup
|
// TODO(dp) cache the func pc to remove this lookup
|
||||||
pc, _ := t.CurrentPC()
|
pc, _ := t.CurrentPC()
|
||||||
fn := t.Process.goSymTable.PCToFunc(pc)
|
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 true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
Loading…
Reference in New Issue
Block a user