delve/proctl/threads.go

252 lines
6.3 KiB
Go
Raw Normal View History

2014-12-08 23:40:59 +00:00
package proctl
import (
"encoding/binary"
"fmt"
2015-02-02 21:09:56 +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 {
Id int
Process *DebuggedProcess
Status *sys.WaitStatus
2015-01-14 02:37:10 +00:00
os *OSSpecificDetails
}
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.
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.
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:40:59 +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
}
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 {
pc, err := thread.CurrentPC()
2014-12-08 23:40:59 +00:00
if err != nil {
return err
2014-12-08 23:40:59 +00:00
}
// Check whether we are stopped at a breakpoint, and
// if so, single step over it before continuing.
if _, ok := thread.Process.BreakPoints[pc-1]; 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) {
pc, err := thread.CurrentPC()
2014-12-08 23:40:59 +00:00
if err != nil {
return err
}
bp, ok := thread.Process.BreakPoints[pc-1]
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
}
// Reset program counter to our restored instruction.
err = thread.SetPC(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() {
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
}
// Call a function named `name`. This is currently _NOT_ safe.
func (thread *ThreadContext) CallFn(name string, fn func(*ThreadContext) error) error {
f := thread.Process.goSymTable.LookupFunc(name)
if f == nil {
return fmt.Errorf("could not find function %s", name)
}
// Set breakpoint at the end of the function (before it returns).
bp, err := thread.Process.Break(f.End - 2)
2014-12-08 23:40:59 +00:00
if err != nil {
return err
}
defer thread.Process.Clear(bp.Addr)
2014-12-08 23:40:59 +00:00
if err := thread.saveRegisters(); err != nil {
return err
}
if err = thread.SetPC(f.Entry); err != nil {
return err
}
defer thread.restoreRegisters()
if err := thread.Continue(); err != nil {
return err
}
if _, _, err = trapWait(thread.Process, -1); err != nil {
return err
2014-12-08 23:40:59 +00:00
}
return fn(thread)
}
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.
//
// 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) {
curpc, err := thread.CurrentPC()
2014-12-08 23:40:59 +00:00
if err != nil {
return err
}
// Check and see if we're at a breakpoint, if so
// correct the PC value for the breakpoint instruction.
if bp, ok := thread.Process.BreakPoints[curpc-1]; ok {
curpc = bp.Addr
}
2014-12-08 23:40:59 +00:00
// Grab info on our current stack frame. Used to determine
// whether we may be stepping outside of the current function.
fde, err := thread.Process.frameEntries.FDEForPC(curpc)
if err != nil {
return err
2014-12-08 23:40:59 +00:00
}
// Get current file/line.
f, l, _ := thread.Process.goSymTable.PCToLine(curpc)
2014-12-08 23:40:59 +00:00
// Find any line we could potentially get to.
lines, err := thread.Process.ast.NextLines(f, l)
if err != nil {
return err
}
2014-12-08 23:40:59 +00:00
// Set a breakpoint at every line reachable from our location.
for _, l := range lines {
pcs := thread.Process.lineInfo.AllPCsForFileLine(f, l)
for _, pc := range pcs {
if pc == curpc {
continue
2014-12-08 23:40:59 +00:00
}
if !fde.Cover(pc) {
2015-04-03 16:55:58 +00:00
pc = thread.ReturnAddressFromOffset(fde.ReturnAddressOffset(curpc))
2014-12-08 23:40:59 +00:00
}
bp, err := thread.Process.Break(pc)
2015-01-14 02:37:10 +00:00
if err != nil {
if err, ok := err.(BreakPointExistsError); !ok {
return err
}
continue
2014-12-08 23:40:59 +00:00
}
bp.Temp = true
2014-12-08 23:40:59 +00:00
}
}
return thread.Continue()
}
2014-12-08 23:40:59 +00:00
func (thread *ThreadContext) SetPC(pc uint64) error {
regs, err := thread.Registers()
if err != nil {
return err
}
return regs.SetPC(thread, pc)
2014-12-08 23:40:59 +00:00
}
// 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")
}
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 {
clearbp := func(bp *BreakPoint) error {
if _, err := thread.Process.Clear(bp.Addr); err != nil {
return err
}
return thread.SetPC(bp.Addr)
}
for _, bp := range thread.Process.HWBreakPoints {
if bp != nil && bp.Temp && bp.Addr == pc {
return clearbp(bp)
}
}
if bp, ok := thread.Process.BreakPoints[pc]; ok && bp.Temp {
return clearbp(bp)
}
return nil
}
func (thread *ThreadContext) curG() (*G, error) {
var g *G
err := thread.CallFn("runtime.getg", func(t *ThreadContext) error {
regs, err := t.Registers()
2014-12-08 23:40:59 +00:00
if err != nil {
return err
}
reader := t.Process.dwarf.Reader()
g, err = parseG(t.Process, regs.SP()+uint64(ptrsize), reader)
return err
})
return g, err
2014-12-08 23:40:59 +00:00
}