1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
package native
import (
"fmt"
"github.com/go-delve/delve/pkg/proc"
)
// Thread represents a single thread in the traced process
// ID represents the thread id or port, Process holds a reference to the
// Process struct that contains info on the process as
// a whole, and Status represents the last result of a `wait` call
// on this thread.
type nativeThread struct {
ID int // Thread ID or mach port
Status *waitStatus // Status returned from last wait call
CurrentBreakpoint proc.BreakpointState // Breakpoint thread is currently stopped at
dbp *nativeProcess
singleStepping bool
os *osSpecificDetails
common proc.CommonThread
}
func (procgrp *processGroup) StepInstruction(threadID int) error {
return procgrp.stepInstruction(procgrp.procForThread(threadID).threads[threadID])
}
// StepInstruction steps a single instruction.
//
// Executes exactly one instruction and then returns.
// If the thread is at a breakpoint, we first clear it,
// execute the instruction, and then replace the breakpoint.
// Otherwise we simply execute the next instruction.
func (procgrp *processGroup) stepInstruction(t *nativeThread) (err error) {
t.singleStepping = true
defer func() {
t.singleStepping = false
}()
if bp := t.CurrentBreakpoint.Breakpoint; bp != nil && bp.WatchType != 0 && t.dbp.Breakpoints().M[bp.Addr] == bp {
err = t.clearHardwareBreakpoint(bp.Addr, bp.WatchType, bp.HWBreakIndex)
if err != nil {
return err
}
defer func() {
err = t.writeHardwareBreakpoint(bp.Addr, bp.WatchType, bp.HWBreakIndex)
}()
}
pc, err := t.PC()
if err != nil {
return err
}
bp, ok := t.dbp.FindBreakpoint(pc, false)
if ok {
// Clear the breakpoint so that we can continue execution.
err = t.clearSoftwareBreakpoint(bp)
if err != nil {
return err
}
// Restore breakpoint now that we have passed it.
defer func() {
err = t.dbp.writeSoftwareBreakpoint(t, bp.Addr)
}()
}
err = procgrp.singleStep(t)
if err != nil {
if _, exited := err.(proc.ErrProcessExited); exited {
return err
}
return fmt.Errorf("step failed: %s", err.Error())
}
return nil
}
// Location returns the threads location, including the file:line
// of the corresponding source code, the function we're in
// and the current instruction address.
func (t *nativeThread) Location() (*proc.Location, error) {
pc, err := t.PC()
if err != nil {
return nil, err
}
f, l, fn := t.dbp.bi.PCToLine(pc)
return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
}
// BinInfo returns information on the binary.
func (t *nativeThread) BinInfo() *proc.BinaryInfo {
return t.dbp.bi
}
// Common returns information common across Process
// implementations.
func (t *nativeThread) Common() *proc.CommonThread {
return &t.common
}
// SetCurrentBreakpoint sets the current breakpoint that this
// thread is stopped at as CurrentBreakpoint on the thread struct.
func (t *nativeThread) SetCurrentBreakpoint(adjustPC bool) error {
t.CurrentBreakpoint.Clear()
var bp *proc.Breakpoint
if t.dbp.Breakpoints().HasHWBreakpoints() {
var err error
bp, err = t.findHardwareBreakpoint()
if err != nil {
return err
}
}
if bp == nil {
pc, err := t.PC()
if err != nil {
return err
}
// If the breakpoint instruction does not change the value
// of PC after being executed we should look for breakpoints
// with bp.Addr == PC and there is no need to call SetPC
// after finding one.
adjustPC = adjustPC && t.BinInfo().Arch.BreakInstrMovesPC()
var ok bool
bp, ok = t.dbp.FindBreakpoint(pc, adjustPC)
if ok {
if adjustPC {
if err = t.setPC(bp.Addr); err != nil {
return err
}
}
}
}
t.CurrentBreakpoint.Breakpoint = bp
return nil
}
// Breakpoint returns the current breakpoint that is active
// on this thread.
func (t *nativeThread) Breakpoint() *proc.BreakpointState {
return &t.CurrentBreakpoint
}
// ThreadID returns the ID of this thread.
func (t *nativeThread) ThreadID() int {
return t.ID
}
// clearSoftwareBreakpoint clears the specified breakpoint.
func (t *nativeThread) clearSoftwareBreakpoint(bp *proc.Breakpoint) error {
if _, err := t.WriteMemory(bp.Addr, bp.OriginalData); err != nil {
return fmt.Errorf("could not clear breakpoint %s", err)
}
return nil
}
// Registers obtains register values from the debugged process.
func (t *nativeThread) Registers() (proc.Registers, error) {
return registers(t)
}
// RestoreRegisters will set the value of the CPU registers to those
// passed in via 'savedRegs'.
func (t *nativeThread) RestoreRegisters(savedRegs proc.Registers) error {
return t.restoreRegisters(savedRegs)
}
// PC returns the current program counter value for this thread.
func (t *nativeThread) PC() (uint64, error) {
regs, err := t.Registers()
if err != nil {
return 0, err
}
return regs.PC(), nil
}
// ProcessMemory returns this thread's process memory.
func (t *nativeThread) ProcessMemory() proc.MemoryReadWriter {
return t.dbp.Memory()
}
|