pkg/proc: Introduce Target and remove CommonProcess (#1834)
* pkg/proc: Introduce Target * pkg/proc: Remove Common.fncallEnabled Realistically we only block it on recorded backends. * pkg/proc: Move fncallForG to Target * pkg/proc: Remove CommonProcess Remove final bit of functionality stored in CommonProcess and move it to *Target. * pkg/proc: Add SupportsFunctionCall to Target
This commit is contained in:
parent
3f7571ec30
commit
94a20d57da
@ -159,7 +159,6 @@ type Process struct {
|
|||||||
breakpoints proc.BreakpointMap
|
breakpoints proc.BreakpointMap
|
||||||
currentThread *Thread
|
currentThread *Thread
|
||||||
selectedGoroutine *proc.G
|
selectedGoroutine *proc.G
|
||||||
common proc.CommonProcess
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thread represents a thread in the core file being debugged.
|
// Thread represents a thread in the core file being debugged.
|
||||||
@ -200,7 +199,7 @@ var ErrUnrecognizedFormat = errors.New("unrecognized core format")
|
|||||||
// OpenCore will open the core file and return a Process struct.
|
// OpenCore will open the core file and return a Process struct.
|
||||||
// If the DWARF information cannot be found in the binary, Delve will look
|
// If the DWARF information cannot be found in the binary, Delve will look
|
||||||
// for external debug files in the directories passed in.
|
// for external debug files in the directories passed in.
|
||||||
func OpenCore(corePath, exePath string, debugInfoDirs []string) (*Process, error) {
|
func OpenCore(corePath, exePath string, debugInfoDirs []string) (*proc.Target, error) {
|
||||||
var p *Process
|
var p *Process
|
||||||
var err error
|
var err error
|
||||||
for _, openFn := range openFns {
|
for _, openFn := range openFns {
|
||||||
@ -217,7 +216,7 @@ func OpenCore(corePath, exePath string, debugInfoDirs []string) (*Process, error
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return p, nil
|
return proc.NewTarget(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize for core files doesn't do much
|
// initialize for core files doesn't do much
|
||||||
@ -302,8 +301,8 @@ func (t *Thread) Location() (*proc.Location, error) {
|
|||||||
// Breakpoint returns the current breakpoint this thread is stopped at.
|
// Breakpoint returns the current breakpoint this thread is stopped at.
|
||||||
// For core files this always returns an empty BreakpointState struct, as
|
// For core files this always returns an empty BreakpointState struct, as
|
||||||
// there are no breakpoints when debugging core files.
|
// there are no breakpoints when debugging core files.
|
||||||
func (t *Thread) Breakpoint() proc.BreakpointState {
|
func (t *Thread) Breakpoint() *proc.BreakpointState {
|
||||||
return proc.BreakpointState{}
|
return &proc.BreakpointState{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ThreadID returns the ID for this thread.
|
// ThreadID returns the ID for this thread.
|
||||||
@ -434,12 +433,6 @@ func (p *Process) Valid() (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common returns common information across Process
|
|
||||||
// implementations.
|
|
||||||
func (p *Process) Common() *proc.CommonProcess {
|
|
||||||
return &p.common
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pid returns the process ID of this process.
|
// Pid returns the process ID of this process.
|
||||||
func (p *Process) Pid() int {
|
func (p *Process) Pid() int {
|
||||||
return p.pid
|
return p.pid
|
||||||
@ -462,11 +455,7 @@ func (p *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SwitchGoroutine will change the selected and active goroutine.
|
// SwitchGoroutine will change the selected and active goroutine.
|
||||||
func (p *Process) SwitchGoroutine(gid int) error {
|
func (p *Process) SwitchGoroutine(g *proc.G) error {
|
||||||
g, err := proc.FindGoroutine(p, gid)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if g == nil {
|
if g == nil {
|
||||||
// user specified -1 and selectedGoroutine is nil
|
// user specified -1 and selectedGoroutine is nil
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@ -148,7 +148,7 @@ func TestSplicedReader(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func withCoreFile(t *testing.T, name, args string) *Process {
|
func withCoreFile(t *testing.T, name, args string) *proc.Target {
|
||||||
// This is all very fragile and won't work on hosts with non-default core patterns.
|
// This is all very fragile and won't work on hosts with non-default core patterns.
|
||||||
// Might be better to check in the binary and core?
|
// Might be better to check in the binary and core?
|
||||||
tempDir, err := ioutil.TempDir("", "")
|
tempDir, err := ioutil.TempDir("", "")
|
||||||
|
|||||||
@ -90,7 +90,7 @@ type functionCallState struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type callContext struct {
|
type callContext struct {
|
||||||
p Process
|
p *Target
|
||||||
|
|
||||||
// checkEscape is true if the escape check should be performed.
|
// checkEscape is true if the escape check should be performed.
|
||||||
// See service/api.DebuggerCommand.UnsafeCall in service/api/types.go.
|
// See service/api.DebuggerCommand.UnsafeCall in service/api/types.go.
|
||||||
@ -115,6 +115,14 @@ type continueRequest struct {
|
|||||||
ret *Variable
|
ret *Variable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type callInjection struct {
|
||||||
|
// if continueCompleted is not nil it means we are in the process of
|
||||||
|
// executing an injected function call, see comments throughout
|
||||||
|
// pkg/proc/fncall.go for a description of how this works.
|
||||||
|
continueCompleted chan<- *G
|
||||||
|
continueRequest <-chan continueRequest
|
||||||
|
}
|
||||||
|
|
||||||
func (callCtx *callContext) doContinue() *G {
|
func (callCtx *callContext) doContinue() *G {
|
||||||
callCtx.continueRequest <- continueRequest{cont: true}
|
callCtx.continueRequest <- continueRequest{cont: true}
|
||||||
return <-callCtx.continueCompleted
|
return <-callCtx.continueCompleted
|
||||||
@ -130,9 +138,9 @@ func (callCtx *callContext) doReturn(ret *Variable, err error) {
|
|||||||
// EvalExpressionWithCalls is like EvalExpression but allows function calls in 'expr'.
|
// EvalExpressionWithCalls is like EvalExpression but allows function calls in 'expr'.
|
||||||
// Because this can only be done in the current goroutine, unlike
|
// Because this can only be done in the current goroutine, unlike
|
||||||
// EvalExpression, EvalExpressionWithCalls is not a method of EvalScope.
|
// EvalExpression, EvalExpressionWithCalls is not a method of EvalScope.
|
||||||
func EvalExpressionWithCalls(p Process, g *G, expr string, retLoadCfg LoadConfig, checkEscape bool) error {
|
func EvalExpressionWithCalls(t *Target, g *G, expr string, retLoadCfg LoadConfig, checkEscape bool) error {
|
||||||
bi := p.BinInfo()
|
bi := t.BinInfo()
|
||||||
if !p.Common().fncallEnabled {
|
if !t.SupportsFunctionCalls() {
|
||||||
return errFuncCallUnsupportedBackend
|
return errFuncCallUnsupportedBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +152,7 @@ func EvalExpressionWithCalls(p Process, g *G, expr string, retLoadCfg LoadConfig
|
|||||||
return errGoroutineNotRunning
|
return errGoroutineNotRunning
|
||||||
}
|
}
|
||||||
|
|
||||||
if callinj := p.Common().fncallForG[g.ID]; callinj != nil && callinj.continueCompleted != nil {
|
if callinj := t.fncallForG[g.ID]; callinj != nil && callinj.continueCompleted != nil {
|
||||||
return errFuncCallInProgress
|
return errFuncCallInProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,14 +170,14 @@ func EvalExpressionWithCalls(p Process, g *G, expr string, retLoadCfg LoadConfig
|
|||||||
continueCompleted := make(chan *G)
|
continueCompleted := make(chan *G)
|
||||||
|
|
||||||
scope.callCtx = &callContext{
|
scope.callCtx = &callContext{
|
||||||
p: p,
|
p: t,
|
||||||
checkEscape: checkEscape,
|
checkEscape: checkEscape,
|
||||||
retLoadCfg: retLoadCfg,
|
retLoadCfg: retLoadCfg,
|
||||||
continueRequest: continueRequest,
|
continueRequest: continueRequest,
|
||||||
continueCompleted: continueCompleted,
|
continueCompleted: continueCompleted,
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Common().fncallForG[g.ID] = &callInjection{
|
t.fncallForG[g.ID] = &callInjection{
|
||||||
continueCompleted,
|
continueCompleted,
|
||||||
continueRequest,
|
continueRequest,
|
||||||
}
|
}
|
||||||
@ -178,13 +186,13 @@ func EvalExpressionWithCalls(p Process, g *G, expr string, retLoadCfg LoadConfig
|
|||||||
|
|
||||||
contReq, ok := <-continueRequest
|
contReq, ok := <-continueRequest
|
||||||
if contReq.cont {
|
if contReq.cont {
|
||||||
return Continue(p)
|
return Continue(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
return finishEvalExpressionWithCalls(p, g, contReq, ok)
|
return finishEvalExpressionWithCalls(t, g, contReq, ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
func finishEvalExpressionWithCalls(p Process, g *G, contReq continueRequest, ok bool) error {
|
func finishEvalExpressionWithCalls(t *Target, g *G, contReq continueRequest, ok bool) error {
|
||||||
fncallLog("stashing return values for %d in thread=%d\n", g.ID, g.Thread.ThreadID())
|
fncallLog("stashing return values for %d in thread=%d\n", g.ID, g.Thread.ThreadID())
|
||||||
var err error
|
var err error
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -208,8 +216,8 @@ func finishEvalExpressionWithCalls(p Process, g *G, contReq continueRequest, ok
|
|||||||
g.Thread.Common().returnValues = []*Variable{contReq.ret}
|
g.Thread.Common().returnValues = []*Variable{contReq.ret}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(p.Common().fncallForG[g.ID].continueCompleted)
|
close(t.fncallForG[g.ID].continueCompleted)
|
||||||
delete(p.Common().fncallForG, g.ID)
|
delete(t.fncallForG, g.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,7 +242,7 @@ func evalFunctionCall(scope *EvalScope, node *ast.CallExpr) (*Variable, error) {
|
|||||||
|
|
||||||
p := scope.callCtx.p
|
p := scope.callCtx.p
|
||||||
bi := scope.BinInfo
|
bi := scope.BinInfo
|
||||||
if !p.Common().fncallEnabled {
|
if !p.SupportsFunctionCalls() {
|
||||||
return nil, errFuncCallUnsupportedBackend
|
return nil, errFuncCallUnsupportedBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -879,8 +887,8 @@ func isCallInjectionStop(loc *Location) bool {
|
|||||||
// callInjectionProtocol is the function called from Continue to progress
|
// callInjectionProtocol is the function called from Continue to progress
|
||||||
// the injection protocol for all threads.
|
// the injection protocol for all threads.
|
||||||
// Returns true if a call injection terminated
|
// Returns true if a call injection terminated
|
||||||
func callInjectionProtocol(p Process, threads []Thread) (done bool, err error) {
|
func callInjectionProtocol(t *Target, threads []Thread) (done bool, err error) {
|
||||||
if len(p.Common().fncallForG) == 0 {
|
if len(t.fncallForG) == 0 {
|
||||||
// we aren't injecting any calls, no need to check the threads.
|
// we aren't injecting any calls, no need to check the threads.
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@ -897,7 +905,7 @@ func callInjectionProtocol(p Process, threads []Thread) (done bool, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return done, fmt.Errorf("could not determine running goroutine for thread %#x currently executing the function call injection protocol: %v", thread.ThreadID(), err)
|
return done, fmt.Errorf("could not determine running goroutine for thread %#x currently executing the function call injection protocol: %v", thread.ThreadID(), err)
|
||||||
}
|
}
|
||||||
callinj := p.Common().fncallForG[g.ID]
|
callinj := t.fncallForG[g.ID]
|
||||||
if callinj == nil || callinj.continueCompleted == nil {
|
if callinj == nil || callinj.continueCompleted == nil {
|
||||||
return false, fmt.Errorf("could not recover call injection state for goroutine %d", g.ID)
|
return false, fmt.Errorf("could not recover call injection state for goroutine %d", g.ID)
|
||||||
}
|
}
|
||||||
@ -905,7 +913,7 @@ func callInjectionProtocol(p Process, threads []Thread) (done bool, err error) {
|
|||||||
callinj.continueCompleted <- g
|
callinj.continueCompleted <- g
|
||||||
contReq, ok := <-callinj.continueRequest
|
contReq, ok := <-callinj.continueRequest
|
||||||
if !contReq.cont {
|
if !contReq.cont {
|
||||||
err := finishEvalExpressionWithCalls(p, g, contReq, ok)
|
err := finishEvalExpressionWithCalls(t, g, contReq, ok)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return done, err
|
return done, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -126,7 +126,6 @@ type Process struct {
|
|||||||
|
|
||||||
onDetach func() // called after a successful detach
|
onDetach func() // called after a successful detach
|
||||||
|
|
||||||
common proc.CommonProcess
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thread represents an operating system thread.
|
// Thread represents an operating system thread.
|
||||||
@ -184,7 +183,6 @@ func New(process *os.Process) *Process {
|
|||||||
gcmdok: true,
|
gcmdok: true,
|
||||||
threadStopInfo: true,
|
threadStopInfo: true,
|
||||||
process: process,
|
process: process,
|
||||||
common: proc.NewCommonProcess(true),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if process != nil {
|
if process != nil {
|
||||||
@ -320,7 +318,7 @@ func getLdEnvVars() []string {
|
|||||||
// LLDBLaunch starts an instance of lldb-server and connects to it, asking
|
// LLDBLaunch starts an instance of lldb-server and connects to it, asking
|
||||||
// it to launch the specified target program with the specified arguments
|
// it to launch the specified target program with the specified arguments
|
||||||
// (cmd) on the specified directory wd.
|
// (cmd) on the specified directory wd.
|
||||||
func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*Process, error) {
|
func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*proc.Target, error) {
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
return nil, ErrUnsupportedOS
|
return nil, ErrUnsupportedOS
|
||||||
@ -343,7 +341,7 @@ func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string
|
|||||||
|
|
||||||
var listener net.Listener
|
var listener net.Listener
|
||||||
var port string
|
var port string
|
||||||
var proc *exec.Cmd
|
var process *exec.Cmd
|
||||||
if _, err := os.Stat(debugserverExecutable); err == nil {
|
if _, err := os.Stat(debugserverExecutable); err == nil {
|
||||||
listener, err = net.Listen("tcp", "127.0.0.1:0")
|
listener, err = net.Listen("tcp", "127.0.0.1:0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -363,7 +361,7 @@ func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string
|
|||||||
|
|
||||||
isDebugserver = true
|
isDebugserver = true
|
||||||
|
|
||||||
proc = exec.Command(debugserverExecutable, args...)
|
process = exec.Command(debugserverExecutable, args...)
|
||||||
} else {
|
} else {
|
||||||
if _, err := exec.LookPath("lldb-server"); err != nil {
|
if _, err := exec.LookPath("lldb-server"); err != nil {
|
||||||
return nil, &ErrBackendUnavailable{}
|
return nil, &ErrBackendUnavailable{}
|
||||||
@ -374,29 +372,29 @@ func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string
|
|||||||
args = append(args, port, "--")
|
args = append(args, port, "--")
|
||||||
args = append(args, cmd...)
|
args = append(args, cmd...)
|
||||||
|
|
||||||
proc = exec.Command("lldb-server", args...)
|
process = exec.Command("lldb-server", args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if logflags.LLDBServerOutput() || logflags.GdbWire() || foreground {
|
if logflags.LLDBServerOutput() || logflags.GdbWire() || foreground {
|
||||||
proc.Stdout = os.Stdout
|
process.Stdout = os.Stdout
|
||||||
proc.Stderr = os.Stderr
|
process.Stderr = os.Stderr
|
||||||
}
|
}
|
||||||
if foreground {
|
if foreground {
|
||||||
foregroundSignalsIgnore()
|
foregroundSignalsIgnore()
|
||||||
proc.Stdin = os.Stdin
|
process.Stdin = os.Stdin
|
||||||
}
|
}
|
||||||
if wd != "" {
|
if wd != "" {
|
||||||
proc.Dir = wd
|
process.Dir = wd
|
||||||
}
|
}
|
||||||
|
|
||||||
proc.SysProcAttr = sysProcAttr(foreground)
|
process.SysProcAttr = sysProcAttr(foreground)
|
||||||
|
|
||||||
err := proc.Start()
|
err := process.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
p := New(proc.Process)
|
p := New(process.Process)
|
||||||
p.conn.isDebugserver = isDebugserver
|
p.conn.isDebugserver = isDebugserver
|
||||||
|
|
||||||
if listener != nil {
|
if listener != nil {
|
||||||
@ -407,7 +405,7 @@ func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return p, nil
|
return proc.NewTarget(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LLDBAttach starts an instance of lldb-server and connects to it, asking
|
// LLDBAttach starts an instance of lldb-server and connects to it, asking
|
||||||
@ -415,7 +413,7 @@ func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string
|
|||||||
// Path is path to the target's executable, path only needs to be specified
|
// Path is path to the target's executable, path only needs to be specified
|
||||||
// for some stubs that do not provide an automated way of determining it
|
// for some stubs that do not provide an automated way of determining it
|
||||||
// (for example debugserver).
|
// (for example debugserver).
|
||||||
func LLDBAttach(pid int, path string, debugInfoDirs []string) (*Process, error) {
|
func LLDBAttach(pid int, path string, debugInfoDirs []string) (*proc.Target, error) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
return nil, ErrUnsupportedOS
|
return nil, ErrUnsupportedOS
|
||||||
}
|
}
|
||||||
@ -459,7 +457,7 @@ func LLDBAttach(pid int, path string, debugInfoDirs []string) (*Process, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return p, nil
|
return proc.NewTarget(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EntryPoint will return the process entry point address, useful for
|
// EntryPoint will return the process entry point address, useful for
|
||||||
@ -602,11 +600,6 @@ func (p *Process) CurrentThread() proc.Thread {
|
|||||||
return p.currentThread
|
return p.currentThread
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common returns common information across Process implementations.
|
|
||||||
func (p *Process) Common() *proc.CommonProcess {
|
|
||||||
return &p.common
|
|
||||||
}
|
|
||||||
|
|
||||||
// SelectedGoroutine returns the current actuve selected goroutine.
|
// SelectedGoroutine returns the current actuve selected goroutine.
|
||||||
func (p *Process) SelectedGoroutine() *proc.G {
|
func (p *Process) SelectedGoroutine() *proc.G {
|
||||||
return p.selectedGoroutine
|
return p.selectedGoroutine
|
||||||
@ -645,7 +638,6 @@ func (p *Process) ContinueOnce() (proc.Thread, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.common.ClearAllGCache()
|
|
||||||
for _, th := range p.threads {
|
for _, th := range p.threads {
|
||||||
th.clearBreakpointState()
|
th.clearBreakpointState()
|
||||||
}
|
}
|
||||||
@ -751,37 +743,6 @@ func (p *Process) SetSelectedGoroutine(g *proc.G) {
|
|||||||
p.selectedGoroutine = g
|
p.selectedGoroutine = g
|
||||||
}
|
}
|
||||||
|
|
||||||
// StepInstruction will step exactly one CPU instruction.
|
|
||||||
func (p *Process) StepInstruction() error {
|
|
||||||
thread := p.currentThread
|
|
||||||
if p.selectedGoroutine != nil {
|
|
||||||
if p.selectedGoroutine.Thread == nil {
|
|
||||||
if _, err := p.SetBreakpoint(p.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(p.selectedGoroutine)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return proc.Continue(p)
|
|
||||||
}
|
|
||||||
thread = p.selectedGoroutine.Thread.(*Thread)
|
|
||||||
}
|
|
||||||
p.common.ClearAllGCache()
|
|
||||||
if p.exited {
|
|
||||||
return &proc.ErrProcessExited{Pid: p.conn.pid}
|
|
||||||
}
|
|
||||||
thread.clearBreakpointState()
|
|
||||||
err := thread.StepInstruction()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = thread.SetCurrentBreakpoint(true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if g, _ := proc.GetG(thread); g != nil {
|
|
||||||
p.selectedGoroutine = g
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SwitchThread will change the internal selected thread.
|
// SwitchThread will change the internal selected thread.
|
||||||
func (p *Process) SwitchThread(tid int) error {
|
func (p *Process) SwitchThread(tid int) error {
|
||||||
if p.exited {
|
if p.exited {
|
||||||
@ -796,13 +757,8 @@ func (p *Process) SwitchThread(tid int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SwitchGoroutine will change the internal selected goroutine.
|
// SwitchGoroutine will change the internal selected goroutine.
|
||||||
func (p *Process) SwitchGoroutine(gid int) error {
|
func (p *Process) SwitchGoroutine(g *proc.G) error {
|
||||||
g, err := proc.FindGoroutine(p, gid)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if g == nil {
|
if g == nil {
|
||||||
// user specified -1 and selectedGoroutine is nil
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if g.Thread != nil {
|
if g.Thread != nil {
|
||||||
@ -885,7 +841,6 @@ func (p *Process) Restart(pos string) error {
|
|||||||
|
|
||||||
p.exited = false
|
p.exited = false
|
||||||
|
|
||||||
p.common.ClearAllGCache()
|
|
||||||
for _, th := range p.threads {
|
for _, th := range p.threads {
|
||||||
th.clearBreakpointState()
|
th.clearBreakpointState()
|
||||||
}
|
}
|
||||||
@ -1243,8 +1198,8 @@ func (t *Thread) Location() (*proc.Location, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Breakpoint returns the current active breakpoint for this thread.
|
// Breakpoint returns the current active breakpoint for this thread.
|
||||||
func (t *Thread) Breakpoint() proc.BreakpointState {
|
func (t *Thread) Breakpoint() *proc.BreakpointState {
|
||||||
return t.CurrentBreakpoint
|
return &t.CurrentBreakpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
// ThreadID returns this threads ID.
|
// ThreadID returns this threads ID.
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/go-delve/delve/pkg/proc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Record uses rr to record the execution of the specified program and
|
// Record uses rr to record the execution of the specified program and
|
||||||
@ -54,7 +56,7 @@ func Record(cmd []string, wd string, quiet bool) (tracedir string, err error) {
|
|||||||
|
|
||||||
// Replay starts an instance of rr in replay mode, with the specified trace
|
// Replay starts an instance of rr in replay mode, with the specified trace
|
||||||
// directory, and connects to it.
|
// directory, and connects to it.
|
||||||
func Replay(tracedir string, quiet, deleteOnDetach bool, debugInfoDirs []string) (*Process, error) {
|
func Replay(tracedir string, quiet, deleteOnDetach bool, debugInfoDirs []string) (*proc.Target, error) {
|
||||||
if err := checkRRAvailabe(); err != nil {
|
if err := checkRRAvailabe(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -94,7 +96,7 @@ func Replay(tracedir string, quiet, deleteOnDetach bool, debugInfoDirs []string)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return p, nil
|
return proc.NewTarget(p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrPerfEventParanoid is the error returned by Reply and Record if
|
// ErrPerfEventParanoid is the error returned by Reply and Record if
|
||||||
@ -263,13 +265,13 @@ func splitQuotedFields(in string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RecordAndReplay acts like calling Record and then Replay.
|
// RecordAndReplay acts like calling Record and then Replay.
|
||||||
func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string) (p *Process, tracedir string, err error) {
|
func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string) (*proc.Target, string, error) {
|
||||||
tracedir, err = Record(cmd, wd, quiet)
|
tracedir, err := Record(cmd, wd, quiet)
|
||||||
if tracedir == "" {
|
if tracedir == "" {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
p, err = Replay(tracedir, quiet, true, debugInfoDirs)
|
t, err := Replay(tracedir, quiet, true, debugInfoDirs)
|
||||||
return p, tracedir, err
|
return t, tracedir, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// safeRemoveAll removes dir and its contents but only as long as dir does
|
// safeRemoveAll removes dir and its contents but only as long as dir does
|
||||||
|
|||||||
@ -23,7 +23,7 @@ func TestMain(m *testing.M) {
|
|||||||
os.Exit(protest.RunTestsWithFixtures(m))
|
os.Exit(protest.RunTestsWithFixtures(m))
|
||||||
}
|
}
|
||||||
|
|
||||||
func withTestRecording(name string, t testing.TB, fn func(p *gdbserial.Process, fixture protest.Fixture)) {
|
func withTestRecording(name string, t testing.TB, fn func(t *proc.Target, fixture protest.Fixture)) {
|
||||||
fixture := protest.BuildFixture(name, 0)
|
fixture := protest.BuildFixture(name, 0)
|
||||||
protest.MustHaveRecordingAllowed(t)
|
protest.MustHaveRecordingAllowed(t)
|
||||||
if path, _ := exec.LookPath("rr"); path == "" {
|
if path, _ := exec.LookPath("rr"); path == "" {
|
||||||
@ -36,9 +36,7 @@ func withTestRecording(name string, t testing.TB, fn func(p *gdbserial.Process,
|
|||||||
}
|
}
|
||||||
t.Logf("replaying %q", tracedir)
|
t.Logf("replaying %q", tracedir)
|
||||||
|
|
||||||
defer func() {
|
defer p.Detach(true)
|
||||||
p.Detach(true)
|
|
||||||
}()
|
|
||||||
|
|
||||||
fn(p, fixture)
|
fn(p, fixture)
|
||||||
}
|
}
|
||||||
@ -71,7 +69,7 @@ func setFunctionBreakpoint(p proc.Process, t *testing.T, fname string) *proc.Bre
|
|||||||
|
|
||||||
func TestRestartAfterExit(t *testing.T) {
|
func TestRestartAfterExit(t *testing.T) {
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestRecording("testnextprog", t, func(p *gdbserial.Process, fixture protest.Fixture) {
|
withTestRecording("testnextprog", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
setFunctionBreakpoint(p, t, "main.main")
|
setFunctionBreakpoint(p, t, "main.main")
|
||||||
assertNoError(proc.Continue(p), t, "Continue")
|
assertNoError(proc.Continue(p), t, "Continue")
|
||||||
loc, err := p.CurrentThread().Location()
|
loc, err := p.CurrentThread().Location()
|
||||||
@ -98,7 +96,7 @@ func TestRestartAfterExit(t *testing.T) {
|
|||||||
|
|
||||||
func TestRestartDuringStop(t *testing.T) {
|
func TestRestartDuringStop(t *testing.T) {
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestRecording("testnextprog", t, func(p *gdbserial.Process, fixture protest.Fixture) {
|
withTestRecording("testnextprog", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
setFunctionBreakpoint(p, t, "main.main")
|
setFunctionBreakpoint(p, t, "main.main")
|
||||||
assertNoError(proc.Continue(p), t, "Continue")
|
assertNoError(proc.Continue(p), t, "Continue")
|
||||||
loc, err := p.CurrentThread().Location()
|
loc, err := p.CurrentThread().Location()
|
||||||
@ -139,7 +137,7 @@ func setFileBreakpoint(p proc.Process, t *testing.T, fixture protest.Fixture, li
|
|||||||
|
|
||||||
func TestReverseBreakpointCounts(t *testing.T) {
|
func TestReverseBreakpointCounts(t *testing.T) {
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestRecording("bpcountstest", t, func(p *gdbserial.Process, fixture protest.Fixture) {
|
withTestRecording("bpcountstest", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
endbp := setFileBreakpoint(p, t, fixture, 28)
|
endbp := setFileBreakpoint(p, t, fixture, 28)
|
||||||
assertNoError(proc.Continue(p), t, "Continue()")
|
assertNoError(proc.Continue(p), t, "Continue()")
|
||||||
loc, _ := p.CurrentThread().Location()
|
loc, _ := p.CurrentThread().Location()
|
||||||
@ -183,7 +181,7 @@ func TestReverseBreakpointCounts(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPosition(p *gdbserial.Process, t *testing.T) (when string, loc *proc.Location) {
|
func getPosition(p *proc.Target, t *testing.T) (when string, loc *proc.Location) {
|
||||||
var err error
|
var err error
|
||||||
when, err = p.When()
|
when, err = p.When()
|
||||||
assertNoError(err, t, "When")
|
assertNoError(err, t, "When")
|
||||||
@ -194,7 +192,7 @@ func getPosition(p *gdbserial.Process, t *testing.T) (when string, loc *proc.Loc
|
|||||||
|
|
||||||
func TestCheckpoints(t *testing.T) {
|
func TestCheckpoints(t *testing.T) {
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestRecording("continuetestprog", t, func(p *gdbserial.Process, fixture protest.Fixture) {
|
withTestRecording("continuetestprog", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
// Continues until start of main.main, record output of 'when'
|
// Continues until start of main.main, record output of 'when'
|
||||||
bp := setFunctionBreakpoint(p, t, "main.main")
|
bp := setFunctionBreakpoint(p, t, "main.main")
|
||||||
assertNoError(proc.Continue(p), t, "Continue")
|
assertNoError(proc.Continue(p), t, "Continue")
|
||||||
@ -278,7 +276,7 @@ func TestCheckpoints(t *testing.T) {
|
|||||||
func TestIssue1376(t *testing.T) {
|
func TestIssue1376(t *testing.T) {
|
||||||
// Backward Continue should terminate when it encounters the start of the process.
|
// Backward Continue should terminate when it encounters the start of the process.
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestRecording("continuetestprog", t, func(p *gdbserial.Process, fixture protest.Fixture) {
|
withTestRecording("continuetestprog", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
bp := setFunctionBreakpoint(p, t, "main.main")
|
bp := setFunctionBreakpoint(p, t, "main.main")
|
||||||
assertNoError(proc.Continue(p), t, "Continue (forward)")
|
assertNoError(proc.Continue(p), t, "Continue (forward)")
|
||||||
_, err := p.ClearBreakpoint(bp.Addr)
|
_, err := p.ClearBreakpoint(bp.Addr)
|
||||||
|
|||||||
60
pkg/proc/goroutine_cache.go
Normal file
60
pkg/proc/goroutine_cache.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package proc
|
||||||
|
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
type goroutineCache struct {
|
||||||
|
partialGCache map[int]*G
|
||||||
|
allGCache []*G
|
||||||
|
|
||||||
|
allgentryAddr, allglenAddr uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gcache *goroutineCache) init(bi *BinaryInfo) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
exeimage := bi.Images[0]
|
||||||
|
rdr := exeimage.DwarfReader()
|
||||||
|
|
||||||
|
gcache.allglenAddr, _ = rdr.AddrFor("runtime.allglen", exeimage.StaticBase)
|
||||||
|
|
||||||
|
rdr.Seek(0)
|
||||||
|
gcache.allgentryAddr, err = rdr.AddrFor("runtime.allgs", exeimage.StaticBase)
|
||||||
|
if err != nil {
|
||||||
|
// try old name (pre Go 1.6)
|
||||||
|
gcache.allgentryAddr, _ = rdr.AddrFor("runtime.allg", exeimage.StaticBase)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gcache *goroutineCache) getRuntimeAllg(bi *BinaryInfo, mem MemoryReadWriter) (uint64, uint64, error) {
|
||||||
|
if gcache.allglenAddr == 0 || gcache.allgentryAddr == 0 {
|
||||||
|
return 0, 0, ErrNoRuntimeAllG
|
||||||
|
}
|
||||||
|
allglenBytes := make([]byte, 8)
|
||||||
|
_, err := mem.ReadMemory(allglenBytes, uintptr(gcache.allglenAddr))
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
allglen := binary.LittleEndian.Uint64(allglenBytes)
|
||||||
|
|
||||||
|
faddr := make([]byte, bi.Arch.PtrSize())
|
||||||
|
_, err = mem.ReadMemory(faddr, uintptr(gcache.allgentryAddr))
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
allgptr := binary.LittleEndian.Uint64(faddr)
|
||||||
|
|
||||||
|
return allgptr, allglen, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gcache *goroutineCache) addGoroutine(g *G) {
|
||||||
|
if gcache.partialGCache == nil {
|
||||||
|
gcache.partialGCache = make(map[int]*G)
|
||||||
|
}
|
||||||
|
gcache.partialGCache[g.ID] = g
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear clears the cached contents of the cache for runtime.allgs.
|
||||||
|
func (gcache *goroutineCache) Clear() {
|
||||||
|
gcache.partialGCache = nil
|
||||||
|
gcache.allGCache = nil
|
||||||
|
}
|
||||||
@ -69,8 +69,6 @@ type Info interface {
|
|||||||
Valid() (bool, error)
|
Valid() (bool, error)
|
||||||
BinInfo() *BinaryInfo
|
BinInfo() *BinaryInfo
|
||||||
EntryPoint() (uint64, error)
|
EntryPoint() (uint64, error)
|
||||||
// Common returns a struct with fields common to all backends
|
|
||||||
Common() *CommonProcess
|
|
||||||
|
|
||||||
ThreadInfo
|
ThreadInfo
|
||||||
GoroutineInfo
|
GoroutineInfo
|
||||||
@ -93,9 +91,8 @@ type GoroutineInfo interface {
|
|||||||
// ProcessManipulation is an interface for changing the execution state of a process.
|
// ProcessManipulation is an interface for changing the execution state of a process.
|
||||||
type ProcessManipulation interface {
|
type ProcessManipulation interface {
|
||||||
ContinueOnce() (trapthread Thread, err error)
|
ContinueOnce() (trapthread Thread, err error)
|
||||||
StepInstruction() error
|
|
||||||
SwitchThread(int) error
|
SwitchThread(int) error
|
||||||
SwitchGoroutine(int) error
|
SwitchGoroutine(*G) error
|
||||||
RequestManualStop() error
|
RequestManualStop() error
|
||||||
// CheckAndClearManualStopRequest returns true the first time it's called
|
// CheckAndClearManualStopRequest returns true the first time it's called
|
||||||
// after a call to RequestManualStop.
|
// after a call to RequestManualStop.
|
||||||
@ -110,34 +107,3 @@ type BreakpointManipulation interface {
|
|||||||
ClearBreakpoint(addr uint64) (*Breakpoint, error)
|
ClearBreakpoint(addr uint64) (*Breakpoint, error)
|
||||||
ClearInternalBreakpoints() error
|
ClearInternalBreakpoints() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommonProcess contains fields used by this package, common to all
|
|
||||||
// implementations of the Process interface.
|
|
||||||
type CommonProcess struct {
|
|
||||||
goroutineCache
|
|
||||||
|
|
||||||
fncallEnabled bool
|
|
||||||
|
|
||||||
fncallForG map[int]*callInjection
|
|
||||||
}
|
|
||||||
|
|
||||||
type goroutineCache struct {
|
|
||||||
partialGCache map[int]*G
|
|
||||||
allGCache []*G
|
|
||||||
|
|
||||||
allgentryAddr, allglenAddr uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
type callInjection struct {
|
|
||||||
// if continueCompleted is not nil it means we are in the process of
|
|
||||||
// executing an injected function call, see comments throughout
|
|
||||||
// pkg/proc/fncall.go for a description of how this works.
|
|
||||||
continueCompleted chan<- *G
|
|
||||||
continueRequest <-chan continueRequest
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCommonProcess returns a struct with fields common across
|
|
||||||
// all process implementations.
|
|
||||||
func NewCommonProcess(fncallEnabled bool) CommonProcess {
|
|
||||||
return CommonProcess{fncallEnabled: fncallEnabled, fncallForG: make(map[int]*callInjection)}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -12,12 +12,12 @@ import (
|
|||||||
var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")
|
var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")
|
||||||
|
|
||||||
// Launch returns ErrNativeBackendDisabled.
|
// Launch returns ErrNativeBackendDisabled.
|
||||||
func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, error) {
|
func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target, error) {
|
||||||
return nil, ErrNativeBackendDisabled
|
return nil, ErrNativeBackendDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach returns ErrNativeBackendDisabled.
|
// Attach returns ErrNativeBackendDisabled.
|
||||||
func Attach(pid int, _ []string) (*Process, error) {
|
func Attach(pid int, _ []string) (*proc.Target, error) {
|
||||||
return nil, ErrNativeBackendDisabled
|
return nil, ErrNativeBackendDisabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,6 @@ type Process struct {
|
|||||||
// Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
|
// Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
|
||||||
selectedGoroutine *proc.G
|
selectedGoroutine *proc.G
|
||||||
|
|
||||||
common proc.CommonProcess
|
|
||||||
os *OSProcessDetails
|
os *OSProcessDetails
|
||||||
firstStart bool
|
firstStart bool
|
||||||
stopMu sync.Mutex
|
stopMu sync.Mutex
|
||||||
@ -247,7 +246,6 @@ func (dbp *Process) ContinueOnce() (proc.Thread, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dbp.common.ClearAllGCache()
|
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.threads {
|
||||||
th.CurrentBreakpoint.Clear()
|
th.CurrentBreakpoint.Clear()
|
||||||
}
|
}
|
||||||
@ -267,41 +265,6 @@ func (dbp *Process) ContinueOnce() (proc.Thread, error) {
|
|||||||
return trapthread, err
|
return trapthread, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// StepInstruction will continue the current thread for exactly
|
|
||||||
// one instruction. This method affects only the thread
|
|
||||||
// associated with the selected goroutine. All other
|
|
||||||
// threads will remain stopped.
|
|
||||||
func (dbp *Process) StepInstruction() (err error) {
|
|
||||||
thread := dbp.currentThread
|
|
||||||
if dbp.selectedGoroutine != nil {
|
|
||||||
if dbp.selectedGoroutine.Thread == nil {
|
|
||||||
// Step called on parked goroutine
|
|
||||||
if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(dbp.selectedGoroutine)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return proc.Continue(dbp)
|
|
||||||
}
|
|
||||||
thread = dbp.selectedGoroutine.Thread.(*Thread)
|
|
||||||
}
|
|
||||||
dbp.common.ClearAllGCache()
|
|
||||||
if dbp.exited {
|
|
||||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
|
||||||
}
|
|
||||||
thread.CurrentBreakpoint.Clear()
|
|
||||||
err = thread.StepInstruction()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = thread.SetCurrentBreakpoint(true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if g, _ := proc.GetG(thread); g != nil {
|
|
||||||
dbp.selectedGoroutine = g
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SwitchThread changes from current thread to the thread specified by `tid`.
|
// SwitchThread changes from current thread to the thread specified by `tid`.
|
||||||
func (dbp *Process) SwitchThread(tid int) error {
|
func (dbp *Process) SwitchThread(tid int) error {
|
||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
@ -317,14 +280,10 @@ func (dbp *Process) SwitchThread(tid int) error {
|
|||||||
|
|
||||||
// SwitchGoroutine changes from current thread to the thread
|
// SwitchGoroutine changes from current thread to the thread
|
||||||
// running the specified goroutine.
|
// running the specified goroutine.
|
||||||
func (dbp *Process) SwitchGoroutine(gid int) error {
|
func (dbp *Process) SwitchGoroutine(g *proc.G) error {
|
||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||||
}
|
}
|
||||||
g, err := proc.FindGoroutine(dbp, gid)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if g == nil {
|
if g == nil {
|
||||||
// user specified -1 and selectedGoroutine is nil
|
// user specified -1 and selectedGoroutine is nil
|
||||||
return nil
|
return nil
|
||||||
@ -414,9 +373,3 @@ func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
|
|||||||
_, err := thread.WriteMemory(uintptr(addr), dbp.bi.Arch.BreakpointInstruction())
|
_, err := thread.WriteMemory(uintptr(addr), dbp.bi.Arch.BreakpointInstruction())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common returns common information across Process
|
|
||||||
// implementations
|
|
||||||
func (dbp *Process) Common() *proc.CommonProcess {
|
|
||||||
return &dbp.common
|
|
||||||
}
|
|
||||||
|
|||||||
@ -37,7 +37,7 @@ type OSProcessDetails struct {
|
|||||||
// custom fork/exec process in order to take advantage of
|
// custom fork/exec process in order to take advantage of
|
||||||
// PT_SIGEXC on Darwin which will turn Unix signals into
|
// PT_SIGEXC on Darwin which will turn Unix signals into
|
||||||
// Mach exceptions.
|
// Mach exceptions.
|
||||||
func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, error) {
|
func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target, error) {
|
||||||
// check that the argument to Launch is an executable file
|
// check that the argument to Launch is an executable file
|
||||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
||||||
return nil, proc.ErrNotExecutable
|
return nil, proc.ErrNotExecutable
|
||||||
@ -104,7 +104,6 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, err
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dbp.common.ClearAllGCache()
|
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.threads {
|
||||||
th.CurrentBreakpoint.Clear()
|
th.CurrentBreakpoint.Clear()
|
||||||
}
|
}
|
||||||
@ -127,11 +126,11 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, err
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return dbp, err
|
return proc.NewTarget(dbp), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach to an existing process with the given PID.
|
// Attach to an existing process with the given PID.
|
||||||
func Attach(pid int, _ []string) (*Process, error) {
|
func Attach(pid int, _ []string) (*proc.Target, error) {
|
||||||
dbp := New(pid)
|
dbp := New(pid)
|
||||||
|
|
||||||
kret := C.acquire_mach_task(C.int(pid),
|
kret := C.acquire_mach_task(C.int(pid),
|
||||||
@ -159,7 +158,7 @@ func Attach(pid int, _ []string) (*Process, error) {
|
|||||||
dbp.Detach(false)
|
dbp.Detach(false)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return dbp, nil
|
return proc.NewTarget(dbp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kill kills the process.
|
// Kill kills the process.
|
||||||
|
|||||||
@ -43,7 +43,7 @@ type OSProcessDetails struct {
|
|||||||
// to be supplied to that process. `wd` is working directory of the program.
|
// to be supplied to that process. `wd` is working directory of the program.
|
||||||
// If the DWARF information cannot be found in the binary, Delve will look
|
// If the DWARF information cannot be found in the binary, Delve will look
|
||||||
// for external debug files in the directories passed in.
|
// for external debug files in the directories passed in.
|
||||||
func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*Process, error) {
|
func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*proc.Target, error) {
|
||||||
var (
|
var (
|
||||||
process *exec.Cmd
|
process *exec.Cmd
|
||||||
err error
|
err error
|
||||||
@ -60,7 +60,6 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbp := New(0)
|
dbp := New(0)
|
||||||
dbp.common = proc.NewCommonProcess(true)
|
|
||||||
dbp.execPtraceFunc(func() {
|
dbp.execPtraceFunc(func() {
|
||||||
process = exec.Command(cmd[0])
|
process = exec.Command(cmd[0])
|
||||||
process.Args = cmd
|
process.Args = cmd
|
||||||
@ -88,15 +87,14 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*
|
|||||||
if err = dbp.initialize(cmd[0], debugInfoDirs); err != nil {
|
if err = dbp.initialize(cmd[0], debugInfoDirs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return dbp, nil
|
return proc.NewTarget(dbp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach to an existing process with the given PID. Once attached, if
|
// Attach to an existing process with the given PID. Once attached, if
|
||||||
// the DWARF information cannot be found in the binary, Delve will look
|
// the DWARF information cannot be found in the binary, Delve will look
|
||||||
// for external debug files in the directories passed in.
|
// for external debug files in the directories passed in.
|
||||||
func Attach(pid int, debugInfoDirs []string) (*Process, error) {
|
func Attach(pid int, debugInfoDirs []string) (*proc.Target, error) {
|
||||||
dbp := New(pid)
|
dbp := New(pid)
|
||||||
dbp.common = proc.NewCommonProcess(true)
|
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
|
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
|
||||||
@ -113,7 +111,7 @@ func Attach(pid int, debugInfoDirs []string) (*Process, error) {
|
|||||||
dbp.Detach(false)
|
dbp.Detach(false)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return dbp, nil
|
return proc.NewTarget(dbp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initialize(dbp *Process) error {
|
func initialize(dbp *Process) error {
|
||||||
|
|||||||
@ -48,7 +48,7 @@ type OSProcessDetails struct {
|
|||||||
// to be supplied to that process. `wd` is working directory of the program.
|
// to be supplied to that process. `wd` is working directory of the program.
|
||||||
// If the DWARF information cannot be found in the binary, Delve will look
|
// If the DWARF information cannot be found in the binary, Delve will look
|
||||||
// for external debug files in the directories passed in.
|
// for external debug files in the directories passed in.
|
||||||
func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*Process, error) {
|
func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*proc.Target, error) {
|
||||||
var (
|
var (
|
||||||
process *exec.Cmd
|
process *exec.Cmd
|
||||||
err error
|
err error
|
||||||
@ -65,7 +65,6 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbp := New(0)
|
dbp := New(0)
|
||||||
dbp.common = proc.NewCommonProcess(true)
|
|
||||||
dbp.execPtraceFunc(func() {
|
dbp.execPtraceFunc(func() {
|
||||||
process = exec.Command(cmd[0])
|
process = exec.Command(cmd[0])
|
||||||
process.Args = cmd
|
process.Args = cmd
|
||||||
@ -93,15 +92,14 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*
|
|||||||
if err = dbp.initialize(cmd[0], debugInfoDirs); err != nil {
|
if err = dbp.initialize(cmd[0], debugInfoDirs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return dbp, nil
|
return proc.NewTarget(dbp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach to an existing process with the given PID. Once attached, if
|
// Attach to an existing process with the given PID. Once attached, if
|
||||||
// the DWARF information cannot be found in the binary, Delve will look
|
// the DWARF information cannot be found in the binary, Delve will look
|
||||||
// for external debug files in the directories passed in.
|
// for external debug files in the directories passed in.
|
||||||
func Attach(pid int, debugInfoDirs []string) (*Process, error) {
|
func Attach(pid int, debugInfoDirs []string) (*proc.Target, error) {
|
||||||
dbp := New(pid)
|
dbp := New(pid)
|
||||||
dbp.common = proc.NewCommonProcess(true)
|
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
|
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
|
||||||
@ -125,7 +123,7 @@ func Attach(pid int, debugInfoDirs []string) (*Process, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return dbp, nil
|
return proc.NewTarget(dbp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initialize(dbp *Process) error {
|
func initialize(dbp *Process) error {
|
||||||
|
|||||||
@ -36,7 +36,7 @@ func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Launch creates and begins debugging a new process.
|
// Launch creates and begins debugging a new process.
|
||||||
func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, error) {
|
func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target, error) {
|
||||||
argv0Go, err := filepath.Abs(cmd[0])
|
argv0Go, err := filepath.Abs(cmd[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -57,7 +57,6 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, err
|
|||||||
|
|
||||||
var p *os.Process
|
var p *os.Process
|
||||||
dbp := New(0)
|
dbp := New(0)
|
||||||
dbp.common = proc.NewCommonProcess(true)
|
|
||||||
dbp.execPtraceFunc(func() {
|
dbp.execPtraceFunc(func() {
|
||||||
attr := &os.ProcAttr{
|
attr := &os.ProcAttr{
|
||||||
Dir: wd,
|
Dir: wd,
|
||||||
@ -80,7 +79,7 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, err
|
|||||||
dbp.Detach(true)
|
dbp.Detach(true)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return dbp, nil
|
return proc.NewTarget(dbp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func initialize(dbp *Process) error {
|
func initialize(dbp *Process) error {
|
||||||
@ -151,7 +150,7 @@ func findExePath(pid int) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attach to an existing process with the given PID.
|
// Attach to an existing process with the given PID.
|
||||||
func Attach(pid int, _ []string) (*Process, error) {
|
func Attach(pid int, _ []string) (*proc.Target, error) {
|
||||||
dbp := New(pid)
|
dbp := New(pid)
|
||||||
var err error
|
var err error
|
||||||
dbp.execPtraceFunc(func() {
|
dbp.execPtraceFunc(func() {
|
||||||
@ -169,7 +168,7 @@ func Attach(pid int, _ []string) (*Process, error) {
|
|||||||
dbp.Detach(true)
|
dbp.Detach(true)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return dbp, nil
|
return proc.NewTarget(dbp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// kill kills the process.
|
// kill kills the process.
|
||||||
|
|||||||
@ -145,8 +145,8 @@ func (t *Thread) SetCurrentBreakpoint(adjustPC bool) error {
|
|||||||
|
|
||||||
// Breakpoint returns the current breakpoint that is active
|
// Breakpoint returns the current breakpoint that is active
|
||||||
// on this thread.
|
// on this thread.
|
||||||
func (t *Thread) Breakpoint() proc.BreakpointState {
|
func (t *Thread) Breakpoint() *proc.BreakpointState {
|
||||||
return t.CurrentBreakpoint
|
return &t.CurrentBreakpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
// ThreadID returns the ID of this thread.
|
// ThreadID returns the ID of this thread.
|
||||||
|
|||||||
121
pkg/proc/proc.go
121
pkg/proc/proc.go
@ -2,7 +2,6 @@ package proc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
@ -75,8 +74,6 @@ func PostInitializationSetup(p Process, path string, debugInfoDirs []string, wri
|
|||||||
createUnrecoveredPanicBreakpoint(p, writeBreakpoint)
|
createUnrecoveredPanicBreakpoint(p, writeBreakpoint)
|
||||||
createFatalThrowBreakpoint(p, writeBreakpoint)
|
createFatalThrowBreakpoint(p, writeBreakpoint)
|
||||||
|
|
||||||
p.Common().goroutineCache.init(p.BinInfo())
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +139,7 @@ func FindFunctionLocation(p Process, funcName string, lineOffset int) ([]uint64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Next continues execution until the next source line.
|
// Next continues execution until the next source line.
|
||||||
func Next(dbp Process) (err error) {
|
func Next(dbp *Target) (err error) {
|
||||||
if _, err := dbp.Valid(); err != nil {
|
if _, err := dbp.Valid(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -161,7 +158,7 @@ func Next(dbp Process) (err error) {
|
|||||||
// Continue continues execution of the debugged
|
// Continue continues execution of the debugged
|
||||||
// process. It will continue until it hits a breakpoint
|
// process. It will continue until it hits a breakpoint
|
||||||
// or is otherwise stopped.
|
// or is otherwise stopped.
|
||||||
func Continue(dbp Process) error {
|
func Continue(dbp *Target) error {
|
||||||
if _, err := dbp.Valid(); err != nil {
|
if _, err := dbp.Valid(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -181,6 +178,7 @@ func Continue(dbp Process) error {
|
|||||||
dbp.ClearInternalBreakpoints()
|
dbp.ClearInternalBreakpoints()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
dbp.ClearAllGCache()
|
||||||
trapthread, err := dbp.ContinueOnce()
|
trapthread, err := dbp.ContinueOnce()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -230,7 +228,7 @@ func Continue(dbp Process) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return conditionErrors(threads)
|
return conditionErrors(threads)
|
||||||
case g == nil || dbp.Common().fncallForG[g.ID] == nil:
|
case g == nil || dbp.fncallForG[g.ID] == nil:
|
||||||
// a hardcoded breakpoint somewhere else in the code (probably cgo), or manual stop in cgo
|
// a hardcoded breakpoint somewhere else in the code (probably cgo), or manual stop in cgo
|
||||||
if !arch.BreakInstrMovesPC() {
|
if !arch.BreakInstrMovesPC() {
|
||||||
bpsize := arch.BreakpointSize()
|
bpsize := arch.BreakpointSize()
|
||||||
@ -355,7 +353,7 @@ func stepInstructionOut(dbp Process, curthread Thread, fnname1, fnname2 string)
|
|||||||
|
|
||||||
// Step will continue until another source line is reached.
|
// Step will continue until another source line is reached.
|
||||||
// Will step into functions.
|
// Will step into functions.
|
||||||
func Step(dbp Process) (err error) {
|
func Step(dbp *Target) (err error) {
|
||||||
if _, err := dbp.Valid(); err != nil {
|
if _, err := dbp.Valid(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -418,7 +416,7 @@ func andFrameoffCondition(cond ast.Expr, frameoff int64) ast.Expr {
|
|||||||
|
|
||||||
// StepOut will continue until the current goroutine exits the
|
// StepOut will continue until the current goroutine exits the
|
||||||
// function currently being executed or a deferred function is executed
|
// function currently being executed or a deferred function is executed
|
||||||
func StepOut(dbp Process) error {
|
func StepOut(dbp *Target) error {
|
||||||
if _, err := dbp.Valid(); err != nil {
|
if _, err := dbp.Valid(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -503,6 +501,43 @@ func StepOut(dbp Process) error {
|
|||||||
return Continue(dbp)
|
return Continue(dbp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StepInstruction will continue the current thread for exactly
|
||||||
|
// one instruction. This method affects only the thread
|
||||||
|
// associated with the selected goroutine. All other
|
||||||
|
// threads will remain stopped.
|
||||||
|
func StepInstruction(dbp *Target) (err error) {
|
||||||
|
thread := dbp.CurrentThread()
|
||||||
|
g := dbp.SelectedGoroutine()
|
||||||
|
if g != nil {
|
||||||
|
if g.Thread == nil {
|
||||||
|
// Step called on parked goroutine
|
||||||
|
if _, err := dbp.SetBreakpoint(g.PC, NextBreakpoint,
|
||||||
|
SameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return Continue(dbp)
|
||||||
|
}
|
||||||
|
thread = g.Thread
|
||||||
|
}
|
||||||
|
dbp.ClearAllGCache()
|
||||||
|
if ok, err := dbp.Valid(); !ok {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
thread.Breakpoint().Clear()
|
||||||
|
err = thread.StepInstruction()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = thread.SetCurrentBreakpoint(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if tg, _ := GetG(thread); tg != nil {
|
||||||
|
dbp.SetSelectedGoroutine(tg)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// GoroutinesInfo searches for goroutines starting at index 'start', and
|
// GoroutinesInfo searches for goroutines starting at index 'start', and
|
||||||
// returns an array of up to 'count' (or all found elements, if 'count' is 0)
|
// returns an array of up to 'count' (or all found elements, if 'count' is 0)
|
||||||
// G structures representing the information Delve care about from the internal
|
// G structures representing the information Delve care about from the internal
|
||||||
@ -510,14 +545,14 @@ func StepOut(dbp Process) error {
|
|||||||
// GoroutinesInfo also returns the next index to be used as 'start' argument
|
// GoroutinesInfo also returns the next index to be used as 'start' argument
|
||||||
// while scanning for all available goroutines, or -1 if there was an error
|
// while scanning for all available goroutines, or -1 if there was an error
|
||||||
// or if the index already reached the last possible value.
|
// or if the index already reached the last possible value.
|
||||||
func GoroutinesInfo(dbp Process, start, count int) ([]*G, int, error) {
|
func GoroutinesInfo(dbp *Target, start, count int) ([]*G, int, error) {
|
||||||
if _, err := dbp.Valid(); err != nil {
|
if _, err := dbp.Valid(); err != nil {
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
if dbp.Common().allGCache != nil {
|
if dbp.gcache.allGCache != nil {
|
||||||
// We can't use the cached array to fulfill a subrange request
|
// We can't use the cached array to fulfill a subrange request
|
||||||
if start == 0 && (count == 0 || count >= len(dbp.Common().allGCache)) {
|
if start == 0 && (count == 0 || count >= len(dbp.gcache.allGCache)) {
|
||||||
return dbp.Common().allGCache, -1, nil
|
return dbp.gcache.allGCache, -1, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,7 +572,7 @@ func GoroutinesInfo(dbp Process, start, count int) ([]*G, int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allgptr, allglen, err := dbp.Common().getRuntimeAllg(dbp.BinInfo(), dbp.CurrentThread())
|
allgptr, allglen, err := dbp.gcache.getRuntimeAllg(dbp.BinInfo(), dbp.CurrentThread())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
@ -569,68 +604,18 @@ func GoroutinesInfo(dbp Process, start, count int) ([]*G, int, error) {
|
|||||||
if g.Status != Gdead {
|
if g.Status != Gdead {
|
||||||
allg = append(allg, g)
|
allg = append(allg, g)
|
||||||
}
|
}
|
||||||
dbp.Common().addGoroutine(g)
|
dbp.gcache.addGoroutine(g)
|
||||||
}
|
}
|
||||||
if start == 0 {
|
if start == 0 {
|
||||||
dbp.Common().allGCache = allg
|
dbp.gcache.allGCache = allg
|
||||||
}
|
}
|
||||||
|
|
||||||
return allg, -1, nil
|
return allg, -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gcache *goroutineCache) init(bi *BinaryInfo) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
exeimage := bi.Images[0]
|
|
||||||
rdr := exeimage.DwarfReader()
|
|
||||||
|
|
||||||
gcache.allglenAddr, _ = rdr.AddrFor("runtime.allglen", exeimage.StaticBase)
|
|
||||||
|
|
||||||
rdr.Seek(0)
|
|
||||||
gcache.allgentryAddr, err = rdr.AddrFor("runtime.allgs", exeimage.StaticBase)
|
|
||||||
if err != nil {
|
|
||||||
// try old name (pre Go 1.6)
|
|
||||||
gcache.allgentryAddr, _ = rdr.AddrFor("runtime.allg", exeimage.StaticBase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gcache *goroutineCache) getRuntimeAllg(bi *BinaryInfo, mem MemoryReadWriter) (uint64, uint64, error) {
|
|
||||||
if gcache.allglenAddr == 0 || gcache.allgentryAddr == 0 {
|
|
||||||
return 0, 0, ErrNoRuntimeAllG
|
|
||||||
}
|
|
||||||
allglenBytes := make([]byte, 8)
|
|
||||||
_, err := mem.ReadMemory(allglenBytes, uintptr(gcache.allglenAddr))
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
allglen := binary.LittleEndian.Uint64(allglenBytes)
|
|
||||||
|
|
||||||
faddr := make([]byte, bi.Arch.PtrSize())
|
|
||||||
_, err = mem.ReadMemory(faddr, uintptr(gcache.allgentryAddr))
|
|
||||||
if err != nil {
|
|
||||||
return 0, 0, err
|
|
||||||
}
|
|
||||||
allgptr := binary.LittleEndian.Uint64(faddr)
|
|
||||||
|
|
||||||
return allgptr, allglen, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gcache *goroutineCache) addGoroutine(g *G) {
|
|
||||||
if gcache.partialGCache == nil {
|
|
||||||
gcache.partialGCache = make(map[int]*G)
|
|
||||||
}
|
|
||||||
gcache.partialGCache[g.ID] = g
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearAllGCache clears the cached contents of the cache for runtime.allgs.
|
|
||||||
func (gcache *goroutineCache) ClearAllGCache() {
|
|
||||||
gcache.partialGCache = nil
|
|
||||||
gcache.allGCache = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindGoroutine returns a G struct representing the goroutine
|
// FindGoroutine returns a G struct representing the goroutine
|
||||||
// specified by `gid`.
|
// specified by `gid`.
|
||||||
func FindGoroutine(dbp Process, gid int) (*G, error) {
|
func FindGoroutine(dbp *Target, gid int) (*G, error) {
|
||||||
if selg := dbp.SelectedGoroutine(); (gid == -1) || (selg != nil && selg.ID == gid) || (selg == nil && gid == 0) {
|
if selg := dbp.SelectedGoroutine(); (gid == -1) || (selg != nil && selg.ID == gid) || (selg == nil && gid == 0) {
|
||||||
// Return the currently selected goroutine in the following circumstances:
|
// Return the currently selected goroutine in the following circumstances:
|
||||||
//
|
//
|
||||||
@ -667,7 +652,7 @@ func FindGoroutine(dbp Process, gid int) (*G, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if g := dbp.Common().partialGCache[gid]; g != nil {
|
if g := dbp.gcache.partialGCache[gid]; g != nil {
|
||||||
return g, nil
|
return g, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,7 +681,7 @@ func FindGoroutine(dbp Process, gid int) (*G, error) {
|
|||||||
// ConvertEvalScope returns a new EvalScope in the context of the
|
// ConvertEvalScope returns a new EvalScope in the context of the
|
||||||
// specified goroutine ID and stack frame.
|
// specified goroutine ID and stack frame.
|
||||||
// If deferCall is > 0 the eval scope will be relative to the specified deferred call.
|
// If deferCall is > 0 the eval scope will be relative to the specified deferred call.
|
||||||
func ConvertEvalScope(dbp Process, gid, frame, deferCall int) (*EvalScope, error) {
|
func ConvertEvalScope(dbp *Target, gid, frame, deferCall int) (*EvalScope, error) {
|
||||||
if _, err := dbp.Valid(); err != nil {
|
if _, err := dbp.Valid(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -29,7 +29,7 @@ func TestIssue419(t *testing.T) {
|
|||||||
errChan := make(chan error, 2)
|
errChan := make(chan error, 2)
|
||||||
|
|
||||||
// SIGINT directed at the inferior should be passed along not swallowed by delve
|
// SIGINT directed at the inferior should be passed along not swallowed by delve
|
||||||
withTestProcess("issue419", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("issue419", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
defer close(errChan)
|
defer close(errChan)
|
||||||
setFunctionBreakpoint(p, t, "main.main")
|
setFunctionBreakpoint(p, t, "main.main")
|
||||||
assertNoError(proc.Continue(p), t, "Continue()")
|
assertNoError(proc.Continue(p), t, "Continue()")
|
||||||
|
|||||||
@ -23,7 +23,7 @@ func TestScopeWithEscapedVariable(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
withTestProcess("scopeescapevareval", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("scopeescapevareval", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
assertNoError(proc.Continue(p), t, "Continue")
|
assertNoError(proc.Continue(p), t, "Continue")
|
||||||
|
|
||||||
// On the breakpoint there are two 'a' variables in scope, the one that
|
// On the breakpoint there are two 'a' variables in scope, the one that
|
||||||
@ -72,7 +72,7 @@ func TestScope(t *testing.T) {
|
|||||||
|
|
||||||
scopeChecks := getScopeChecks(scopetestPath, t)
|
scopeChecks := getScopeChecks(scopetestPath, t)
|
||||||
|
|
||||||
withTestProcess("scopetest", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("scopetest", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
for i := range scopeChecks {
|
for i := range scopeChecks {
|
||||||
setFileBreakpoint(p, t, fixture.Source, scopeChecks[i].line)
|
setFileBreakpoint(p, t, fixture.Source, scopeChecks[i].line)
|
||||||
}
|
}
|
||||||
@ -237,7 +237,7 @@ func (check *scopeCheck) Parse(descr string, t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (scopeCheck *scopeCheck) checkLocalsAndArgs(p proc.Process, t *testing.T) (*proc.EvalScope, bool) {
|
func (scopeCheck *scopeCheck) checkLocalsAndArgs(p *proc.Target, t *testing.T) (*proc.EvalScope, bool) {
|
||||||
scope, err := proc.GoroutineScope(p.CurrentThread())
|
scope, err := proc.GoroutineScope(p.CurrentThread())
|
||||||
assertNoError(err, t, "GoroutineScope()")
|
assertNoError(err, t, "GoroutineScope()")
|
||||||
|
|
||||||
|
|||||||
47
pkg/proc/target.go
Normal file
47
pkg/proc/target.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package proc
|
||||||
|
|
||||||
|
// Target represents the process being debugged.
|
||||||
|
type Target struct {
|
||||||
|
Process
|
||||||
|
|
||||||
|
// fncallForG stores a mapping of current active function calls.
|
||||||
|
fncallForG map[int]*callInjection
|
||||||
|
|
||||||
|
// gcache is a cache for Goroutines that we
|
||||||
|
// have read and parsed from the targets memory.
|
||||||
|
// This must be cleared whenever the target is resumed.
|
||||||
|
gcache goroutineCache
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTarget returns an initialized Target object.
|
||||||
|
func NewTarget(p Process) *Target {
|
||||||
|
t := &Target{
|
||||||
|
Process: p,
|
||||||
|
fncallForG: make(map[int]*callInjection),
|
||||||
|
}
|
||||||
|
t.gcache.init(p.BinInfo())
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportsFunctionCalls returns whether or not the backend supports
|
||||||
|
// calling functions during a debug session.
|
||||||
|
// Currently only non-recorded processes running on AMD64 support
|
||||||
|
// function calls.
|
||||||
|
func (t *Target) SupportsFunctionCalls() bool {
|
||||||
|
if ok, _ := t.Process.Recorded(); ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_, ok := t.Process.BinInfo().Arch.(*AMD64)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearAllGCache clears the internal Goroutine cache.
|
||||||
|
// This should be called anytime the target process executes instructions.
|
||||||
|
func (t *Target) ClearAllGCache() {
|
||||||
|
t.gcache.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Target) Restart(from string) error {
|
||||||
|
t.ClearAllGCache()
|
||||||
|
return t.Process.Restart(from)
|
||||||
|
}
|
||||||
@ -20,7 +20,7 @@ type Thread interface {
|
|||||||
Location() (*Location, error)
|
Location() (*Location, error)
|
||||||
// Breakpoint will return the breakpoint that this thread is stopped at or
|
// Breakpoint will return the breakpoint that this thread is stopped at or
|
||||||
// nil if the thread is not stopped at any breakpoint.
|
// nil if the thread is not stopped at any breakpoint.
|
||||||
Breakpoint() BreakpointState
|
Breakpoint() *BreakpointState
|
||||||
ThreadID() int
|
ThreadID() int
|
||||||
|
|
||||||
// Registers returns the CPU registers of this thread. The contents of the
|
// Registers returns the CPU registers of this thread. The contents of the
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func TestGoroutineCreationLocation(t *testing.T) {
|
func TestGoroutineCreationLocation(t *testing.T) {
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("goroutinestackprog", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("goroutinestackprog", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
bp := setFunctionBreakpoint(p, t, "main.agoroutine")
|
bp := setFunctionBreakpoint(p, t, "main.agoroutine")
|
||||||
assertNoError(proc.Continue(p), t, "Continue()")
|
assertNoError(proc.Continue(p), t, "Continue()")
|
||||||
|
|
||||||
|
|||||||
@ -37,7 +37,7 @@ type Debugger struct {
|
|||||||
processArgs []string
|
processArgs []string
|
||||||
// TODO(DO NOT MERGE WITHOUT) rename to targetMutex
|
// TODO(DO NOT MERGE WITHOUT) rename to targetMutex
|
||||||
processMutex sync.Mutex
|
processMutex sync.Mutex
|
||||||
target proc.Process
|
target *proc.Target
|
||||||
log *logrus.Entry
|
log *logrus.Entry
|
||||||
|
|
||||||
running bool
|
running bool
|
||||||
@ -102,7 +102,7 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
|||||||
d.target = p
|
d.target = p
|
||||||
|
|
||||||
case d.config.CoreFile != "":
|
case d.config.CoreFile != "":
|
||||||
var p proc.Process
|
var p *proc.Target
|
||||||
var err error
|
var err error
|
||||||
switch d.config.Backend {
|
switch d.config.Backend {
|
||||||
case "rr":
|
case "rr":
|
||||||
@ -165,7 +165,7 @@ func (d *Debugger) checkGoVersion() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Launch will start a process with the given args and working directory.
|
// Launch will start a process with the given args and working directory.
|
||||||
func (d *Debugger) Launch(processArgs []string, wd string) (proc.Process, error) {
|
func (d *Debugger) Launch(processArgs []string, wd string) (*proc.Target, error) {
|
||||||
switch d.config.Backend {
|
switch d.config.Backend {
|
||||||
case "native":
|
case "native":
|
||||||
return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories)
|
return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories)
|
||||||
@ -190,7 +190,7 @@ func (d *Debugger) Launch(processArgs []string, wd string) (proc.Process, error)
|
|||||||
var ErrNoAttachPath = errors.New("must specify executable path on macOS")
|
var ErrNoAttachPath = errors.New("must specify executable path on macOS")
|
||||||
|
|
||||||
// Attach will attach to the process specified by 'pid'.
|
// Attach will attach to the process specified by 'pid'.
|
||||||
func (d *Debugger) Attach(pid int, path string) (proc.Process, error) {
|
func (d *Debugger) Attach(pid int, path string) (*proc.Target, error) {
|
||||||
switch d.config.Backend {
|
switch d.config.Backend {
|
||||||
case "native":
|
case "native":
|
||||||
return native.Attach(pid, d.config.DebugInfoDirectories)
|
return native.Attach(pid, d.config.DebugInfoDirectories)
|
||||||
@ -208,7 +208,7 @@ func (d *Debugger) Attach(pid int, path string) (proc.Process, error) {
|
|||||||
|
|
||||||
var errMacOSBackendUnavailable = errors.New("debugserver or lldb-server not found: install XCode's command line tools or lldb-server")
|
var errMacOSBackendUnavailable = errors.New("debugserver or lldb-server not found: install XCode's command line tools or lldb-server")
|
||||||
|
|
||||||
func betterGdbserialLaunchError(p proc.Process, err error) (proc.Process, error) {
|
func betterGdbserialLaunchError(p *proc.Target, err error) (*proc.Target, error) {
|
||||||
if runtime.GOOS != "darwin" {
|
if runtime.GOOS != "darwin" {
|
||||||
return p, err
|
return p, err
|
||||||
}
|
}
|
||||||
@ -717,7 +717,7 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
|
|||||||
err = proc.Step(d.target)
|
err = proc.Step(d.target)
|
||||||
case api.StepInstruction:
|
case api.StepInstruction:
|
||||||
d.log.Debug("single stepping")
|
d.log.Debug("single stepping")
|
||||||
err = d.target.StepInstruction()
|
err = proc.StepInstruction(d.target)
|
||||||
case api.ReverseStepInstruction:
|
case api.ReverseStepInstruction:
|
||||||
d.log.Debug("reverse single stepping")
|
d.log.Debug("reverse single stepping")
|
||||||
if err := d.target.Direction(proc.Backward); err != nil {
|
if err := d.target.Direction(proc.Backward); err != nil {
|
||||||
@ -726,7 +726,7 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
|
|||||||
defer func() {
|
defer func() {
|
||||||
d.target.Direction(proc.Forward)
|
d.target.Direction(proc.Forward)
|
||||||
}()
|
}()
|
||||||
err = d.target.StepInstruction()
|
err = proc.StepInstruction(d.target)
|
||||||
case api.StepOut:
|
case api.StepOut:
|
||||||
d.log.Debug("step out")
|
d.log.Debug("step out")
|
||||||
err = proc.StepOut(d.target)
|
err = proc.StepOut(d.target)
|
||||||
@ -736,7 +736,10 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
|
|||||||
withBreakpointInfo = false
|
withBreakpointInfo = false
|
||||||
case api.SwitchGoroutine:
|
case api.SwitchGoroutine:
|
||||||
d.log.Debugf("switching to goroutine %d", command.GoroutineID)
|
d.log.Debugf("switching to goroutine %d", command.GoroutineID)
|
||||||
err = d.target.SwitchGoroutine(command.GoroutineID)
|
g, err := proc.FindGoroutine(d.target, command.GoroutineID)
|
||||||
|
if err == nil {
|
||||||
|
err = d.target.SwitchGoroutine(g)
|
||||||
|
}
|
||||||
withBreakpointInfo = false
|
withBreakpointInfo = false
|
||||||
case api.Halt:
|
case api.Halt:
|
||||||
// RequestManualStop already called
|
// RequestManualStop already called
|
||||||
|
|||||||
@ -59,7 +59,7 @@ func assertVariable(t *testing.T, variable *proc.Variable, expected varTest) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func findFirstNonRuntimeFrame(p proc.Process) (proc.Stackframe, error) {
|
func findFirstNonRuntimeFrame(p *proc.Target) (proc.Stackframe, error) {
|
||||||
frames, err := proc.ThreadStacktrace(p.CurrentThread(), 10)
|
frames, err := proc.ThreadStacktrace(p.CurrentThread(), 10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return proc.Stackframe{}, err
|
return proc.Stackframe{}, err
|
||||||
@ -73,7 +73,7 @@ func findFirstNonRuntimeFrame(p proc.Process) (proc.Stackframe, error) {
|
|||||||
return proc.Stackframe{}, fmt.Errorf("non-runtime frame not found")
|
return proc.Stackframe{}, fmt.Errorf("non-runtime frame not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalScope(p proc.Process) (*proc.EvalScope, error) {
|
func evalScope(p *proc.Target) (*proc.EvalScope, error) {
|
||||||
if testBackend != "rr" {
|
if testBackend != "rr" {
|
||||||
return proc.GoroutineScope(p.CurrentThread())
|
return proc.GoroutineScope(p.CurrentThread())
|
||||||
}
|
}
|
||||||
@ -84,7 +84,7 @@ func evalScope(p proc.Process) (*proc.EvalScope, error) {
|
|||||||
return proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frame), nil
|
return proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frame), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalVariable(p proc.Process, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
|
func evalVariable(p *proc.Target, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
|
||||||
scope, err := evalScope(p)
|
scope, err := evalScope(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -99,7 +99,7 @@ func (tc *varTest) alternateVarTest() varTest {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func setVariable(p proc.Process, symbol, value string) error {
|
func setVariable(p *proc.Target, symbol, value string) error {
|
||||||
scope, err := proc.GoroutineScope(p.CurrentThread())
|
scope, err := proc.GoroutineScope(p.CurrentThread())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -107,16 +107,16 @@ func setVariable(p proc.Process, symbol, value string) error {
|
|||||||
return scope.SetVariable(symbol, value)
|
return scope.SetVariable(symbol, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func withTestProcess(name string, t *testing.T, fn func(p proc.Process, fixture protest.Fixture)) {
|
func withTestProcess(name string, t *testing.T, fn func(p *proc.Target, fixture protest.Fixture)) {
|
||||||
withTestProcessArgs(name, t, ".", []string{}, 0, fn)
|
withTestProcessArgs(name, t, ".", []string{}, 0, fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func withTestProcessArgs(name string, t *testing.T, wd string, args []string, buildFlags protest.BuildFlags, fn func(p proc.Process, fixture protest.Fixture)) {
|
func withTestProcessArgs(name string, t *testing.T, wd string, args []string, buildFlags protest.BuildFlags, fn func(p *proc.Target, fixture protest.Fixture)) {
|
||||||
if buildMode == "pie" {
|
if buildMode == "pie" {
|
||||||
buildFlags |= protest.BuildModePIE
|
buildFlags |= protest.BuildModePIE
|
||||||
}
|
}
|
||||||
fixture := protest.BuildFixture(name, buildFlags)
|
fixture := protest.BuildFixture(name, buildFlags)
|
||||||
var p proc.Process
|
var p *proc.Target
|
||||||
var err error
|
var err error
|
||||||
var tracedir string
|
var tracedir string
|
||||||
switch testBackend {
|
switch testBackend {
|
||||||
@ -189,7 +189,7 @@ func TestVariableEvaluation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("testvariables", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
err := proc.Continue(p)
|
err := proc.Continue(p)
|
||||||
assertNoError(err, t, "Continue() returned an error")
|
assertNoError(err, t, "Continue() returned an error")
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ func TestSetVariable(t *testing.T) {
|
|||||||
{"s3", "[]int", "[]int len: 3, cap: 3, [3,4,5]", "arr1[:]", "[]int len: 4, cap: 4, [0,1,2,3]"},
|
{"s3", "[]int", "[]int len: 3, cap: 3, [3,4,5]", "arr1[:]", "[]int len: 4, cap: 4, [0,1,2,3]"},
|
||||||
}
|
}
|
||||||
|
|
||||||
withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("testvariables2", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
assertNoError(proc.Continue(p), t, "Continue()")
|
assertNoError(proc.Continue(p), t, "Continue()")
|
||||||
|
|
||||||
for _, tc := range testcases {
|
for _, tc := range testcases {
|
||||||
@ -316,7 +316,7 @@ func TestVariableEvaluationShort(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("testvariables", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
err := proc.Continue(p)
|
err := proc.Continue(p)
|
||||||
assertNoError(err, t, "Continue() returned an error")
|
assertNoError(err, t, "Continue() returned an error")
|
||||||
|
|
||||||
@ -372,7 +372,7 @@ func TestMultilineVariableEvaluation(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("testvariables", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
err := proc.Continue(p)
|
err := proc.Continue(p)
|
||||||
assertNoError(err, t, "Continue() returned an error")
|
assertNoError(err, t, "Continue() returned an error")
|
||||||
|
|
||||||
@ -446,7 +446,7 @@ func TestLocalVariables(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("testvariables", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
err := proc.Continue(p)
|
err := proc.Continue(p)
|
||||||
assertNoError(err, t, "Continue() returned an error")
|
assertNoError(err, t, "Continue() returned an error")
|
||||||
|
|
||||||
@ -483,7 +483,7 @@ func TestLocalVariables(t *testing.T) {
|
|||||||
|
|
||||||
func TestEmbeddedStruct(t *testing.T) {
|
func TestEmbeddedStruct(t *testing.T) {
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("testvariables2", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
testcases := []varTest{
|
testcases := []varTest{
|
||||||
{"b.val", true, "-314", "-314", "int", nil},
|
{"b.val", true, "-314", "-314", "int", nil},
|
||||||
{"b.A.val", true, "-314", "-314", "int", nil},
|
{"b.A.val", true, "-314", "-314", "int", nil},
|
||||||
@ -524,7 +524,7 @@ func TestEmbeddedStruct(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestComplexSetting(t *testing.T) {
|
func TestComplexSetting(t *testing.T) {
|
||||||
withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("testvariables", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
err := proc.Continue(p)
|
err := proc.Continue(p)
|
||||||
assertNoError(err, t, "Continue() returned an error")
|
assertNoError(err, t, "Continue() returned an error")
|
||||||
|
|
||||||
@ -822,7 +822,7 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("testvariables2", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
||||||
for _, tc := range testcases {
|
for _, tc := range testcases {
|
||||||
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
||||||
@ -851,7 +851,7 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
|
|
||||||
func TestEvalAddrAndCast(t *testing.T) {
|
func TestEvalAddrAndCast(t *testing.T) {
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("testvariables2", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
||||||
c1addr, err := evalVariable(p, "&c1", pnormalLoadConfig)
|
c1addr, err := evalVariable(p, "&c1", pnormalLoadConfig)
|
||||||
assertNoError(err, t, "EvalExpression(&c1)")
|
assertNoError(err, t, "EvalExpression(&c1)")
|
||||||
@ -878,7 +878,7 @@ func TestEvalAddrAndCast(t *testing.T) {
|
|||||||
|
|
||||||
func TestMapEvaluation(t *testing.T) {
|
func TestMapEvaluation(t *testing.T) {
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("testvariables2", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
||||||
m1v, err := evalVariable(p, "m1", pnormalLoadConfig)
|
m1v, err := evalVariable(p, "m1", pnormalLoadConfig)
|
||||||
assertNoError(err, t, "EvalVariable()")
|
assertNoError(err, t, "EvalVariable()")
|
||||||
@ -920,7 +920,7 @@ func TestMapEvaluation(t *testing.T) {
|
|||||||
|
|
||||||
func TestUnsafePointer(t *testing.T) {
|
func TestUnsafePointer(t *testing.T) {
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("testvariables2", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
||||||
up1v, err := evalVariable(p, "up1", pnormalLoadConfig)
|
up1v, err := evalVariable(p, "up1", pnormalLoadConfig)
|
||||||
assertNoError(err, t, "EvalVariable(up1)")
|
assertNoError(err, t, "EvalVariable(up1)")
|
||||||
@ -958,7 +958,7 @@ func TestIssue426(t *testing.T) {
|
|||||||
// Serialization of type expressions (go/ast.Expr) containing anonymous structs or interfaces
|
// Serialization of type expressions (go/ast.Expr) containing anonymous structs or interfaces
|
||||||
// differs from the serialization used by the linker to produce DWARF type information
|
// differs from the serialization used by the linker to produce DWARF type information
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("testvariables2", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
||||||
for _, testcase := range testcases {
|
for _, testcase := range testcases {
|
||||||
v, err := evalVariable(p, testcase.name, pnormalLoadConfig)
|
v, err := evalVariable(p, testcase.name, pnormalLoadConfig)
|
||||||
@ -971,7 +971,7 @@ func TestIssue426(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPackageRenamesHelper(t *testing.T, p proc.Process, testcases []varTest) {
|
func testPackageRenamesHelper(t *testing.T, p *proc.Target, testcases []varTest) {
|
||||||
for _, tc := range testcases {
|
for _, tc := range testcases {
|
||||||
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
||||||
if tc.err == nil {
|
if tc.err == nil {
|
||||||
@ -1041,7 +1041,7 @@ func TestPackageRenames(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("pkgrenames", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("pkgrenames", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
assertNoError(proc.Continue(p), t, "Continue() returned an error")
|
||||||
testPackageRenamesHelper(t, p, testcases)
|
testPackageRenamesHelper(t, p, testcases)
|
||||||
|
|
||||||
@ -1075,7 +1075,7 @@ func TestConstants(t *testing.T) {
|
|||||||
// Not supported on 1.9 or earlier
|
// Not supported on 1.9 or earlier
|
||||||
t.Skip("constants added in go 1.10")
|
t.Skip("constants added in go 1.10")
|
||||||
}
|
}
|
||||||
withTestProcess("consts", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("consts", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
assertNoError(proc.Continue(p), t, "Continue")
|
assertNoError(proc.Continue(p), t, "Continue")
|
||||||
for _, testcase := range testcases {
|
for _, testcase := range testcases {
|
||||||
variable, err := evalVariable(p, testcase.name, pnormalLoadConfig)
|
variable, err := evalVariable(p, testcase.name, pnormalLoadConfig)
|
||||||
@ -1085,7 +1085,7 @@ func TestConstants(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func setFunctionBreakpoint(p proc.Process, t testing.TB, fname string) *proc.Breakpoint {
|
func setFunctionBreakpoint(p *proc.Target, t testing.TB, fname string) *proc.Breakpoint {
|
||||||
_, f, l, _ := runtime.Caller(1)
|
_, f, l, _ := runtime.Caller(1)
|
||||||
f = filepath.Base(f)
|
f = filepath.Base(f)
|
||||||
|
|
||||||
@ -1104,7 +1104,7 @@ func setFunctionBreakpoint(p proc.Process, t testing.TB, fname string) *proc.Bre
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue1075(t *testing.T) {
|
func TestIssue1075(t *testing.T) {
|
||||||
withTestProcess("clientdo", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("clientdo", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
setFunctionBreakpoint(p, t, "net/http.(*Client).Do")
|
setFunctionBreakpoint(p, t, "net/http.(*Client).Do")
|
||||||
assertNoError(proc.Continue(p), t, "Continue()")
|
assertNoError(proc.Continue(p), t, "Continue()")
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
@ -1230,7 +1230,7 @@ func TestCallFunction(t *testing.T) {
|
|||||||
{`getVRcvrableFromAStructPtr(6).VRcvr(5)`, []string{`:string:"5 + 6 = 11"`}, nil}, // indirect call of method on interface / containing pointer with pointer method
|
{`getVRcvrableFromAStructPtr(6).VRcvr(5)`, []string{`:string:"5 + 6 = 11"`}, nil}, // indirect call of method on interface / containing pointer with pointer method
|
||||||
}
|
}
|
||||||
|
|
||||||
withTestProcess("fncall", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("fncall", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
_, err := proc.FindFunctionLocation(p, "runtime.debugCallV1", 0)
|
_, err := proc.FindFunctionLocation(p, "runtime.debugCallV1", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Skip("function calls not supported on this version of go")
|
t.Skip("function calls not supported on this version of go")
|
||||||
@ -1257,7 +1257,7 @@ func TestCallFunction(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCallFunction(t *testing.T, p proc.Process, tc testCaseCallFunction) {
|
func testCallFunction(t *testing.T, p *proc.Target, tc testCaseCallFunction) {
|
||||||
const unsafePrefix = "-unsafe "
|
const unsafePrefix = "-unsafe "
|
||||||
|
|
||||||
var callExpr, varExpr string
|
var callExpr, varExpr string
|
||||||
@ -1333,7 +1333,7 @@ func testCallFunction(t *testing.T, p proc.Process, tc testCaseCallFunction) {
|
|||||||
|
|
||||||
func TestIssue1531(t *testing.T) {
|
func TestIssue1531(t *testing.T) {
|
||||||
// Go 1.12 introduced a change to the map representation where empty cells can be marked with 1 instead of just 0.
|
// Go 1.12 introduced a change to the map representation where empty cells can be marked with 1 instead of just 0.
|
||||||
withTestProcess("issue1531", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("issue1531", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
assertNoError(proc.Continue(p), t, "Continue()")
|
assertNoError(proc.Continue(p), t, "Continue()")
|
||||||
|
|
||||||
hasKeys := func(mv *proc.Variable, keys ...string) {
|
hasKeys := func(mv *proc.Variable, keys ...string) {
|
||||||
@ -1373,7 +1373,7 @@ func TestIssue1531(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func setFileBreakpoint(p proc.Process, t *testing.T, fixture protest.Fixture, lineno int) *proc.Breakpoint {
|
func setFileBreakpoint(p *proc.Target, t *testing.T, fixture protest.Fixture, lineno int) *proc.Breakpoint {
|
||||||
_, f, l, _ := runtime.Caller(1)
|
_, f, l, _ := runtime.Caller(1)
|
||||||
f = filepath.Base(f)
|
f = filepath.Base(f)
|
||||||
|
|
||||||
@ -1391,7 +1391,7 @@ func setFileBreakpoint(p proc.Process, t *testing.T, fixture protest.Fixture, li
|
|||||||
return bp
|
return bp
|
||||||
}
|
}
|
||||||
|
|
||||||
func currentLocation(p proc.Process, t *testing.T) (pc uint64, f string, ln int, fn *proc.Function) {
|
func currentLocation(p *proc.Target, t *testing.T) (pc uint64, f string, ln int, fn *proc.Function) {
|
||||||
regs, err := p.CurrentThread().Registers(false)
|
regs, err := p.CurrentThread().Registers(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Registers error: %v", err)
|
t.Fatalf("Registers error: %v", err)
|
||||||
@ -1401,7 +1401,7 @@ func currentLocation(p proc.Process, t *testing.T) (pc uint64, f string, ln int,
|
|||||||
return regs.PC(), f, l, fn
|
return regs.PC(), f, l, fn
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertCurrentLocationFunction(p proc.Process, t *testing.T, fnname string) {
|
func assertCurrentLocationFunction(p *proc.Target, t *testing.T, fnname string) {
|
||||||
_, _, _, fn := currentLocation(p, t)
|
_, _, _, fn := currentLocation(p, t)
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
t.Fatalf("Not in a function")
|
t.Fatalf("Not in a function")
|
||||||
@ -1414,7 +1414,7 @@ func assertCurrentLocationFunction(p proc.Process, t *testing.T, fnname string)
|
|||||||
func TestPluginVariables(t *testing.T) {
|
func TestPluginVariables(t *testing.T) {
|
||||||
pluginFixtures := protest.WithPlugins(t, protest.AllNonOptimized, "plugin1/", "plugin2/")
|
pluginFixtures := protest.WithPlugins(t, protest.AllNonOptimized, "plugin1/", "plugin2/")
|
||||||
|
|
||||||
withTestProcessArgs("plugintest2", t, ".", []string{pluginFixtures[0].Path, pluginFixtures[1].Path}, protest.AllNonOptimized, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcessArgs("plugintest2", t, ".", []string{pluginFixtures[0].Path, pluginFixtures[1].Path}, protest.AllNonOptimized, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
setFileBreakpoint(p, t, fixture, 41)
|
setFileBreakpoint(p, t, fixture, 41)
|
||||||
assertNoError(proc.Continue(p), t, "Continue 1")
|
assertNoError(proc.Continue(p), t, "Continue 1")
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user