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
|
||||
currentThread *Thread
|
||||
selectedGoroutine *proc.G
|
||||
common proc.CommonProcess
|
||||
}
|
||||
|
||||
// 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.
|
||||
// If the DWARF information cannot be found in the binary, Delve will look
|
||||
// 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 err error
|
||||
for _, openFn := range openFns {
|
||||
@ -217,7 +216,7 @@ func OpenCore(corePath, exePath string, debugInfoDirs []string) (*Process, error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
return proc.NewTarget(p), nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
// For core files this always returns an empty BreakpointState struct, as
|
||||
// there are no breakpoints when debugging core files.
|
||||
func (t *Thread) Breakpoint() proc.BreakpointState {
|
||||
return proc.BreakpointState{}
|
||||
func (t *Thread) Breakpoint() *proc.BreakpointState {
|
||||
return &proc.BreakpointState{}
|
||||
}
|
||||
|
||||
// ThreadID returns the ID for this thread.
|
||||
@ -434,12 +433,6 @@ func (p *Process) Valid() (bool, error) {
|
||||
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.
|
||||
func (p *Process) Pid() int {
|
||||
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.
|
||||
func (p *Process) SwitchGoroutine(gid int) error {
|
||||
g, err := proc.FindGoroutine(p, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (p *Process) SwitchGoroutine(g *proc.G) error {
|
||||
if g == nil {
|
||||
// user specified -1 and selectedGoroutine is 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.
|
||||
// Might be better to check in the binary and core?
|
||||
tempDir, err := ioutil.TempDir("", "")
|
||||
|
@ -90,7 +90,7 @@ type functionCallState struct {
|
||||
}
|
||||
|
||||
type callContext struct {
|
||||
p Process
|
||||
p *Target
|
||||
|
||||
// checkEscape is true if the escape check should be performed.
|
||||
// See service/api.DebuggerCommand.UnsafeCall in service/api/types.go.
|
||||
@ -115,6 +115,14 @@ type continueRequest struct {
|
||||
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 {
|
||||
callCtx.continueRequest <- continueRequest{cont: true}
|
||||
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'.
|
||||
// Because this can only be done in the current goroutine, unlike
|
||||
// EvalExpression, EvalExpressionWithCalls is not a method of EvalScope.
|
||||
func EvalExpressionWithCalls(p Process, g *G, expr string, retLoadCfg LoadConfig, checkEscape bool) error {
|
||||
bi := p.BinInfo()
|
||||
if !p.Common().fncallEnabled {
|
||||
func EvalExpressionWithCalls(t *Target, g *G, expr string, retLoadCfg LoadConfig, checkEscape bool) error {
|
||||
bi := t.BinInfo()
|
||||
if !t.SupportsFunctionCalls() {
|
||||
return errFuncCallUnsupportedBackend
|
||||
}
|
||||
|
||||
@ -144,7 +152,7 @@ func EvalExpressionWithCalls(p Process, g *G, expr string, retLoadCfg LoadConfig
|
||||
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
|
||||
}
|
||||
|
||||
@ -162,14 +170,14 @@ func EvalExpressionWithCalls(p Process, g *G, expr string, retLoadCfg LoadConfig
|
||||
continueCompleted := make(chan *G)
|
||||
|
||||
scope.callCtx = &callContext{
|
||||
p: p,
|
||||
p: t,
|
||||
checkEscape: checkEscape,
|
||||
retLoadCfg: retLoadCfg,
|
||||
continueRequest: continueRequest,
|
||||
continueCompleted: continueCompleted,
|
||||
}
|
||||
|
||||
p.Common().fncallForG[g.ID] = &callInjection{
|
||||
t.fncallForG[g.ID] = &callInjection{
|
||||
continueCompleted,
|
||||
continueRequest,
|
||||
}
|
||||
@ -178,13 +186,13 @@ func EvalExpressionWithCalls(p Process, g *G, expr string, retLoadCfg LoadConfig
|
||||
|
||||
contReq, ok := <-continueRequest
|
||||
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())
|
||||
var err error
|
||||
if !ok {
|
||||
@ -208,8 +216,8 @@ func finishEvalExpressionWithCalls(p Process, g *G, contReq continueRequest, ok
|
||||
g.Thread.Common().returnValues = []*Variable{contReq.ret}
|
||||
}
|
||||
|
||||
close(p.Common().fncallForG[g.ID].continueCompleted)
|
||||
delete(p.Common().fncallForG, g.ID)
|
||||
close(t.fncallForG[g.ID].continueCompleted)
|
||||
delete(t.fncallForG, g.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -234,7 +242,7 @@ func evalFunctionCall(scope *EvalScope, node *ast.CallExpr) (*Variable, error) {
|
||||
|
||||
p := scope.callCtx.p
|
||||
bi := scope.BinInfo
|
||||
if !p.Common().fncallEnabled {
|
||||
if !p.SupportsFunctionCalls() {
|
||||
return nil, errFuncCallUnsupportedBackend
|
||||
}
|
||||
|
||||
@ -879,8 +887,8 @@ func isCallInjectionStop(loc *Location) bool {
|
||||
// callInjectionProtocol is the function called from Continue to progress
|
||||
// the injection protocol for all threads.
|
||||
// Returns true if a call injection terminated
|
||||
func callInjectionProtocol(p Process, threads []Thread) (done bool, err error) {
|
||||
if len(p.Common().fncallForG) == 0 {
|
||||
func callInjectionProtocol(t *Target, threads []Thread) (done bool, err error) {
|
||||
if len(t.fncallForG) == 0 {
|
||||
// we aren't injecting any calls, no need to check the threads.
|
||||
return false, nil
|
||||
}
|
||||
@ -897,7 +905,7 @@ func callInjectionProtocol(p Process, threads []Thread) (done bool, err error) {
|
||||
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)
|
||||
}
|
||||
callinj := p.Common().fncallForG[g.ID]
|
||||
callinj := t.fncallForG[g.ID]
|
||||
if callinj == nil || callinj.continueCompleted == nil {
|
||||
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
|
||||
contReq, ok := <-callinj.continueRequest
|
||||
if !contReq.cont {
|
||||
err := finishEvalExpressionWithCalls(p, g, contReq, ok)
|
||||
err := finishEvalExpressionWithCalls(t, g, contReq, ok)
|
||||
if err != nil {
|
||||
return done, err
|
||||
}
|
||||
|
@ -126,7 +126,6 @@ type Process struct {
|
||||
|
||||
onDetach func() // called after a successful detach
|
||||
|
||||
common proc.CommonProcess
|
||||
}
|
||||
|
||||
// Thread represents an operating system thread.
|
||||
@ -184,7 +183,6 @@ func New(process *os.Process) *Process {
|
||||
gcmdok: true,
|
||||
threadStopInfo: true,
|
||||
process: process,
|
||||
common: proc.NewCommonProcess(true),
|
||||
}
|
||||
|
||||
if process != nil {
|
||||
@ -320,7 +318,7 @@ func getLdEnvVars() []string {
|
||||
// LLDBLaunch starts an instance of lldb-server and connects to it, asking
|
||||
// it to launch the specified target program with the specified arguments
|
||||
// (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 {
|
||||
case "windows":
|
||||
return nil, ErrUnsupportedOS
|
||||
@ -343,7 +341,7 @@ func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string
|
||||
|
||||
var listener net.Listener
|
||||
var port string
|
||||
var proc *exec.Cmd
|
||||
var process *exec.Cmd
|
||||
if _, err := os.Stat(debugserverExecutable); err == nil {
|
||||
listener, err = net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
@ -363,7 +361,7 @@ func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string
|
||||
|
||||
isDebugserver = true
|
||||
|
||||
proc = exec.Command(debugserverExecutable, args...)
|
||||
process = exec.Command(debugserverExecutable, args...)
|
||||
} else {
|
||||
if _, err := exec.LookPath("lldb-server"); err != nil {
|
||||
return nil, &ErrBackendUnavailable{}
|
||||
@ -374,29 +372,29 @@ func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string
|
||||
args = append(args, port, "--")
|
||||
args = append(args, cmd...)
|
||||
|
||||
proc = exec.Command("lldb-server", args...)
|
||||
process = exec.Command("lldb-server", args...)
|
||||
}
|
||||
|
||||
if logflags.LLDBServerOutput() || logflags.GdbWire() || foreground {
|
||||
proc.Stdout = os.Stdout
|
||||
proc.Stderr = os.Stderr
|
||||
process.Stdout = os.Stdout
|
||||
process.Stderr = os.Stderr
|
||||
}
|
||||
if foreground {
|
||||
foregroundSignalsIgnore()
|
||||
proc.Stdin = os.Stdin
|
||||
process.Stdin = os.Stdin
|
||||
}
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := New(proc.Process)
|
||||
p := New(process.Process)
|
||||
p.conn.isDebugserver = isDebugserver
|
||||
|
||||
if listener != nil {
|
||||
@ -407,7 +405,7 @@ func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
return proc.NewTarget(p), nil
|
||||
}
|
||||
|
||||
// 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
|
||||
// for some stubs that do not provide an automated way of determining it
|
||||
// (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" {
|
||||
return nil, ErrUnsupportedOS
|
||||
}
|
||||
@ -459,7 +457,7 @@ func LLDBAttach(pid int, path string, debugInfoDirs []string) (*Process, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
return proc.NewTarget(p), nil
|
||||
}
|
||||
|
||||
// EntryPoint will return the process entry point address, useful for
|
||||
@ -602,11 +600,6 @@ func (p *Process) CurrentThread() proc.Thread {
|
||||
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.
|
||||
func (p *Process) SelectedGoroutine() *proc.G {
|
||||
return p.selectedGoroutine
|
||||
@ -645,7 +638,6 @@ func (p *Process) ContinueOnce() (proc.Thread, error) {
|
||||
}
|
||||
}
|
||||
|
||||
p.common.ClearAllGCache()
|
||||
for _, th := range p.threads {
|
||||
th.clearBreakpointState()
|
||||
}
|
||||
@ -751,37 +743,6 @@ func (p *Process) SetSelectedGoroutine(g *proc.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.
|
||||
func (p *Process) SwitchThread(tid int) error {
|
||||
if p.exited {
|
||||
@ -796,13 +757,8 @@ func (p *Process) SwitchThread(tid int) error {
|
||||
}
|
||||
|
||||
// SwitchGoroutine will change the internal selected goroutine.
|
||||
func (p *Process) SwitchGoroutine(gid int) error {
|
||||
g, err := proc.FindGoroutine(p, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (p *Process) SwitchGoroutine(g *proc.G) error {
|
||||
if g == nil {
|
||||
// user specified -1 and selectedGoroutine is nil
|
||||
return nil
|
||||
}
|
||||
if g.Thread != nil {
|
||||
@ -885,7 +841,6 @@ func (p *Process) Restart(pos string) error {
|
||||
|
||||
p.exited = false
|
||||
|
||||
p.common.ClearAllGCache()
|
||||
for _, th := range p.threads {
|
||||
th.clearBreakpointState()
|
||||
}
|
||||
@ -1243,8 +1198,8 @@ func (t *Thread) Location() (*proc.Location, error) {
|
||||
}
|
||||
|
||||
// Breakpoint returns the current active breakpoint for this thread.
|
||||
func (t *Thread) Breakpoint() proc.BreakpointState {
|
||||
return t.CurrentBreakpoint
|
||||
func (t *Thread) Breakpoint() *proc.BreakpointState {
|
||||
return &t.CurrentBreakpoint
|
||||
}
|
||||
|
||||
// ThreadID returns this threads ID.
|
||||
|
@ -12,6 +12,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// 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
|
||||
// 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 {
|
||||
return nil, err
|
||||
}
|
||||
@ -94,7 +96,7 @@ func Replay(tracedir string, quiet, deleteOnDetach bool, debugInfoDirs []string)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
return proc.NewTarget(p), nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string) (p *Process, tracedir string, err error) {
|
||||
tracedir, err = Record(cmd, wd, quiet)
|
||||
func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string) (*proc.Target, string, error) {
|
||||
tracedir, err := Record(cmd, wd, quiet)
|
||||
if tracedir == "" {
|
||||
return nil, "", err
|
||||
}
|
||||
p, err = Replay(tracedir, quiet, true, debugInfoDirs)
|
||||
return p, tracedir, err
|
||||
t, err := Replay(tracedir, quiet, true, debugInfoDirs)
|
||||
return t, tracedir, err
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
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)
|
||||
protest.MustHaveRecordingAllowed(t)
|
||||
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)
|
||||
|
||||
defer func() {
|
||||
p.Detach(true)
|
||||
}()
|
||||
defer p.Detach(true)
|
||||
|
||||
fn(p, fixture)
|
||||
}
|
||||
@ -71,7 +69,7 @@ func setFunctionBreakpoint(p proc.Process, t *testing.T, fname string) *proc.Bre
|
||||
|
||||
func TestRestartAfterExit(t *testing.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")
|
||||
assertNoError(proc.Continue(p), t, "Continue")
|
||||
loc, err := p.CurrentThread().Location()
|
||||
@ -98,7 +96,7 @@ func TestRestartAfterExit(t *testing.T) {
|
||||
|
||||
func TestRestartDuringStop(t *testing.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")
|
||||
assertNoError(proc.Continue(p), t, "Continue")
|
||||
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) {
|
||||
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)
|
||||
assertNoError(proc.Continue(p), t, "Continue()")
|
||||
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
|
||||
when, err = p.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) {
|
||||
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'
|
||||
bp := setFunctionBreakpoint(p, t, "main.main")
|
||||
assertNoError(proc.Continue(p), t, "Continue")
|
||||
@ -278,7 +276,7 @@ func TestCheckpoints(t *testing.T) {
|
||||
func TestIssue1376(t *testing.T) {
|
||||
// Backward Continue should terminate when it encounters the start of the process.
|
||||
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")
|
||||
assertNoError(proc.Continue(p), t, "Continue (forward)")
|
||||
_, 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)
|
||||
BinInfo() *BinaryInfo
|
||||
EntryPoint() (uint64, error)
|
||||
// Common returns a struct with fields common to all backends
|
||||
Common() *CommonProcess
|
||||
|
||||
ThreadInfo
|
||||
GoroutineInfo
|
||||
@ -93,9 +91,8 @@ type GoroutineInfo interface {
|
||||
// ProcessManipulation is an interface for changing the execution state of a process.
|
||||
type ProcessManipulation interface {
|
||||
ContinueOnce() (trapthread Thread, err error)
|
||||
StepInstruction() error
|
||||
SwitchThread(int) error
|
||||
SwitchGoroutine(int) error
|
||||
SwitchGoroutine(*G) error
|
||||
RequestManualStop() error
|
||||
// CheckAndClearManualStopRequest returns true the first time it's called
|
||||
// after a call to RequestManualStop.
|
||||
@ -110,34 +107,3 @@ type BreakpointManipulation interface {
|
||||
ClearBreakpoint(addr uint64) (*Breakpoint, 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")
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Attach returns ErrNativeBackendDisabled.
|
||||
func Attach(pid int, _ []string) (*Process, error) {
|
||||
func Attach(pid int, _ []string) (*proc.Target, error) {
|
||||
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
|
||||
selectedGoroutine *proc.G
|
||||
|
||||
common proc.CommonProcess
|
||||
os *OSProcessDetails
|
||||
firstStart bool
|
||||
stopMu sync.Mutex
|
||||
@ -247,7 +246,6 @@ func (dbp *Process) ContinueOnce() (proc.Thread, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.common.ClearAllGCache()
|
||||
for _, th := range dbp.threads {
|
||||
th.CurrentBreakpoint.Clear()
|
||||
}
|
||||
@ -267,41 +265,6 @@ func (dbp *Process) ContinueOnce() (proc.Thread, error) {
|
||||
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`.
|
||||
func (dbp *Process) SwitchThread(tid int) error {
|
||||
if dbp.exited {
|
||||
@ -317,14 +280,10 @@ func (dbp *Process) SwitchThread(tid int) error {
|
||||
|
||||
// SwitchGoroutine changes from current thread to the thread
|
||||
// running the specified goroutine.
|
||||
func (dbp *Process) SwitchGoroutine(gid int) error {
|
||||
func (dbp *Process) SwitchGoroutine(g *proc.G) error {
|
||||
if dbp.exited {
|
||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||
}
|
||||
g, err := proc.FindGoroutine(dbp, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if g == nil {
|
||||
// user specified -1 and selectedGoroutine is 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())
|
||||
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
|
||||
// PT_SIGEXC on Darwin which will turn Unix signals into
|
||||
// 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
|
||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
||||
return nil, proc.ErrNotExecutable
|
||||
@ -104,7 +104,6 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.common.ClearAllGCache()
|
||||
for _, th := range dbp.threads {
|
||||
th.CurrentBreakpoint.Clear()
|
||||
}
|
||||
@ -127,11 +126,11 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dbp, err
|
||||
return proc.NewTarget(dbp), err
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
kret := C.acquire_mach_task(C.int(pid),
|
||||
@ -159,7 +158,7 @@ func Attach(pid int, _ []string) (*Process, error) {
|
||||
dbp.Detach(false)
|
||||
return nil, err
|
||||
}
|
||||
return dbp, nil
|
||||
return proc.NewTarget(dbp), nil
|
||||
}
|
||||
|
||||
// Kill kills the process.
|
||||
|
@ -43,7 +43,7 @@ type OSProcessDetails struct {
|
||||
// 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
|
||||
// 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 (
|
||||
process *exec.Cmd
|
||||
err error
|
||||
@ -60,7 +60,6 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*
|
||||
}
|
||||
|
||||
dbp := New(0)
|
||||
dbp.common = proc.NewCommonProcess(true)
|
||||
dbp.execPtraceFunc(func() {
|
||||
process = exec.Command(cmd[0])
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
return dbp, nil
|
||||
return proc.NewTarget(dbp), nil
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID. Once attached, if
|
||||
// the DWARF information cannot be found in the binary, Delve will look
|
||||
// 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.common = proc.NewCommonProcess(true)
|
||||
|
||||
var err error
|
||||
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
|
||||
@ -113,7 +111,7 @@ func Attach(pid int, debugInfoDirs []string) (*Process, error) {
|
||||
dbp.Detach(false)
|
||||
return nil, err
|
||||
}
|
||||
return dbp, nil
|
||||
return proc.NewTarget(dbp), nil
|
||||
}
|
||||
|
||||
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.
|
||||
// If the DWARF information cannot be found in the binary, Delve will look
|
||||
// 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 (
|
||||
process *exec.Cmd
|
||||
err error
|
||||
@ -65,7 +65,6 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*
|
||||
}
|
||||
|
||||
dbp := New(0)
|
||||
dbp.common = proc.NewCommonProcess(true)
|
||||
dbp.execPtraceFunc(func() {
|
||||
process = exec.Command(cmd[0])
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
return dbp, nil
|
||||
return proc.NewTarget(dbp), nil
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID. Once attached, if
|
||||
// the DWARF information cannot be found in the binary, Delve will look
|
||||
// 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.common = proc.NewCommonProcess(true)
|
||||
|
||||
var err error
|
||||
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
|
||||
@ -125,7 +123,7 @@ func Attach(pid int, debugInfoDirs []string) (*Process, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dbp, nil
|
||||
return proc.NewTarget(dbp), nil
|
||||
}
|
||||
|
||||
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.
|
||||
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])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -57,7 +57,6 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, err
|
||||
|
||||
var p *os.Process
|
||||
dbp := New(0)
|
||||
dbp.common = proc.NewCommonProcess(true)
|
||||
dbp.execPtraceFunc(func() {
|
||||
attr := &os.ProcAttr{
|
||||
Dir: wd,
|
||||
@ -80,7 +79,7 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, err
|
||||
dbp.Detach(true)
|
||||
return nil, err
|
||||
}
|
||||
return dbp, nil
|
||||
return proc.NewTarget(dbp), nil
|
||||
}
|
||||
|
||||
func initialize(dbp *Process) error {
|
||||
@ -151,7 +150,7 @@ func findExePath(pid int) (string, error) {
|
||||
}
|
||||
|
||||
// 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)
|
||||
var err error
|
||||
dbp.execPtraceFunc(func() {
|
||||
@ -169,7 +168,7 @@ func Attach(pid int, _ []string) (*Process, error) {
|
||||
dbp.Detach(true)
|
||||
return nil, err
|
||||
}
|
||||
return dbp, nil
|
||||
return proc.NewTarget(dbp), nil
|
||||
}
|
||||
|
||||
// kill kills the process.
|
||||
|
@ -145,8 +145,8 @@ func (t *Thread) SetCurrentBreakpoint(adjustPC bool) error {
|
||||
|
||||
// Breakpoint returns the current breakpoint that is active
|
||||
// on this thread.
|
||||
func (t *Thread) Breakpoint() proc.BreakpointState {
|
||||
return t.CurrentBreakpoint
|
||||
func (t *Thread) Breakpoint() *proc.BreakpointState {
|
||||
return &t.CurrentBreakpoint
|
||||
}
|
||||
|
||||
// ThreadID returns the ID of this thread.
|
||||
|
121
pkg/proc/proc.go
121
pkg/proc/proc.go
@ -2,7 +2,6 @@ package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
@ -75,8 +74,6 @@ func PostInitializationSetup(p Process, path string, debugInfoDirs []string, wri
|
||||
createUnrecoveredPanicBreakpoint(p, writeBreakpoint)
|
||||
createFatalThrowBreakpoint(p, writeBreakpoint)
|
||||
|
||||
p.Common().goroutineCache.init(p.BinInfo())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -142,7 +139,7 @@ func FindFunctionLocation(p Process, funcName string, lineOffset int) ([]uint64,
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
@ -161,7 +158,7 @@ func Next(dbp Process) (err error) {
|
||||
// Continue continues execution of the debugged
|
||||
// process. It will continue until it hits a breakpoint
|
||||
// or is otherwise stopped.
|
||||
func Continue(dbp Process) error {
|
||||
func Continue(dbp *Target) error {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -181,6 +178,7 @@ func Continue(dbp Process) error {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return nil
|
||||
}
|
||||
dbp.ClearAllGCache()
|
||||
trapthread, err := dbp.ContinueOnce()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -230,7 +228,7 @@ func Continue(dbp Process) error {
|
||||
return err
|
||||
}
|
||||
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
|
||||
if !arch.BreakInstrMovesPC() {
|
||||
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.
|
||||
// Will step into functions.
|
||||
func Step(dbp Process) (err error) {
|
||||
func Step(dbp *Target) (err error) {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -418,7 +416,7 @@ func andFrameoffCondition(cond ast.Expr, frameoff int64) ast.Expr {
|
||||
|
||||
// StepOut will continue until the current goroutine exits the
|
||||
// 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 {
|
||||
return err
|
||||
}
|
||||
@ -503,6 +501,43 @@ func StepOut(dbp Process) error {
|
||||
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
|
||||
// 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
|
||||
@ -510,14 +545,14 @@ func StepOut(dbp Process) error {
|
||||
// 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
|
||||
// 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 {
|
||||
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
|
||||
if start == 0 && (count == 0 || count >= len(dbp.Common().allGCache)) {
|
||||
return dbp.Common().allGCache, -1, nil
|
||||
if start == 0 && (count == 0 || count >= len(dbp.gcache.allGCache)) {
|
||||
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 {
|
||||
return nil, -1, err
|
||||
}
|
||||
@ -569,68 +604,18 @@ func GoroutinesInfo(dbp Process, start, count int) ([]*G, int, error) {
|
||||
if g.Status != Gdead {
|
||||
allg = append(allg, g)
|
||||
}
|
||||
dbp.Common().addGoroutine(g)
|
||||
dbp.gcache.addGoroutine(g)
|
||||
}
|
||||
if start == 0 {
|
||||
dbp.Common().allGCache = allg
|
||||
dbp.gcache.allGCache = allg
|
||||
}
|
||||
|
||||
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
|
||||
// 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) {
|
||||
// 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
|
||||
}
|
||||
|
||||
@ -696,7 +681,7 @@ func FindGoroutine(dbp Process, gid int) (*G, error) {
|
||||
// ConvertEvalScope returns a new EvalScope in the context of the
|
||||
// specified goroutine ID and stack frame.
|
||||
// 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 {
|
||||
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)
|
||||
|
||||
// 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)
|
||||
setFunctionBreakpoint(p, t, "main.main")
|
||||
assertNoError(proc.Continue(p), t, "Continue()")
|
||||
|
@ -23,7 +23,7 @@ func TestScopeWithEscapedVariable(t *testing.T) {
|
||||
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")
|
||||
|
||||
// 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)
|
||||
|
||||
withTestProcess("scopetest", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
withTestProcess("scopetest", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
for i := range scopeChecks {
|
||||
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())
|
||||
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)
|
||||
// Breakpoint will return the breakpoint that this thread is stopped at or
|
||||
// nil if the thread is not stopped at any breakpoint.
|
||||
Breakpoint() BreakpointState
|
||||
Breakpoint() *BreakpointState
|
||||
ThreadID() int
|
||||
|
||||
// Registers returns the CPU registers of this thread. The contents of the
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
|
||||
func TestGoroutineCreationLocation(t *testing.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")
|
||||
assertNoError(proc.Continue(p), t, "Continue()")
|
||||
|
||||
|
@ -37,7 +37,7 @@ type Debugger struct {
|
||||
processArgs []string
|
||||
// TODO(DO NOT MERGE WITHOUT) rename to targetMutex
|
||||
processMutex sync.Mutex
|
||||
target proc.Process
|
||||
target *proc.Target
|
||||
log *logrus.Entry
|
||||
|
||||
running bool
|
||||
@ -102,7 +102,7 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
||||
d.target = p
|
||||
|
||||
case d.config.CoreFile != "":
|
||||
var p proc.Process
|
||||
var p *proc.Target
|
||||
var err error
|
||||
switch d.config.Backend {
|
||||
case "rr":
|
||||
@ -165,7 +165,7 @@ func (d *Debugger) checkGoVersion() error {
|
||||
}
|
||||
|
||||
// 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 {
|
||||
case "native":
|
||||
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")
|
||||
|
||||
// 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 {
|
||||
case "native":
|
||||
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")
|
||||
|
||||
func betterGdbserialLaunchError(p proc.Process, err error) (proc.Process, error) {
|
||||
func betterGdbserialLaunchError(p *proc.Target, err error) (*proc.Target, error) {
|
||||
if runtime.GOOS != "darwin" {
|
||||
return p, err
|
||||
}
|
||||
@ -717,7 +717,7 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
|
||||
err = proc.Step(d.target)
|
||||
case api.StepInstruction:
|
||||
d.log.Debug("single stepping")
|
||||
err = d.target.StepInstruction()
|
||||
err = proc.StepInstruction(d.target)
|
||||
case api.ReverseStepInstruction:
|
||||
d.log.Debug("reverse single stepping")
|
||||
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() {
|
||||
d.target.Direction(proc.Forward)
|
||||
}()
|
||||
err = d.target.StepInstruction()
|
||||
err = proc.StepInstruction(d.target)
|
||||
case api.StepOut:
|
||||
d.log.Debug("step out")
|
||||
err = proc.StepOut(d.target)
|
||||
@ -736,7 +736,10 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
|
||||
withBreakpointInfo = false
|
||||
case api.SwitchGoroutine:
|
||||
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
|
||||
case api.Halt:
|
||||
// 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)
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
func evalScope(p proc.Process) (*proc.EvalScope, error) {
|
||||
func evalScope(p *proc.Target) (*proc.EvalScope, error) {
|
||||
if testBackend != "rr" {
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -99,7 +99,7 @@ func (tc *varTest) alternateVarTest() varTest {
|
||||
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())
|
||||
if err != nil {
|
||||
return err
|
||||
@ -107,16 +107,16 @@ func setVariable(p proc.Process, symbol, value string) error {
|
||||
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)
|
||||
}
|
||||
|
||||
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" {
|
||||
buildFlags |= protest.BuildModePIE
|
||||
}
|
||||
fixture := protest.BuildFixture(name, buildFlags)
|
||||
var p proc.Process
|
||||
var p *proc.Target
|
||||
var err error
|
||||
var tracedir string
|
||||
switch testBackend {
|
||||
@ -189,7 +189,7 @@ func TestVariableEvaluation(t *testing.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)
|
||||
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]"},
|
||||
}
|
||||
|
||||
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()")
|
||||
|
||||
for _, tc := range testcases {
|
||||
@ -316,7 +316,7 @@ func TestVariableEvaluationShort(t *testing.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)
|
||||
assertNoError(err, t, "Continue() returned an error")
|
||||
|
||||
@ -372,7 +372,7 @@ func TestMultilineVariableEvaluation(t *testing.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)
|
||||
assertNoError(err, t, "Continue() returned an error")
|
||||
|
||||
@ -446,7 +446,7 @@ func TestLocalVariables(t *testing.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)
|
||||
assertNoError(err, t, "Continue() returned an error")
|
||||
|
||||
@ -483,7 +483,7 @@ func TestLocalVariables(t *testing.T) {
|
||||
|
||||
func TestEmbeddedStruct(t *testing.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{
|
||||
{"b.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) {
|
||||
withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
withTestProcess("testvariables", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
err := proc.Continue(p)
|
||||
assertNoError(err, t, "Continue() returned an error")
|
||||
|
||||
@ -822,7 +822,7 @@ func TestEvalExpression(t *testing.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")
|
||||
for _, tc := range testcases {
|
||||
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
||||
@ -851,7 +851,7 @@ func TestEvalExpression(t *testing.T) {
|
||||
|
||||
func TestEvalAddrAndCast(t *testing.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")
|
||||
c1addr, err := evalVariable(p, "&c1", pnormalLoadConfig)
|
||||
assertNoError(err, t, "EvalExpression(&c1)")
|
||||
@ -878,7 +878,7 @@ func TestEvalAddrAndCast(t *testing.T) {
|
||||
|
||||
func TestMapEvaluation(t *testing.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")
|
||||
m1v, err := evalVariable(p, "m1", pnormalLoadConfig)
|
||||
assertNoError(err, t, "EvalVariable()")
|
||||
@ -920,7 +920,7 @@ func TestMapEvaluation(t *testing.T) {
|
||||
|
||||
func TestUnsafePointer(t *testing.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")
|
||||
up1v, err := evalVariable(p, "up1", pnormalLoadConfig)
|
||||
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
|
||||
// differs from the serialization used by the linker to produce DWARF type information
|
||||
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")
|
||||
for _, testcase := range testcases {
|
||||
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 {
|
||||
variable, err := evalVariable(p, tc.name, pnormalLoadConfig)
|
||||
if tc.err == nil {
|
||||
@ -1041,7 +1041,7 @@ func TestPackageRenames(t *testing.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")
|
||||
testPackageRenamesHelper(t, p, testcases)
|
||||
|
||||
@ -1075,7 +1075,7 @@ func TestConstants(t *testing.T) {
|
||||
// Not supported on 1.9 or earlier
|
||||
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")
|
||||
for _, testcase := range testcases {
|
||||
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 = filepath.Base(f)
|
||||
|
||||
@ -1104,7 +1104,7 @@ func setFunctionBreakpoint(p proc.Process, t testing.TB, fname string) *proc.Bre
|
||||
}
|
||||
|
||||
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")
|
||||
assertNoError(proc.Continue(p), t, "Continue()")
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
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 "
|
||||
|
||||
var callExpr, varExpr string
|
||||
@ -1333,7 +1333,7 @@ func testCallFunction(t *testing.T, p proc.Process, tc testCaseCallFunction) {
|
||||
|
||||
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.
|
||||
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()")
|
||||
|
||||
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 = filepath.Base(f)
|
||||
|
||||
@ -1391,7 +1391,7 @@ func setFileBreakpoint(p proc.Process, t *testing.T, fixture protest.Fixture, li
|
||||
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)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
func assertCurrentLocationFunction(p proc.Process, t *testing.T, fnname string) {
|
||||
func assertCurrentLocationFunction(p *proc.Target, t *testing.T, fnname string) {
|
||||
_, _, _, fn := currentLocation(p, t)
|
||||
if fn == nil {
|
||||
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) {
|
||||
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)
|
||||
assertNoError(proc.Continue(p), t, "Continue 1")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user