2014-12-08 23:40:59 +00:00
|
|
|
package proctl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
2015-02-02 21:09:56 +00:00
|
|
|
|
2015-01-25 23:16:18 +00:00
|
|
|
sys "golang.org/x/sys/unix"
|
2014-12-08 23:40:59 +00:00
|
|
|
|
|
|
|
"github.com/derekparker/delve/dwarf/frame"
|
|
|
|
)
|
|
|
|
|
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 {
|
|
|
|
Id int
|
|
|
|
Process *DebuggedProcess
|
2015-01-25 23:16:18 +00:00
|
|
|
Status *sys.WaitStatus
|
2015-01-14 02:37:10 +00:00
|
|
|
os *OSSpecificDetails
|
2014-12-08 23:54:34 +00:00
|
|
|
}
|
|
|
|
|
2015-02-02 21:09:56 +00:00
|
|
|
// An interface for a generic register type. The
|
|
|
|
// interface encapsulates the generic values / actions
|
|
|
|
// we need independant of arch. The concrete register types
|
|
|
|
// will be different depending on OS/Arch.
|
2014-12-08 23:54:34 +00:00
|
|
|
type Registers interface {
|
|
|
|
PC() uint64
|
|
|
|
SP() uint64
|
2015-01-14 02:37:10 +00:00
|
|
|
SetPC(*ThreadContext, uint64) error
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Obtains register values from the debugged process.
|
2014-12-08 23:54:34 +00:00
|
|
|
func (thread *ThreadContext) Registers() (Registers, error) {
|
2015-01-14 02:37:10 +00:00
|
|
|
regs, err := registers(thread)
|
2014-12-08 23:40:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not get registers %s", err)
|
|
|
|
}
|
2014-12-08 23:54:34 +00:00
|
|
|
return regs, nil
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|
|
|
|
|
2015-02-02 21:09:56 +00:00
|
|
|
// Returns the current PC for this thread.
|
2014-12-08 23:40:59 +00:00
|
|
|
func (thread *ThreadContext) CurrentPC() (uint64, error) {
|
|
|
|
regs, err := thread.Registers()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return regs.PC(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// PrintInfo prints out the thread status
|
|
|
|
// including: PC, tid, file, line, and function.
|
|
|
|
func (thread *ThreadContext) PrintInfo() error {
|
|
|
|
pc, err := thread.CurrentPC()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
f, l, fn := thread.Process.GoSymTable.PCToLine(pc)
|
|
|
|
if fn != nil {
|
|
|
|
fmt.Printf("Thread %d at %#v %s:%d %s\n", thread.Id, pc, f, l, fn.Name)
|
|
|
|
} else {
|
|
|
|
fmt.Printf("Thread %d at %#v\n", thread.Id, pc)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
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 {
|
|
|
|
// Check whether we are stopped at a breakpoint, and
|
|
|
|
// if so, single step over it before continuing.
|
|
|
|
regs, err := thread.Registers()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not get registers %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := thread.Process.BreakPoints[regs.PC()-1]; ok {
|
|
|
|
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) {
|
|
|
|
regs, err := thread.Registers()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
bp, ok := thread.Process.BreakPoints[regs.PC()-1]
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset program counter to our restored instruction.
|
2015-01-14 02:37:10 +00:00
|
|
|
err = regs.SetPC(thread, bp.Addr)
|
2014-12-08 23:40:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not set registers %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore breakpoint now that we have passed it.
|
|
|
|
defer func() {
|
2015-01-14 02:37:10 +00:00
|
|
|
_, err = thread.Process.Break(bp.Addr)
|
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
|
|
|
}
|
|
|
|
|
|
|
|
// Step to next source line. Next will step over functions,
|
|
|
|
// and will follow through to the return address of a function.
|
|
|
|
// Next is implemented on the thread context, however during the
|
|
|
|
// course of this function running, it's very likely that the
|
|
|
|
// goroutine our M is executing will switch to another M, therefore
|
|
|
|
// this function cannot assume all execution will happen on this thread
|
|
|
|
// in the traced process.
|
|
|
|
func (thread *ThreadContext) Next() (err error) {
|
|
|
|
pc, err := thread.CurrentPC()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-12-29 02:48:58 +00:00
|
|
|
if bp, ok := thread.Process.BreakPoints[pc-1]; ok {
|
|
|
|
pc = bp.Addr
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fde, err := thread.Process.FrameEntries.FDEForPC(pc)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-12-29 02:48:58 +00:00
|
|
|
_, l, _ := thread.Process.GoSymTable.PCToLine(pc)
|
2014-12-08 23:40:59 +00:00
|
|
|
ret := thread.ReturnAddressFromOffset(fde.ReturnAddressOffset(pc))
|
|
|
|
for {
|
2014-12-29 02:48:58 +00:00
|
|
|
if err = thread.Step(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if pc, err = thread.CurrentPC(); err != nil {
|
2014-12-08 23:40:59 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !fde.Cover(pc) && pc != ret {
|
2014-12-29 02:48:58 +00:00
|
|
|
if err := thread.continueToReturnAddress(pc, fde); err != nil {
|
2014-12-08 23:40:59 +00:00
|
|
|
if _, ok := err.(InvalidAddressError); !ok {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2014-12-29 02:48:58 +00:00
|
|
|
if pc, err = thread.CurrentPC(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|
|
|
|
|
2014-12-29 02:48:58 +00:00
|
|
|
if _, nl, _ := thread.Process.GoSymTable.PCToLine(pc); nl != l {
|
2014-12-08 23:40:59 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (thread *ThreadContext) continueToReturnAddress(pc uint64, fde *frame.FrameDescriptionEntry) error {
|
|
|
|
for !fde.Cover(pc) {
|
2015-01-14 02:37:10 +00:00
|
|
|
// Offset is 0 because we have just stepped into this function.
|
2014-12-08 23:40:59 +00:00
|
|
|
addr := thread.ReturnAddressFromOffset(0)
|
2015-01-14 02:37:10 +00:00
|
|
|
bp, err := thread.Process.Break(addr)
|
2014-12-08 23:40:59 +00:00
|
|
|
if err != nil {
|
|
|
|
if _, ok := err.(BreakPointExistsError); !ok {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2015-03-06 14:01:41 +00:00
|
|
|
bp.Temp = true
|
2014-12-08 23:40:59 +00:00
|
|
|
// Ensure we cleanup after ourselves no matter what.
|
|
|
|
defer thread.clearTempBreakpoint(bp.Addr)
|
|
|
|
|
|
|
|
for {
|
|
|
|
err = thread.Continue()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-01-14 02:37:10 +00:00
|
|
|
// Wait on -1, just in case scheduler switches threads for this G.
|
2015-01-09 22:24:33 +00:00
|
|
|
wpid, _, err := trapWait(thread.Process, -1)
|
2014-12-08 23:40:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if wpid != thread.Id {
|
|
|
|
thread = thread.Process.Threads[wpid]
|
|
|
|
}
|
2015-01-14 02:37:10 +00:00
|
|
|
pc, err = thread.CurrentPC()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-01-10 23:33:06 +00:00
|
|
|
if (pc-1) == bp.Addr || pc == bp.Addr {
|
2014-12-08 23:40:59 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Takes an offset from RSP and returns the address of the
|
|
|
|
// instruction the currect function is going to return to.
|
|
|
|
func (thread *ThreadContext) ReturnAddressFromOffset(offset int64) uint64 {
|
|
|
|
regs, err := thread.Registers()
|
|
|
|
if err != nil {
|
|
|
|
panic("Could not obtain register values")
|
|
|
|
}
|
|
|
|
|
2014-12-08 23:54:34 +00:00
|
|
|
retaddr := int64(regs.SP()) + offset
|
2014-12-08 23:40:59 +00:00
|
|
|
data := make([]byte, 8)
|
2015-01-14 02:37:10 +00:00
|
|
|
readMemory(thread, uintptr(retaddr), data)
|
2014-12-08 23:40:59 +00:00
|
|
|
return binary.LittleEndian.Uint64(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (thread *ThreadContext) clearTempBreakpoint(pc uint64) error {
|
2015-01-10 23:33:06 +00:00
|
|
|
var software bool
|
|
|
|
if _, ok := thread.Process.BreakPoints[pc]; ok {
|
|
|
|
software = true
|
|
|
|
}
|
2015-01-14 02:37:10 +00:00
|
|
|
if _, err := thread.Process.Clear(pc); err != nil {
|
2015-01-10 23:33:06 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if software {
|
2014-12-08 23:40:59 +00:00
|
|
|
// Reset program counter to our restored instruction.
|
|
|
|
regs, err := thread.Registers()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-01-14 02:37:10 +00:00
|
|
|
return regs.SetPC(thread, pc)
|
2014-12-08 23:40:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|