delve/pkg/proc/interface.go

155 lines
5.0 KiB
Go
Raw Normal View History

package proc
2017-02-08 00:23:47 +00:00
import (
"sync"
"time"
"github.com/go-delve/delve/pkg/elfwriter"
"github.com/go-delve/delve/pkg/proc/internal/ebpf"
)
// ProcessGroup is a group of processes that are resumed at the same time.
type ProcessGroup interface {
ContinueOnce(*ContinueOnceContext) (Thread, StopReason, error)
StepInstruction(int) error
Detach(int, bool) error
Close() error
}
// Process represents the target of the debugger. This
2017-02-08 00:23:47 +00:00
// target could be a system process, core file, etc.
//
// Implementations of Process are not required to be thread safe and users
// of Process should not assume they are.
// There is one exception to this rule: it is safe to call RequestManualStop
// concurrently with ContinueOnce.
type Process interface {
BinInfo() *BinaryInfo
EntryPoint() (uint64, error)
FindThread(threadID int) (Thread, bool)
ThreadList() []Thread
Breakpoints() *BreakpointMap
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
// Memory returns a memory read/writer for this process's memory.
Memory() MemoryReadWriter
}
// ProcessInternal holds a set of methods that need to be implemented by a
// Delve backend. Methods in the Process interface are safe to be called by
// clients of the 'proc' library, while all other methods are only called
// directly within 'proc'.
type ProcessInternal interface {
Process
// Valid returns true if this Process can be used. When it returns false it
// also returns an error describing why the Process is invalid (either
// ErrProcessExited or ErrProcessDetached).
Valid() (bool, error)
// RequestManualStop attempts to stop all the process' threads.
RequestManualStop(cctx *ContinueOnceContext) error
WriteBreakpoint(*Breakpoint) error
EraseBreakpoint(*Breakpoint) error
SupportsBPF() bool
SetUProbe(string, int64, []ebpf.UProbeArgMap) error
GetBufferedTracepoints() []ebpf.RawUProbeParams
// DumpProcessNotes returns ELF core notes describing the process and its threads.
// Implementing this method is optional.
DumpProcessNotes(notes []elfwriter.Note, threadDone func()) (bool, []elfwriter.Note, error)
// MemoryMap returns the memory map of the target process. This method must be implemented if CanDump is true.
MemoryMap() ([]MemoryMapEntry, error)
// StartCallInjection notifies the backend that we are about to inject a function call.
StartCallInjection() (func(), error)
// FollowExec enables (or disables) follow exec mode
FollowExec(bool) error
}
// RecordingManipulation is an interface for manipulating process recordings.
type RecordingManipulation interface {
// Recorded returns true if the current process is a recording and the path
// to the trace directory.
Recorded() (recorded bool, tracedir string)
// ChangeDirection changes execution direction.
ChangeDirection(Direction) error
// GetDirection returns the current direction of execution.
GetDirection() Direction
// When returns current recording position.
When() (string, error)
// Checkpoint sets a checkpoint at the current position.
Checkpoint(where string) (id int, err error)
// Checkpoints returns the list of currently set checkpoint.
Checkpoints() ([]Checkpoint, error)
// ClearCheckpoint removes a checkpoint.
ClearCheckpoint(id int) error
}
// RecordingManipulationInternal is an interface that a Delve backend can
// implement if it is a recording.
type RecordingManipulationInternal interface {
RecordingManipulation
// Restart restarts the recording from the specified position, or from the
// last checkpoint if pos == "".
// If pos starts with 'c' it's a checkpoint ID, otherwise it's an event
// number.
// Returns the new current thread after the restart has completed.
Restart(cctx *ContinueOnceContext, pos string) (Thread, error)
}
// Direction is the direction of execution for the target process.
type Direction int8
const (
// Forward direction executes the target normally.
Forward Direction = 0
// Backward direction executes the target in reverse.
Backward Direction = 1
)
// Checkpoint is a checkpoint
type Checkpoint struct {
ID int
When string
Where string
2017-02-08 00:23:47 +00:00
}
// ContinueOnceContext is an object passed to ContinueOnce that the backend
// can use to communicate with the target layer.
type ContinueOnceContext struct {
ResumeChan chan<- struct{}
StopMu sync.Mutex
// manualStopRequested is set if all the threads in the process were
// signalled to stop as a result of a Halt API call. Used to disambiguate
// why a thread is found to have stopped.
manualStopRequested bool
}
// CheckAndClearManualStopRequest will check for a manual
// stop and then clear that state.
func (cctx *ContinueOnceContext) CheckAndClearManualStopRequest() bool {
cctx.StopMu.Lock()
defer cctx.StopMu.Unlock()
msr := cctx.manualStopRequested
cctx.manualStopRequested = false
return msr
}
func (cctx *ContinueOnceContext) GetManualStopRequested() bool {
cctx.StopMu.Lock()
defer cctx.StopMu.Unlock()
return cctx.manualStopRequested
}
// WaitFor is passed to native.Attach and gdbserver.LLDBAttach to wait for a
// process to start before attaching.
type WaitFor struct {
Name string
Interval, Duration time.Duration
}