delve/pkg/proc/threads.go

106 lines
3.4 KiB
Go
Raw Permalink Normal View History

2015-06-12 19:49:23 +00:00
package proc
2014-12-08 23:40:59 +00:00
import (
"errors"
"github.com/go-delve/delve/pkg/dwarf/op"
2014-12-08 23:40:59 +00:00
)
// Thread represents a thread.
type Thread interface {
Location() (*Location, error)
// Breakpoint will return the breakpoint that this thread is stopped at or
// nil if the thread is not stopped at any breakpoint.
Breakpoint() *BreakpointState
ThreadID() int
// Registers returns the CPU registers of this thread. The contents of the
// variable returned may or may not change to reflect the new CPU status
// when the thread is resumed or the registers are changed by calling
// SetPC/SetSP/etc.
// To insure that the returned variable won't change call the Copy
// method of Registers.
Registers() (Registers, error)
// RestoreRegisters restores saved registers
RestoreRegisters(Registers) error
BinInfo() *BinaryInfo
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
// ProcessMemory returns the process memory.
ProcessMemory() MemoryReadWriter
// SetCurrentBreakpoint updates the current breakpoint of this thread, if adjustPC is true also checks for breakpoints that were just hit (this should only be passed true after a thread resume)
SetCurrentBreakpoint(adjustPC bool) error
// SoftExc returns true if this thread received a software exception during the last resume.
SoftExc() bool
// Common returns the CommonThread structure for this thread
Common() *CommonThread
// SetReg changes the value of the specified register. A minimal
// implementation of this interface can support just setting the PC
// register.
SetReg(uint64, *op.DwarfRegister) error
}
2016-01-10 08:57:52 +00:00
// Location represents the location of a thread.
2015-06-18 03:01:31 +00:00
// Holds information on the current instruction
// address, the source file:line, and the function.
type Location struct {
PC uint64
File string
Line int
Fn *Function
}
// CommonThread contains fields used by this package, common to all
// implementations of the Thread interface.
type CommonThread struct {
CallReturn bool // returnValues are the return values of a call injection
returnValues []*Variable
g *G // cached g for this thread
}
// ReturnValues reads the return values from the function executing on
// this thread using the provided LoadConfig.
func (t *CommonThread) ReturnValues(cfg LoadConfig) []*Variable {
loadValues(t.returnValues, cfg)
return t.returnValues
}
// topframe returns the two topmost frames of g, or thread if g is nil.
func topframe(tgt *Target, g *G, thread Thread) (Stackframe, Stackframe, error) {
var frames []Stackframe
var err error
if g == nil {
frames, err = ThreadStacktrace(tgt, thread, 1)
} else {
frames, err = GoroutineStacktrace(tgt, g, 1, StacktraceReadDefers)
}
2014-12-08 23:40:59 +00:00
if err != nil {
return Stackframe{}, Stackframe{}, err
2014-12-08 23:40:59 +00:00
}
switch len(frames) {
case 0:
return Stackframe{}, Stackframe{}, errors.New("empty stack trace")
case 1:
return frames[0], Stackframe{}, nil
default:
return frames[0], frames[1], nil
}
}
func setPC(thread Thread, newPC uint64) error {
return thread.SetReg(thread.BinInfo().Arch.PCRegNum, op.DwarfRegisterFromUint64(newPC))
}
func setSP(thread Thread, newSP uint64) error {
return thread.SetReg(thread.BinInfo().Arch.SPRegNum, op.DwarfRegisterFromUint64(newSP))
}
func setClosureReg(thread Thread, newClosureReg uint64) error {
return thread.SetReg(thread.BinInfo().Arch.ContextRegNum, op.DwarfRegisterFromUint64(newClosureReg))
}
func setLR(thread Thread, newLR uint64) error {
return thread.SetReg(thread.BinInfo().Arch.LRRegNum, op.DwarfRegisterFromUint64(newLR))
}