2014-12-08 23:40:59 +00:00
|
|
|
package proctl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-04-19 22:11:33 +00:00
|
|
|
"path/filepath"
|
2015-02-02 21:09:56 +00:00
|
|
|
|
2015-04-19 22:11:33 +00:00
|
|
|
"github.com/derekparker/delve/dwarf/frame"
|
2015-01-25 23:16:18 +00:00
|
|
|
sys "golang.org/x/sys/unix"
|
2014-12-08 23:40:59 +00:00
|
|
|
)
|
|
|
|
|
2015-02-02 21:09:56 +00:00
|
|
|
// ThreadContext represents a single thread in the traced process
|
|
|
|
// Id represents the thread id, Process holds a reference to the
|
|
|
|
// DebuggedProcess struct that contains info on the process as
|
|
|
|
// a whole, and Status represents the last result of a `wait` call
|
|
|
|
// on this thread.
|
2014-12-08 23:40:59 +00:00
|
|
|
type ThreadContext struct {
|
2015-04-20 18:03:22 +00:00
|
|
|
Id int
|
|
|
|
Process *DebuggedProcess
|
|
|
|
Status *sys.WaitStatus
|
|
|
|
CurrentBreakpoint *BreakPoint
|
2015-04-25 19:53:55 +00:00
|
|
|
singleStepping bool
|
2015-04-20 18:03:22 +00:00
|
|
|
running bool
|
|
|
|
os *OSSpecificDetails
|
2014-12-08 23:54:34 +00:00
|
|
|
}
|
|
|
|
|
2015-02-02 21:09:56 +00:00
|
|
|
// Continue the execution of this thread. This method takes
|
|
|
|
// software breakpoints into consideration and ensures that
|
|
|
|
// we step over any breakpoints. It will restore the instruction,
|
|
|
|
// step, and then restore the breakpoint and continue.
|
2014-12-08 23:40:59 +00:00
|
|
|
func (thread *ThreadContext) Continue() error {
|
2015-04-23 15:40:33 +00:00
|
|
|
pc, err := thread.PC()
|
2014-12-08 23:40:59 +00:00
|
|
|
if err != nil {
|
2015-03-05 22:59:51 +00:00
|
|
|
return err
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|
|
|
|
|
2015-03-05 22:59:51 +00:00
|
|
|
// Check whether we are stopped at a breakpoint, and
|
|
|
|
// if so, single step over it before continuing.
|
2015-04-23 15:54:26 +00:00
|
|
|
if _, ok := thread.Process.BreakPoints[pc]; ok {
|
2014-12-08 23:40:59 +00:00
|
|
|
err := thread.Step()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not step %s", err)
|
|
|
|
}
|
|
|
|
}
|
2015-03-01 03:14:22 +00:00
|
|
|
return thread.resume()
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Single steps this thread a single instruction, ensuring that
|
|
|
|
// we correctly handle the likely case that we are at a breakpoint.
|
|
|
|
func (thread *ThreadContext) Step() (err error) {
|
2015-04-25 19:53:55 +00:00
|
|
|
thread.singleStepping = true
|
|
|
|
defer func() { thread.singleStepping = false }()
|
2015-04-23 15:40:33 +00:00
|
|
|
pc, err := thread.PC()
|
2014-12-08 23:40:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-04-23 15:54:26 +00:00
|
|
|
bp, ok := thread.Process.BreakPoints[pc]
|
2014-12-08 23:40:59 +00:00
|
|
|
if ok {
|
|
|
|
// Clear the breakpoint so that we can continue execution.
|
2015-01-14 02:37:10 +00:00
|
|
|
_, err = thread.Process.Clear(bp.Addr)
|
2014-12-08 23:40:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore breakpoint now that we have passed it.
|
|
|
|
defer func() {
|
2015-03-28 01:12:07 +00:00
|
|
|
var nbp *BreakPoint
|
|
|
|
nbp, err = thread.Process.Break(bp.Addr)
|
|
|
|
nbp.Temp = bp.Temp
|
2014-12-08 23:40:59 +00:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2015-01-14 02:37:10 +00:00
|
|
|
err = thread.singleStep()
|
2014-12-08 23:40:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("step failed: %s", err.Error())
|
|
|
|
}
|
|
|
|
|
2015-02-17 17:27:47 +00:00
|
|
|
return err
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|
|
|
|
|
2015-03-28 01:12:07 +00:00
|
|
|
// Call a function named `name`. This is currently _NOT_ safe.
|
2015-04-19 22:11:33 +00:00
|
|
|
func (thread *ThreadContext) CallFn(name string, fn func() error) error {
|
2015-04-03 16:10:35 +00:00
|
|
|
f := thread.Process.goSymTable.LookupFunc(name)
|
2015-03-28 01:12:07 +00:00
|
|
|
if f == nil {
|
|
|
|
return fmt.Errorf("could not find function %s", name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set breakpoint at the end of the function (before it returns).
|
2015-04-19 22:11:33 +00:00
|
|
|
bp, err := thread.Break(f.End - 2)
|
2014-12-08 23:40:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-03-28 01:12:07 +00:00
|
|
|
defer thread.Process.Clear(bp.Addr)
|
2014-12-08 23:40:59 +00:00
|
|
|
|
2015-04-23 15:30:27 +00:00
|
|
|
if err = thread.saveRegisters(); err != nil {
|
2015-03-28 01:12:07 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err = thread.SetPC(f.Entry); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer thread.restoreRegisters()
|
2015-04-23 15:30:27 +00:00
|
|
|
if err = thread.Continue(); err != nil {
|
2015-03-28 01:12:07 +00:00
|
|
|
return err
|
|
|
|
}
|
2015-04-23 15:42:28 +00:00
|
|
|
th, err := thread.Process.trapWait(-1)
|
|
|
|
if err != nil {
|
2015-03-28 01:12:07 +00:00
|
|
|
return err
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|
2015-04-23 15:42:28 +00:00
|
|
|
th.CurrentBreakpoint = nil
|
2015-04-19 22:11:33 +00:00
|
|
|
return fn()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set breakpoint using this thread.
|
|
|
|
func (thread *ThreadContext) Break(addr uint64) (*BreakPoint, error) {
|
|
|
|
return thread.Process.setBreakpoint(thread.Id, addr, false)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clear breakpoint using this thread.
|
|
|
|
func (thread *ThreadContext) Clear(addr uint64) (*BreakPoint, error) {
|
|
|
|
return thread.Process.clearBreakpoint(thread.Id, addr)
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
2014-12-08 23:40:59 +00:00
|
|
|
|
2015-03-28 01:12:07 +00:00
|
|
|
// Step to next source line.
|
|
|
|
//
|
|
|
|
// Next will step over functions, and will follow through to the
|
|
|
|
// return address of a function.
|
|
|
|
//
|
|
|
|
// This functionality is implemented by finding all possible next lines
|
|
|
|
// and setting a breakpoint at them. Once we've set a breakpoint at each
|
|
|
|
// potential line, we continue the thread.
|
|
|
|
func (thread *ThreadContext) Next() (err error) {
|
2015-04-23 15:40:33 +00:00
|
|
|
curpc, err := thread.PC()
|
2014-12-08 23:40:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-03-28 01:12:07 +00:00
|
|
|
// Grab info on our current stack frame. Used to determine
|
|
|
|
// whether we may be stepping outside of the current function.
|
2015-04-03 16:10:35 +00:00
|
|
|
fde, err := thread.Process.frameEntries.FDEForPC(curpc)
|
2015-03-28 01:12:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|
|
|
|
|
2015-03-28 01:12:07 +00:00
|
|
|
// Get current file/line.
|
2015-04-03 16:10:35 +00:00
|
|
|
f, l, _ := thread.Process.goSymTable.PCToLine(curpc)
|
2015-04-19 22:11:33 +00:00
|
|
|
if filepath.Ext(f) == ".go" {
|
|
|
|
if err = thread.next(curpc, fde, f, l); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
2015-04-20 18:03:22 +00:00
|
|
|
if err = thread.cnext(curpc, fde); err != nil {
|
2015-04-19 22:11:33 +00:00
|
|
|
return err
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|
|
|
|
}
|
2015-03-28 01:12:07 +00:00
|
|
|
return thread.Continue()
|
|
|
|
}
|
2014-12-08 23:40:59 +00:00
|
|
|
|
2015-04-19 22:11:33 +00:00
|
|
|
// Go routine is exiting.
|
|
|
|
type GoroutineExitingError struct {
|
|
|
|
goid int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ge GoroutineExitingError) Error() string {
|
|
|
|
return fmt.Sprintf("goroutine %d is exiting", ge.goid)
|
|
|
|
}
|
|
|
|
|
|
|
|
// This version of next uses the AST from the current source file to figure out all of the potential source lines
|
|
|
|
// we could end up at.
|
|
|
|
func (thread *ThreadContext) next(curpc uint64, fde *frame.FrameDescriptionEntry, file string, line int) error {
|
|
|
|
lines, err := thread.Process.ast.NextLines(file, line)
|
2015-03-28 01:12:07 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-12-08 23:40:59 +00:00
|
|
|
|
2015-04-19 22:11:33 +00:00
|
|
|
ret, err := thread.ReturnAddress()
|
2014-12-08 23:40:59 +00:00
|
|
|
if err != nil {
|
2015-04-19 22:11:33 +00:00
|
|
|
return err
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|
|
|
|
|
2015-04-19 22:11:33 +00:00
|
|
|
pcs := make([]uint64, 0, len(lines))
|
|
|
|
for i := range lines {
|
|
|
|
pcs = append(pcs, thread.Process.lineInfo.AllPCsForFileLine(file, lines[i])...)
|
|
|
|
}
|
2014-12-08 23:40:59 +00:00
|
|
|
|
2015-04-19 22:11:33 +00:00
|
|
|
var covered bool
|
|
|
|
for i := range pcs {
|
|
|
|
if fde.Cover(pcs[i]) {
|
|
|
|
covered = true
|
|
|
|
break
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
2015-01-10 23:33:06 +00:00
|
|
|
}
|
2015-04-19 22:11:33 +00:00
|
|
|
|
|
|
|
if !covered {
|
|
|
|
fn := thread.Process.goSymTable.PCToFunc(ret)
|
|
|
|
if fn != nil && fn.Name == "runtime.goexit" {
|
|
|
|
g, err := thread.curG()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return GoroutineExitingError{goid: g.Id}
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
2015-01-10 23:33:06 +00:00
|
|
|
}
|
2015-04-20 18:03:22 +00:00
|
|
|
pcs = append(pcs, ret)
|
|
|
|
return thread.setNextTempBreakpoints(curpc, pcs)
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
}
|
2015-04-19 22:11:33 +00:00
|
|
|
|
2015-04-20 18:03:22 +00:00
|
|
|
func (thread *ThreadContext) setNextTempBreakpoints(curpc uint64, pcs []uint64) error {
|
|
|
|
for i := range pcs {
|
|
|
|
if pcs[i] == curpc || pcs[i] == curpc-1 {
|
2015-04-19 22:11:33 +00:00
|
|
|
continue
|
|
|
|
}
|
2015-04-20 18:03:22 +00:00
|
|
|
if _, err := thread.Process.TempBreak(pcs[i]); err != nil {
|
2015-04-19 22:11:33 +00:00
|
|
|
if err, ok := err.(BreakPointExistsError); !ok {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2015-03-28 01:12:07 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-04-23 01:00:42 +00:00
|
|
|
// Sets the PC for this thread.
|
2015-04-19 22:11:33 +00:00
|
|
|
func (thread *ThreadContext) SetPC(pc uint64) error {
|
|
|
|
regs, err := thread.Registers()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return regs.SetPC(thread, pc)
|
|
|
|
}
|
|
|
|
|
2015-03-28 01:12:07 +00:00
|
|
|
func (thread *ThreadContext) curG() (*G, error) {
|
|
|
|
var g *G
|
2015-04-19 22:11:33 +00:00
|
|
|
err := thread.CallFn("runtime.getg", func() error {
|
|
|
|
regs, err := thread.Registers()
|
2014-12-08 23:40:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-04-19 22:11:33 +00:00
|
|
|
reader := thread.Process.dwarf.Reader()
|
|
|
|
g, err = parseG(thread, regs.SP()+uint64(ptrsize), reader)
|
2015-03-28 01:12:07 +00:00
|
|
|
return err
|
|
|
|
})
|
|
|
|
return g, err
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|