diff --git a/pkg/proc/core/core.go b/pkg/proc/core/core.go index 6e2fabf6..7ebf751c 100644 --- a/pkg/proc/core/core.go +++ b/pkg/proc/core/core.go @@ -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 diff --git a/pkg/proc/core/core_test.go b/pkg/proc/core/core_test.go index da0c1d43..b5752043 100644 --- a/pkg/proc/core/core_test.go +++ b/pkg/proc/core/core_test.go @@ -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("", "") diff --git a/pkg/proc/fncall.go b/pkg/proc/fncall.go index 8acd4d91..a45a19d5 100644 --- a/pkg/proc/fncall.go +++ b/pkg/proc/fncall.go @@ -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 } diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go index 62a01dc4..1a0f6676 100644 --- a/pkg/proc/gdbserial/gdbserver.go +++ b/pkg/proc/gdbserial/gdbserver.go @@ -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. diff --git a/pkg/proc/gdbserial/rr.go b/pkg/proc/gdbserial/rr.go index 31f30761..dc437fcd 100644 --- a/pkg/proc/gdbserial/rr.go +++ b/pkg/proc/gdbserial/rr.go @@ -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 diff --git a/pkg/proc/gdbserial/rr_test.go b/pkg/proc/gdbserial/rr_test.go index a7c68630..41407c8a 100644 --- a/pkg/proc/gdbserial/rr_test.go +++ b/pkg/proc/gdbserial/rr_test.go @@ -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) diff --git a/pkg/proc/goroutine_cache.go b/pkg/proc/goroutine_cache.go new file mode 100644 index 00000000..e0517337 --- /dev/null +++ b/pkg/proc/goroutine_cache.go @@ -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 +} diff --git a/pkg/proc/interface.go b/pkg/proc/interface.go index 951282c6..45424b3a 100644 --- a/pkg/proc/interface.go +++ b/pkg/proc/interface.go @@ -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)} -} diff --git a/pkg/proc/native/nonative_darwin.go b/pkg/proc/native/nonative_darwin.go index 690b3bc7..a8f1a7a8 100644 --- a/pkg/proc/native/nonative_darwin.go +++ b/pkg/proc/native/nonative_darwin.go @@ -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 } diff --git a/pkg/proc/native/proc.go b/pkg/proc/native/proc.go index 66133e0a..5bff11b6 100644 --- a/pkg/proc/native/proc.go +++ b/pkg/proc/native/proc.go @@ -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 -} diff --git a/pkg/proc/native/proc_darwin.go b/pkg/proc/native/proc_darwin.go index 6118dda4..cbaf91e2 100644 --- a/pkg/proc/native/proc_darwin.go +++ b/pkg/proc/native/proc_darwin.go @@ -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. diff --git a/pkg/proc/native/proc_freebsd.go b/pkg/proc/native/proc_freebsd.go index 98797df3..ad7d226a 100644 --- a/pkg/proc/native/proc_freebsd.go +++ b/pkg/proc/native/proc_freebsd.go @@ -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 { diff --git a/pkg/proc/native/proc_linux.go b/pkg/proc/native/proc_linux.go index f481c705..a45ec855 100644 --- a/pkg/proc/native/proc_linux.go +++ b/pkg/proc/native/proc_linux.go @@ -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 { diff --git a/pkg/proc/native/proc_windows.go b/pkg/proc/native/proc_windows.go index dc9667c2..5a763bcc 100644 --- a/pkg/proc/native/proc_windows.go +++ b/pkg/proc/native/proc_windows.go @@ -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. diff --git a/pkg/proc/native/threads.go b/pkg/proc/native/threads.go index a3c4f61a..aca7fcfe 100644 --- a/pkg/proc/native/threads.go +++ b/pkg/proc/native/threads.go @@ -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. diff --git a/pkg/proc/proc.go b/pkg/proc/proc.go index f42cbdac..b4258310 100644 --- a/pkg/proc/proc.go +++ b/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 } diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index 9fb3e268..05acdde2 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -52,16 +52,16 @@ func TestMain(m *testing.M) { os.Exit(protest.RunTestsWithFixtures(m)) } -func withTestProcess(name string, t testing.TB, fn func(p proc.Process, fixture protest.Fixture)) { +func withTestProcess(name string, t testing.TB, fn func(p *proc.Target, fixture protest.Fixture)) { withTestProcessArgs(name, t, ".", []string{}, 0, fn) } -func withTestProcessArgs(name string, t testing.TB, wd string, args []string, buildFlags protest.BuildFlags, fn func(p proc.Process, fixture protest.Fixture)) { +func withTestProcessArgs(name string, t testing.TB, 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 @@ -89,7 +89,7 @@ func withTestProcessArgs(name string, t testing.TB, wd string, args []string, bu fn(p, fixture) } -func getRegisters(p proc.Process, t *testing.T) proc.Registers { +func getRegisters(p *proc.Target, t *testing.T) proc.Registers { regs, err := p.CurrentThread().Registers(false) if err != nil { t.Fatal("Registers():", err) @@ -112,7 +112,7 @@ func assertNoError(err error, t testing.TB, s string) { } } -func currentPC(p proc.Process, t *testing.T) uint64 { +func currentPC(p *proc.Target, t *testing.T) uint64 { regs, err := p.CurrentThread().Registers(false) if err != nil { t.Fatal(err) @@ -121,13 +121,13 @@ func currentPC(p proc.Process, t *testing.T) uint64 { return regs.PC() } -func currentLineNumber(p proc.Process, t *testing.T) (string, int) { +func currentLineNumber(p *proc.Target, t *testing.T) (string, int) { pc := currentPC(p, t) f, l, _ := p.BinInfo().PCToLine(pc) return f, l } -func assertLineNumber(p proc.Process, t *testing.T, lineno int, descr string) (string, int) { +func assertLineNumber(p *proc.Target, t *testing.T, lineno int, descr string) (string, int) { f, l := currentLineNumber(p, t) if l != lineno { _, callerFile, callerLine, _ := runtime.Caller(1) @@ -138,7 +138,7 @@ func assertLineNumber(p proc.Process, t *testing.T, lineno int, descr string) (s func TestExit(t *testing.T) { protest.AllowRecording(t) - withTestProcess("continuetestprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("continuetestprog", t, func(p *proc.Target, fixture protest.Fixture) { err := proc.Continue(p) pe, ok := err.(proc.ErrProcessExited) if !ok { @@ -155,7 +155,7 @@ func TestExit(t *testing.T) { func TestExitAfterContinue(t *testing.T) { protest.AllowRecording(t) - withTestProcess("continuetestprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("continuetestprog", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.sayhi") assertNoError(proc.Continue(p), t, "First Continue()") err := proc.Continue(p) @@ -172,7 +172,7 @@ func TestExitAfterContinue(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) @@ -190,7 +190,7 @@ func setFunctionBreakpoint(p proc.Process, t testing.TB, fname string) *proc.Bre return bp } -func setFileBreakpoint(p proc.Process, t *testing.T, path string, lineno int) *proc.Breakpoint { +func setFileBreakpoint(p *proc.Target, t *testing.T, path string, lineno int) *proc.Breakpoint { _, f, l, _ := runtime.Caller(1) f = filepath.Base(f) @@ -208,7 +208,7 @@ func setFileBreakpoint(p proc.Process, t *testing.T, path string, lineno int) *p return bp } -func findFunctionLocation(p proc.Process, t *testing.T, fnname string) uint64 { +func findFunctionLocation(p *proc.Target, t *testing.T, fnname string) uint64 { _, f, l, _ := runtime.Caller(1) f = filepath.Base(f) addrs, err := proc.FindFunctionLocation(p, fnname, 0) @@ -221,7 +221,7 @@ func findFunctionLocation(p proc.Process, t *testing.T, fnname string) uint64 { return addrs[0] } -func findFileLocation(p proc.Process, t *testing.T, file string, lineno int) uint64 { +func findFileLocation(p *proc.Target, t *testing.T, file string, lineno int) uint64 { _, f, l, _ := runtime.Caller(1) f = filepath.Base(f) addrs, err := proc.FindFileLocation(p, file, lineno) @@ -236,10 +236,10 @@ func findFileLocation(p proc.Process, t *testing.T, file string, lineno int) uin func TestHalt(t *testing.T) { stopChan := make(chan interface{}, 1) - withTestProcess("loopprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("loopprog", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.loop") assertNoError(proc.Continue(p), t, "Continue") - if p, ok := p.(*native.Process); ok { + if p, ok := p.Process.(*native.Process); ok { for _, th := range p.ThreadList() { _, err := th.Registers(false) assertNoError(err, t, "Registers") @@ -262,7 +262,7 @@ func TestHalt(t *testing.T) { // Loop through threads and make sure they are all // actually stopped, err will not be nil if the process // is still running. - if p, ok := p.(*native.Process); ok { + if p, ok := p.Process.(*native.Process); ok { for _, th := range p.ThreadList() { if th, ok := th.(*native.Thread); ok { if !th.Stopped() { @@ -278,7 +278,7 @@ func TestHalt(t *testing.T) { func TestStep(t *testing.T) { protest.AllowRecording(t) - withTestProcess("testprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testprog", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.helloworld") assertNoError(proc.Continue(p), t, "Continue()") @@ -297,7 +297,7 @@ func TestStep(t *testing.T) { func TestBreakpoint(t *testing.T) { protest.AllowRecording(t) - withTestProcess("testprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testprog", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFunctionBreakpoint(p, t, "main.helloworld") assertNoError(proc.Continue(p), t, "Continue()") @@ -318,7 +318,7 @@ func TestBreakpoint(t *testing.T) { func TestBreakpointInSeparateGoRoutine(t *testing.T) { protest.AllowRecording(t) - withTestProcess("testthreads", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testthreads", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.anotherthread") assertNoError(proc.Continue(p), t, "Continue") @@ -335,7 +335,7 @@ func TestBreakpointInSeparateGoRoutine(t *testing.T) { } func TestBreakpointWithNonExistantFunction(t *testing.T) { - withTestProcess("testprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testprog", t, func(p *proc.Target, fixture protest.Fixture) { _, err := p.SetBreakpoint(0, proc.UserBreakpoint, nil) if err == nil { t.Fatal("Should not be able to break at non existant function") @@ -344,7 +344,7 @@ func TestBreakpointWithNonExistantFunction(t *testing.T) { } func TestClearBreakpointBreakpoint(t *testing.T) { - withTestProcess("testprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testprog", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFunctionBreakpoint(p, t, "main.sleepytime") _, err := p.ClearBreakpoint(bp.Addr) @@ -368,7 +368,7 @@ type nextTest struct { begin, end int } -func countBreakpoints(p proc.Process) int { +func countBreakpoints(p *proc.Target) int { bpcount := 0 for _, bp := range p.Breakpoints().M { if bp.LogicalID >= 0 { @@ -414,7 +414,7 @@ func testseq2(t *testing.T, program string, initialLocation string, testcases [] func testseq2Args(wd string, args []string, buildFlags protest.BuildFlags, t *testing.T, program string, initialLocation string, testcases []seqTest) { protest.AllowRecording(t) - withTestProcessArgs(program, t, wd, args, buildFlags, func(p proc.Process, fixture protest.Fixture) { + withTestProcessArgs(program, t, wd, args, buildFlags, func(p *proc.Target, fixture protest.Fixture) { var bp *proc.Breakpoint if initialLocation != "" { bp = setFunctionBreakpoint(p, t, initialLocation) @@ -546,7 +546,7 @@ func TestNextConcurrent(t *testing.T) { {10, 11}, } protest.AllowRecording(t) - withTestProcess("parallel_next", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFunctionBreakpoint(p, t, "main.sayhi") assertNoError(proc.Continue(p), t, "Continue") f, ln := currentLineNumber(p, t) @@ -585,7 +585,7 @@ func TestNextConcurrentVariant2(t *testing.T) { {10, 11}, } protest.AllowRecording(t) - withTestProcess("parallel_next", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.sayhi") assertNoError(proc.Continue(p), t, "Continue") f, ln := currentLineNumber(p, t) @@ -663,7 +663,7 @@ func TestNextNetHTTP(t *testing.T) { {11, 12}, {12, 13}, } - withTestProcess("testnextnethttp", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testnextnethttp", t, func(p *proc.Target, fixture protest.Fixture) { go func() { // Wait for program to start listening. for { @@ -693,7 +693,7 @@ func TestNextNetHTTP(t *testing.T) { } func TestRuntimeBreakpoint(t *testing.T) { - withTestProcess("testruntimebreakpoint", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testruntimebreakpoint", t, func(p *proc.Target, fixture protest.Fixture) { err := proc.Continue(p) if err != nil { t.Fatal(err) @@ -721,7 +721,7 @@ func returnAddress(thread proc.Thread) (uint64, error) { func TestFindReturnAddress(t *testing.T) { protest.AllowRecording(t) - withTestProcess("testnextprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testnextprog", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 24) err := proc.Continue(p) if err != nil { @@ -740,7 +740,7 @@ func TestFindReturnAddress(t *testing.T) { func TestFindReturnAddressTopOfStackFn(t *testing.T) { protest.AllowRecording(t) - withTestProcess("testreturnaddress", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testreturnaddress", t, func(p *proc.Target, fixture protest.Fixture) { fnName := "runtime.rt0_go" setFunctionBreakpoint(p, t, fnName) if err := proc.Continue(p); err != nil { @@ -754,7 +754,7 @@ func TestFindReturnAddressTopOfStackFn(t *testing.T) { func TestSwitchThread(t *testing.T) { protest.AllowRecording(t) - withTestProcess("testnextprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testnextprog", t, func(p *proc.Target, fixture protest.Fixture) { // With invalid thread id err := p.SwitchThread(-1) if err == nil { @@ -798,7 +798,7 @@ func TestCGONext(t *testing.T) { } protest.AllowRecording(t) - withTestProcess("cgotest", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("cgotest", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.main") assertNoError(proc.Continue(p), t, "Continue()") assertNoError(proc.Next(p), t, "Next()") @@ -825,7 +825,7 @@ func TestStacktrace(t *testing.T) { {{4, "main.stacktraceme"}, {8, "main.func1"}, {12, "main.func2"}, {17, "main.main"}}, } protest.AllowRecording(t) - withTestProcess("stacktraceprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("stacktraceprog", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFunctionBreakpoint(p, t, "main.stacktraceme") for i := range stacks { @@ -855,7 +855,7 @@ func TestStacktrace(t *testing.T) { } func TestStacktrace2(t *testing.T) { - withTestProcess("retstack", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("retstack", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue()") locations, err := proc.ThreadStacktrace(p.CurrentThread(), 40) @@ -914,7 +914,7 @@ func TestStacktraceGoroutine(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.stacktraceme") assertNoError(proc.Continue(p), t, "Continue()") @@ -976,7 +976,7 @@ func TestKill(t *testing.T) { // k command presumably works but leaves the process around? return } - withTestProcess("testprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testprog", t, func(p *proc.Target, fixture protest.Fixture) { if err := p.Detach(true); err != nil { t.Fatal(err) } @@ -996,7 +996,7 @@ func TestKill(t *testing.T) { }) } -func testGSupportFunc(name string, t *testing.T, p proc.Process, fixture protest.Fixture) { +func testGSupportFunc(name string, t *testing.T, p *proc.Target, fixture protest.Fixture) { bp := setFunctionBreakpoint(p, t, "main.main") assertNoError(proc.Continue(p), t, name+": Continue()") @@ -1014,7 +1014,7 @@ func testGSupportFunc(name string, t *testing.T, p proc.Process, fixture protest } func TestGetG(t *testing.T) { - withTestProcess("testprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testprog", t, func(p *proc.Target, fixture protest.Fixture) { testGSupportFunc("nocgo", t, p, fixture) }) @@ -1027,14 +1027,14 @@ func TestGetG(t *testing.T) { } protest.AllowRecording(t) - withTestProcess("cgotest", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("cgotest", t, func(p *proc.Target, fixture protest.Fixture) { testGSupportFunc("cgo", t, p, fixture) }) } func TestContinueMulti(t *testing.T) { protest.AllowRecording(t) - withTestProcess("integrationprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("integrationprog", t, func(p *proc.Target, fixture protest.Fixture) { bp1 := setFunctionBreakpoint(p, t, "main.main") bp2 := setFunctionBreakpoint(p, t, "main.sayhi") @@ -1072,7 +1072,7 @@ func TestBreakpointOnFunctionEntry(t *testing.T) { func TestProcessReceivesSIGCHLD(t *testing.T) { protest.AllowRecording(t) - withTestProcess("sigchldprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("sigchldprog", t, func(p *proc.Target, fixture protest.Fixture) { err := proc.Continue(p) _, ok := err.(proc.ErrProcessExited) if !ok { @@ -1082,13 +1082,13 @@ func TestProcessReceivesSIGCHLD(t *testing.T) { } func TestIssue239(t *testing.T) { - withTestProcess("is sue239", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("is sue239", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 17) assertNoError(proc.Continue(p), t, fmt.Sprintf("Continue()")) }) } -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 @@ -1102,7 +1102,7 @@ func findFirstNonRuntimeFrame(p proc.Process) (proc.Stackframe, error) { return proc.Stackframe{}, fmt.Errorf("non-runtime frame not found") } -func evalVariableOrError(p proc.Process, symbol string) (*proc.Variable, error) { +func evalVariableOrError(p *proc.Target, symbol string) (*proc.Variable, error) { var scope *proc.EvalScope var err error @@ -1122,7 +1122,7 @@ func evalVariableOrError(p proc.Process, symbol string) (*proc.Variable, error) return scope.EvalVariable(symbol, normalLoadConfig) } -func evalVariable(p proc.Process, t testing.TB, symbol string) *proc.Variable { +func evalVariable(p *proc.Target, t testing.TB, symbol string) *proc.Variable { v, err := evalVariableOrError(p, symbol) if err != nil { _, file, line, _ := runtime.Caller(1) @@ -1132,7 +1132,7 @@ func evalVariable(p proc.Process, t testing.TB, symbol string) *proc.Variable { return v } -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 @@ -1177,7 +1177,7 @@ func TestVariableEvaluation(t *testing.T) { {"ba", reflect.Slice, nil, 200, 200, 64}, } - withTestProcess("testvariables", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testvariables", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue() returned an error") for _, tc := range testcases { @@ -1227,7 +1227,7 @@ func TestVariableEvaluation(t *testing.T) { func TestFrameEvaluation(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) { setFunctionBreakpoint(p, t, "main.stacktraceme") assertNoError(proc.Continue(p), t, "Continue()") @@ -1297,7 +1297,7 @@ func TestFrameEvaluation(t *testing.T) { } func TestPointerSetting(t *testing.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") pval := func(n int64) { @@ -1325,7 +1325,7 @@ func TestPointerSetting(t *testing.T) { } func TestVariableFunctionScoping(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") @@ -1347,7 +1347,7 @@ func TestVariableFunctionScoping(t *testing.T) { func TestRecursiveStructure(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()") v := evalVariable(p, t, "aas") t.Logf("v: %v\n", v) @@ -1357,7 +1357,7 @@ func TestRecursiveStructure(t *testing.T) { func TestIssue316(t *testing.T) { // A pointer loop that includes one interface should not send dlv into an infinite loop 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()") evalVariable(p, t, "iface5") }) @@ -1366,7 +1366,7 @@ func TestIssue316(t *testing.T) { func TestIssue325(t *testing.T) { // nil pointer dereference when evaluating interfaces to function pointers 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()") iface2fn1v := evalVariable(p, t, "iface2fn1") t.Logf("iface2fn1: %v\n", iface2fn1v) @@ -1381,7 +1381,7 @@ func TestBreakpointCounts(t *testing.T) { t.Skip("test is not valid on FreeBSD") } protest.AllowRecording(t) - withTestProcess("bpcountstest", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("bpcountstest", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture.Source, 12) for { @@ -1414,7 +1414,7 @@ func BenchmarkArray(b *testing.B) { // each bencharr struct is 128 bytes, bencharr is 64 elements long protest.AllowRecording(b) b.SetBytes(int64(64 * 128)) - withTestProcess("testvariables2", b, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testvariables2", b, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), b, "Continue()") for i := 0; i < b.N; i++ { evalVariable(p, b, "bencharr") @@ -1430,7 +1430,7 @@ func TestBreakpointCountsWithDetection(t *testing.T) { } m := map[int64]int64{} protest.AllowRecording(t) - withTestProcess("bpcountstest", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("bpcountstest", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture.Source, 12) for { @@ -1487,7 +1487,7 @@ func BenchmarkArrayPointer(b *testing.B) { // each read will read 64 bencharr structs plus the 64 pointers of benchparr protest.AllowRecording(b) b.SetBytes(int64(64*128 + 64*8)) - withTestProcess("testvariables2", b, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testvariables2", b, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), b, "Continue()") for i := 0; i < b.N; i++ { evalVariable(p, b, "bencharr") @@ -1501,7 +1501,7 @@ func BenchmarkMap(b *testing.B) { // reading strings and the map structure imposes a overhead that we ignore here protest.AllowRecording(b) b.SetBytes(int64(41 * (2*8 + 9))) - withTestProcess("testvariables2", b, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testvariables2", b, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), b, "Continue()") for i := 0; i < b.N; i++ { evalVariable(p, b, "m1") @@ -1511,10 +1511,10 @@ func BenchmarkMap(b *testing.B) { func BenchmarkGoroutinesInfo(b *testing.B) { protest.AllowRecording(b) - withTestProcess("testvariables2", b, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testvariables2", b, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), b, "Continue()") for i := 0; i < b.N; i++ { - p.Common().ClearAllGCache() + p.ClearAllGCache() _, _, err := proc.GoroutinesInfo(p, 0, 0) assertNoError(err, b, "GoroutinesInfo") } @@ -1524,7 +1524,7 @@ func BenchmarkGoroutinesInfo(b *testing.B) { func TestIssue262(t *testing.T) { // Continue does not work when the current breakpoint is set on a NOP instruction protest.AllowRecording(t) - withTestProcess("issue262", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue262", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 11) assertNoError(proc.Continue(p), t, "Continue()") @@ -1544,7 +1544,7 @@ func TestIssue305(t *testing.T) { // the internal breakpoints aren't cleared preventing further use of // 'next' command protest.AllowRecording(t) - withTestProcess("issue305", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue305", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 5) assertNoError(proc.Continue(p), t, "Continue()") @@ -1561,7 +1561,7 @@ func TestPointerLoops(t *testing.T) { // Pointer loops through map entries, pointers and slices // Regression test for issue #341 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()") for _, expr := range []string{"mapinf", "ptrinf", "sliceinf"} { t.Logf("requesting %s", expr) @@ -1573,7 +1573,7 @@ func TestPointerLoops(t *testing.T) { func BenchmarkLocalVariables(b *testing.B) { protest.AllowRecording(b) - withTestProcess("testvariables", b, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testvariables", b, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), b, "Continue() returned an error") scope, err := proc.GoroutineScope(p.CurrentThread()) assertNoError(err, b, "Scope()") @@ -1589,7 +1589,7 @@ func TestCondBreakpoint(t *testing.T) { t.Skip("test is not valid on FreeBSD") } protest.AllowRecording(t) - withTestProcess("parallel_next", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture.Source, 9) bp.Cond = &ast.BinaryExpr{ Op: token.EQL, @@ -1613,7 +1613,7 @@ func TestCondBreakpointError(t *testing.T) { t.Skip("test is not valid on FreeBSD") } protest.AllowRecording(t) - withTestProcess("parallel_next", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture.Source, 9) bp.Cond = &ast.BinaryExpr{ Op: token.EQL, @@ -1655,7 +1655,7 @@ func TestCondBreakpointError(t *testing.T) { func TestIssue356(t *testing.T) { // slice with a typedef does not get printed correctly 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") mmvar := evalVariable(p, t, "mainMenu") if mmvar.Kind != reflect.Slice { @@ -1665,7 +1665,7 @@ func TestIssue356(t *testing.T) { } func TestStepIntoFunction(t *testing.T) { - withTestProcess("teststep", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("teststep", t, func(p *proc.Target, fixture protest.Fixture) { // Continue until breakpoint assertNoError(proc.Continue(p), t, "Continue() returned an error") // Step into function @@ -1698,7 +1698,7 @@ func TestIssue384(t *testing.T) { } protest.AllowRecording(t) - withTestProcess("issue384", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue384", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 13) assertNoError(proc.Continue(p), t, "Continue()") evalVariable(p, t, "st") @@ -1708,7 +1708,7 @@ func TestIssue384(t *testing.T) { func TestIssue332_Part1(t *testing.T) { // Next shouldn't step inside a function call protest.AllowRecording(t) - withTestProcess("issue332", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue332", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 8) assertNoError(proc.Continue(p), t, "Continue()") assertNoError(proc.Next(p), t, "first Next()") @@ -1732,7 +1732,7 @@ func TestIssue332_Part2(t *testing.T) { // which leads to 'next' and 'stack' failing with error "could not find FDE for PC: " // because the incorrect FDE data leads to reading the wrong stack address as the return address protest.AllowRecording(t) - withTestProcess("issue332", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue332", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 8) assertNoError(proc.Continue(p), t, "Continue()") @@ -1776,7 +1776,7 @@ func TestIssue396(t *testing.T) { // https://go-review.googlesource.com/c/go/+/161337 t.Skip("no autogenerated init function in Go 1.13 or later") } - withTestProcess("callme", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("callme", t, func(p *proc.Target, fixture protest.Fixture) { findFunctionLocation(p, t, "main.init") }) } @@ -1784,7 +1784,7 @@ func TestIssue396(t *testing.T) { func TestIssue414(t *testing.T) { // Stepping until the program exits protest.AllowRecording(t) - withTestProcess("math", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("math", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 9) assertNoError(proc.Continue(p), t, "Continue()") for { @@ -1801,7 +1801,7 @@ func TestIssue414(t *testing.T) { func TestPackageVariables(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()") scope, err := proc.GoroutineScope(p.CurrentThread()) @@ -1827,14 +1827,14 @@ func TestIssue149(t *testing.T) { return } // setting breakpoint on break statement - withTestProcess("break", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("break", t, func(p *proc.Target, fixture protest.Fixture) { findFileLocation(p, t, fixture.Source, 8) }) } func TestPanicBreakpoint(t *testing.T) { protest.AllowRecording(t) - withTestProcess("panic", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("panic", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue()") bp := p.CurrentThread().Breakpoint() if bp.Breakpoint == nil || bp.Name != proc.UnrecoveredPanic { @@ -1844,7 +1844,7 @@ func TestPanicBreakpoint(t *testing.T) { } func TestCmdLineArgs(t *testing.T) { - expectSuccess := func(p proc.Process, fixture protest.Fixture) { + expectSuccess := func(p *proc.Target, fixture protest.Fixture) { err := proc.Continue(p) bp := p.CurrentThread().Breakpoint() if bp.Breakpoint != nil && bp.Name == proc.UnrecoveredPanic { @@ -1860,7 +1860,7 @@ func TestCmdLineArgs(t *testing.T) { } } - expectPanic := func(p proc.Process, fixture protest.Fixture) { + expectPanic := func(p *proc.Target, fixture protest.Fixture) { proc.Continue(p) bp := p.CurrentThread().Breakpoint() if bp.Breakpoint == nil || bp.Name != proc.UnrecoveredPanic { @@ -1888,7 +1888,7 @@ func TestIssue462(t *testing.T) { if runtime.GOOS == "windows" { return } - withTestProcess("testnextnethttp", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testnextnethttp", t, func(p *proc.Target, fixture protest.Fixture) { go func() { // Wait for program to start listening. for { @@ -1914,7 +1914,7 @@ func TestNextParked(t *testing.T) { t.Skip("test is not valid on FreeBSD") } protest.AllowRecording(t) - withTestProcess("parallel_next", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFunctionBreakpoint(p, t, "main.sayhi") // continue until a parked goroutine exists @@ -1952,7 +1952,7 @@ func TestNextParked(t *testing.T) { } } - assertNoError(p.SwitchGoroutine(parkedg.ID), t, "SwitchGoroutine()") + assertNoError(p.SwitchGoroutine(parkedg), t, "SwitchGoroutine()") p.ClearBreakpoint(bp.Addr) assertNoError(proc.Next(p), t, "Next()") @@ -1967,7 +1967,7 @@ func TestStepParked(t *testing.T) { t.Skip("test is not valid on FreeBSD") } protest.AllowRecording(t) - withTestProcess("parallel_next", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFunctionBreakpoint(p, t, "main.sayhi") // continue until a parked goroutine exists @@ -2002,7 +2002,7 @@ func TestStepParked(t *testing.T) { t.Logf("\t%s:%d in %s (%#x)", frame.Call.File, frame.Call.Line, name, frame.Current.PC) } - assertNoError(p.SwitchGoroutine(parkedg.ID), t, "SwitchGoroutine()") + assertNoError(p.SwitchGoroutine(parkedg), t, "SwitchGoroutine()") p.ClearBreakpoint(bp.Addr) assertNoError(proc.Step(p), t, "Step()") @@ -2062,7 +2062,7 @@ func TestUnsupportedArch(t *testing.T) { } defer os.Remove(outfile) - var p proc.Process + var p *proc.Target switch testBackend { case "native": @@ -2088,7 +2088,7 @@ func TestIssue573(t *testing.T) { // calls to runtime.duffzero and runtime.duffcopy jump directly into the middle // of the function and the internal breakpoint set by StepInto may be missed. protest.AllowRecording(t) - withTestProcess("issue573", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue573", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.foo") assertNoError(proc.Continue(p), t, "Continue()") assertNoError(proc.Step(p), t, "Step() #1") @@ -2098,7 +2098,7 @@ func TestIssue573(t *testing.T) { } func TestTestvariables2Prologue(t *testing.T) { - withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testvariables2", t, func(p *proc.Target, fixture protest.Fixture) { addrEntry := p.BinInfo().LookupFunc["main.main"].Entry addrPrologue := findFunctionLocation(p, t, "main.main") if addrEntry == addrPrologue { @@ -2270,7 +2270,7 @@ func TestIssue561(t *testing.T) { // Step fails to make progress when PC is at a CALL instruction // where a breakpoint is also set. protest.AllowRecording(t) - withTestProcess("issue561", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue561", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 10) assertNoError(proc.Continue(p), t, "Continue()") assertNoError(proc.Step(p), t, "Step()") @@ -2279,7 +2279,7 @@ func TestIssue561(t *testing.T) { } func TestGoroutineLables(t *testing.T) { - withTestProcess("goroutineLabels", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("goroutineLabels", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue()") g, err := proc.GetG(p.CurrentThread()) assertNoError(err, t, "GetG()") @@ -2309,7 +2309,7 @@ func TestStepConcurrentDirect(t *testing.T) { t.Skip("test is not valid on FreeBSD") } protest.AllowRecording(t) - withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture.Source, 37) assertNoError(proc.Continue(p), t, "Continue()") @@ -2375,7 +2375,7 @@ func TestStepConcurrentPtr(t *testing.T) { t.Skip("test is not valid on FreeBSD") } protest.AllowRecording(t) - withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 24) for _, b := range p.Breakpoints().M { @@ -2447,7 +2447,7 @@ func TestStepConcurrentPtr(t *testing.T) { func TestStepOutDefer(t *testing.T) { protest.AllowRecording(t) - withTestProcess("testnextdefer", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testnextdefer", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture.Source, 9) assertNoError(proc.Continue(p), t, "Continue()") p.ClearBreakpoint(bp.Addr) @@ -2476,7 +2476,7 @@ var maxInstructionLength uint64 func TestStepOnCallPtrInstr(t *testing.T) { protest.AllowRecording(t) - withTestProcess("teststepprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("teststepprog", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 10) assertNoError(proc.Continue(p), t, "Continue()") @@ -2497,7 +2497,7 @@ func TestStepOnCallPtrInstr(t *testing.T) { found = true break } - assertNoError(p.StepInstruction(), t, "StepInstruction()") + assertNoError(proc.StepInstruction(p), t, "StepInstruction()") } if !found { @@ -2528,7 +2528,7 @@ func TestIssue594(t *testing.T) { // In particular the target should be able to cause a nil pointer // dereference panic and recover from it. protest.AllowRecording(t) - withTestProcess("issue594", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue594", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue()") var f string var ln int @@ -2567,7 +2567,7 @@ func TestWorkDir(t *testing.T) { wd = "/private/tmp" } protest.AllowRecording(t) - withTestProcessArgs("workdir", t, wd, []string{}, 0, func(p proc.Process, fixture protest.Fixture) { + withTestProcessArgs("workdir", t, wd, []string{}, 0, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 14) proc.Continue(p) v := evalVariable(p, t, "pwd") @@ -2589,7 +2589,7 @@ func TestNegativeIntEvaluation(t *testing.T) { {"ni32", "int32", int64(-5)}, } 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()") for _, tc := range testcases { v := evalVariable(p, t, tc.name) @@ -2606,7 +2606,7 @@ func TestNegativeIntEvaluation(t *testing.T) { func TestIssue683(t *testing.T) { // Step panics when source file can not be found protest.AllowRecording(t) - withTestProcess("issue683", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue683", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.main") assertNoError(proc.Continue(p), t, "First Continue()") for i := 0; i < 20; i++ { @@ -2622,7 +2622,7 @@ func TestIssue683(t *testing.T) { func TestIssue664(t *testing.T) { protest.AllowRecording(t) - withTestProcess("issue664", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue664", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 4) assertNoError(proc.Continue(p), t, "Continue()") assertNoError(proc.Next(p), t, "Next()") @@ -2633,7 +2633,7 @@ func TestIssue664(t *testing.T) { // Benchmarks (*Processs).Continue + (*Scope).FunctionArguments func BenchmarkTrace(b *testing.B) { protest.AllowRecording(b) - withTestProcess("traceperf", b, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("traceperf", b, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, b, "main.PerfCheck") b.ResetTimer() for i := 0; i < b.N; i++ { @@ -2653,7 +2653,7 @@ func TestNextInDeferReturn(t *testing.T) { // field being nil. // We need to deal with this without panicing. protest.AllowRecording(t) - withTestProcess("defercall", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("defercall", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "runtime.deferreturn") assertNoError(proc.Continue(p), t, "First Continue()") @@ -2699,7 +2699,7 @@ func TestStacktraceWithBarriers(t *testing.T) { defer os.Setenv("GODEBUG", godebugOld) os.Setenv("GODEBUG", "gcrescanstacks=1") - withTestProcess("binarytrees", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("binarytrees", t, func(p *proc.Target, fixture protest.Fixture) { // We want to get a user goroutine with a stack barrier, to get that we execute the program until runtime.gcInstallStackBarrier is executed AND the goroutine it was executed onto contains a call to main.bottomUpTree setFunctionBreakpoint(p, t, "runtime.gcInstallStackBarrier") stackBarrierGoids := []int{} @@ -2815,7 +2815,7 @@ func TestAttachDetach(t *testing.T) { } } - var p proc.Process + var p *proc.Target var err error switch testBackend { @@ -2855,7 +2855,7 @@ func TestAttachDetach(t *testing.T) { func TestVarSum(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()") sumvar := evalVariable(p, t, "s1[0] + s1[1]") sumvarstr := constant.StringVal(sumvar.Value) @@ -2870,7 +2870,7 @@ func TestVarSum(t *testing.T) { func TestPackageWithPathVar(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()") evalVariable(p, t, "pkg.SomeVar") evalVariable(p, t, "pkg.SomeVar.X") @@ -2880,7 +2880,7 @@ func TestPackageWithPathVar(t *testing.T) { func TestEnvironment(t *testing.T) { protest.AllowRecording(t) os.Setenv("SOMEVAR", "bah") - withTestProcess("testenv", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testenv", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue()") v := evalVariable(p, t, "x") vv := constant.StringVal(v.Value) @@ -2891,7 +2891,7 @@ func TestEnvironment(t *testing.T) { }) } -func getFrameOff(p proc.Process, t *testing.T) int64 { +func getFrameOff(p *proc.Target, t *testing.T) int64 { frameoffvar := evalVariable(p, t, "runtime.frameoff") frameoff, _ := constant.Int64Val(frameoffvar.Value) return frameoff @@ -2907,7 +2907,7 @@ func TestRecursiveNext(t *testing.T) { } testseq("increment", contNext, testcases, "main.Increment", t) - withTestProcess("increment", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("increment", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFunctionBreakpoint(p, t, "main.Increment") assertNoError(proc.Continue(p), t, "Continue") _, err := p.ClearBreakpoint(bp.Addr) @@ -2946,7 +2946,7 @@ func TestIssue877(t *testing.T) { } const envval = "/usr/local/lib" os.Setenv("DYLD_LIBRARY_PATH", envval) - withTestProcess("issue877", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue877", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue()") v := evalVariable(p, t, "dyldenv") vv := constant.StringVal(v.Value) @@ -2962,7 +2962,7 @@ func TestIssue893(t *testing.T) { // executable, acceptable behaviors are: (a) no error, (b) no source at PC // error, (c) program runs to completion protest.AllowRecording(t) - withTestProcess("increment", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("increment", t, func(p *proc.Target, fixture protest.Fixture) { err := proc.Next(p) if err == nil { return @@ -2985,16 +2985,16 @@ func TestIssue893(t *testing.T) { func TestStepInstructionNoGoroutine(t *testing.T) { protest.AllowRecording(t) - withTestProcess("increment", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("increment", t, func(p *proc.Target, fixture protest.Fixture) { // Call StepInstruction immediately after launching the program, it should // work even though no goroutine is selected. - assertNoError(p.StepInstruction(), t, "StepInstruction") + assertNoError(proc.StepInstruction(p), t, "StepInstruction") }) } func TestIssue871(t *testing.T) { protest.AllowRecording(t) - withTestProcess("issue871", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue871", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue") var scope *proc.EvalScope @@ -3042,7 +3042,7 @@ func TestShadowedFlag(t *testing.T) { if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) { return } - withTestProcess("testshadow", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testshadow", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue") scope, err := proc.GoroutineScope(p.CurrentThread()) assertNoError(err, t, "GoroutineScope") @@ -3116,7 +3116,7 @@ func TestAttachStripped(t *testing.T) { } } - var p proc.Process + var p *proc.Target var err error switch testBackend { @@ -3146,7 +3146,7 @@ func TestAttachStripped(t *testing.T) { func TestIssue844(t *testing.T) { // Conditional breakpoints should not prevent next from working if their // condition isn't met. - withTestProcess("nextcond", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("nextcond", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 9) condbp := setFileBreakpoint(p, t, fixture.Source, 10) condbp.Cond = &ast.BinaryExpr{ @@ -3283,7 +3283,7 @@ func TestCgoStacktrace(t *testing.T) { frameOffs := map[string]int64{} framePointerOffs := map[string]int64{} - withTestProcess("cgostacktest/", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("cgostacktest/", t, func(p *proc.Target, fixture protest.Fixture) { for itidx, tc := range testCases { assertNoError(proc.Continue(p), t, fmt.Sprintf("Continue at iteration step %d", itidx)) @@ -3360,7 +3360,7 @@ func TestCgoSources(t *testing.T) { } } - withTestProcess("cgostacktest/", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("cgostacktest/", t, func(p *proc.Target, fixture protest.Fixture) { sources := p.BinInfo().Sources for _, needle := range []string{"main.go", "hello.c"} { found := false @@ -3379,7 +3379,7 @@ func TestCgoSources(t *testing.T) { func TestSystemstackStacktrace(t *testing.T) { // check that we can follow a stack switch initiated by runtime.systemstack() - withTestProcess("panic", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("panic", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "runtime.startpanic_m") assertNoError(proc.Continue(p), t, "first continue") assertNoError(proc.Continue(p), t, "second continue") @@ -3401,7 +3401,7 @@ func TestSystemstackOnRuntimeNewstack(t *testing.T) { // - try to look at stacktraces of other goroutines // If one of the other goroutines is resizing its own stack the stack // command won't work for it. - withTestProcess("binarytrees", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("binarytrees", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.main") assertNoError(proc.Continue(p), t, "first continue") @@ -3431,7 +3431,7 @@ func TestSystemstackOnRuntimeNewstack(t *testing.T) { func TestIssue1034(t *testing.T) { // The external linker on macOS produces an abbrev for DW_TAG_subprogram // without the "has children" flag, we should support this. - withTestProcess("cgostacktest/", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("cgostacktest/", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.main") assertNoError(proc.Continue(p), t, "Continue()") frames, err := p.SelectedGoroutine().Stacktrace(10, 0) @@ -3448,7 +3448,7 @@ func TestIssue1034(t *testing.T) { func TestIssue1008(t *testing.T) { // The external linker on macOS inserts "end of sequence" extended opcodes // in debug_line. which we should support correctly. - withTestProcess("cgostacktest/", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("cgostacktest/", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.main") assertNoError(proc.Continue(p), t, "Continue()") loc, err := p.CurrentThread().Location() @@ -3469,7 +3469,7 @@ func TestDeclLine(t *testing.T) { t.Skip("go 1.9 and prior versions do not emit DW_AT_decl_line") } - withTestProcess("decllinetest", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("decllinetest", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue") scope, err := proc.GoroutineScope(p.CurrentThread()) assertNoError(err, t, "GoroutineScope (1)") @@ -3492,7 +3492,7 @@ func TestDeclLine(t *testing.T) { } func TestIssue1137(t *testing.T) { - withTestProcess("dotpackagesiface", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("dotpackagesiface", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue()") v := evalVariable(p, t, "iface") assertNoError(v.Unreadable, t, "iface unreadable") @@ -3518,7 +3518,7 @@ func TestIssue1101(t *testing.T) { // close proximity to main.main calling os.Exit() and causing the death of // the thread group leader. - withTestProcess("issue1101", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue1101", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.f") assertNoError(proc.Continue(p), t, "Continue()") assertNoError(proc.Next(p), t, "Next() 1") @@ -3543,7 +3543,7 @@ func TestIssue1101(t *testing.T) { } func TestIssue1145(t *testing.T) { - withTestProcess("sleep", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("sleep", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 18) assertNoError(proc.Continue(p), t, "Continue()") resumeChan := make(chan struct{}, 1) @@ -3565,7 +3565,7 @@ func TestDisassembleGlobalVars(t *testing.T) { if runtime.GOARCH == "arm64" { t.Skip("On ARM64 symLookup can't look up variables due to how they are loaded, see issue #1778") } - withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) { mainfn := p.BinInfo().LookupFunc["main.main"] regs, _ := p.CurrentThread().Registers(false) text, err := proc.Disassemble(p.CurrentThread(), regs, p.Breakpoints(), p.BinInfo(), mainfn.Entry, mainfn.End) @@ -3607,7 +3607,7 @@ func TestAllPCsForFileLines(t *testing.T) { // Versions of go before 1.10 do not have DWARF information for inlined calls t.Skip("inlining not supported") } - withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining, func(p proc.Process, fixture protest.Fixture) { + withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining, func(p *proc.Target, fixture protest.Fixture) { l2pcs := p.BinInfo().AllPCsForFileLines(fixture.Source, []int{7, 20}) if len(l2pcs) != 2 { t.Fatalf("expected two map entries for %s:{%d,%d} (got %d: %v)", fixture.Source, 7, 20, len(l2pcs), l2pcs) @@ -3671,7 +3671,7 @@ func TestInlinedStacktraceAndVariables(t *testing.T) { }, } - withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining, func(p proc.Process, fixture protest.Fixture) { + withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining, func(p *proc.Target, fixture protest.Fixture) { pcs, err := p.BinInfo().LineToPC(fixture.Source, 7) assertNoError(err, t, "LineToPC") if len(pcs) < 2 { @@ -3800,7 +3800,7 @@ func TestInlineFunctionList(t *testing.T) { // Versions of go before 1.10 do not have DWARF information for inlined calls t.Skip("inlining not supported") } - withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p proc.Process, fixture protest.Fixture) { + withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p *proc.Target, fixture protest.Fixture) { var found bool for _, fn := range p.BinInfo().Functions { if strings.Contains(fn.Name, "inlineThis") { @@ -3820,7 +3820,7 @@ func TestInlineBreakpoint(t *testing.T) { // Versions of go before 1.10 do not have DWARF information for inlined calls t.Skip("inlining not supported") } - withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p proc.Process, fixture protest.Fixture) { + withTestProcessArgs("testinline", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p *proc.Target, fixture protest.Fixture) { pcs, err := p.BinInfo().LineToPC(fixture.Source, 17) t.Logf("%#v\n", pcs) if len(pcs) != 1 { @@ -3843,7 +3843,7 @@ func TestIssue951(t *testing.T) { t.Skip("scopes not implemented in <=go1.8") } - withTestProcess("issue951", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue951", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue()") scope, err := proc.GoroutineScope(p.CurrentThread()) assertNoError(err, t, "GoroutineScope") @@ -3879,7 +3879,7 @@ func TestDWZCompression(t *testing.T) { t.Skip("dwz not installed") } - withTestProcessArgs("dwzcompression", t, ".", []string{}, protest.EnableDWZCompression, func(p proc.Process, fixture protest.Fixture) { + withTestProcessArgs("dwzcompression", t, ".", []string{}, protest.EnableDWZCompression, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "C.fortytwo") assertNoError(proc.Continue(p), t, "first Continue()") val := evalVariable(p, t, "stdin") @@ -3891,7 +3891,7 @@ func TestDWZCompression(t *testing.T) { func TestMapLoadConfigWithReslice(t *testing.T) { // Check that load configuration is respected for resliced maps. - withTestProcess("testvariables2", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testvariables2", t, func(p *proc.Target, fixture protest.Fixture) { zolotovLoadCfg := proc.LoadConfig{FollowPointers: true, MaxStructFields: -1, MaxVariableRecurse: 3, MaxStringLen: 10, MaxArrayValues: 10} assertNoError(proc.Continue(p), t, "First Continue()") scope, err := proc.GoroutineScope(p.CurrentThread()) @@ -3919,7 +3919,7 @@ func TestStepOutReturn(t *testing.T) { if ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{1, 10, -1, 0, 0, ""}) { t.Skip("return variables aren't marked on 1.9 or earlier") } - withTestProcess("stepoutret", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("stepoutret", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.stepout") assertNoError(proc.Continue(p), t, "Continue") assertNoError(proc.StepOut(p), t, "StepOut") @@ -3967,7 +3967,7 @@ func TestStepOutReturn(t *testing.T) { } func TestOptimizationCheck(t *testing.T) { - withTestProcess("continuetestprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("continuetestprog", t, func(p *proc.Target, fixture protest.Fixture) { fn := p.BinInfo().LookupFunc["main.main"] if fn.Optimized() { t.Fatalf("main.main is optimized") @@ -3975,7 +3975,7 @@ func TestOptimizationCheck(t *testing.T) { }) if goversion.VersionAfterOrEqual(runtime.Version(), 1, 10) { - withTestProcessArgs("continuetestprog", t, ".", []string{}, protest.EnableOptimization|protest.EnableInlining, func(p proc.Process, fixture protest.Fixture) { + withTestProcessArgs("continuetestprog", t, ".", []string{}, protest.EnableOptimization|protest.EnableInlining, func(p *proc.Target, fixture protest.Fixture) { fn := p.BinInfo().LookupFunc["main.main"] if !fn.Optimized() { t.Fatalf("main.main is not optimized") @@ -3987,7 +3987,7 @@ func TestOptimizationCheck(t *testing.T) { func TestIssue1264(t *testing.T) { // It should be possible to set a breakpoint condition that consists only // of evaluating a single boolean variable. - withTestProcess("issue1264", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue1264", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture.Source, 8) bp.Cond = &ast.Ident{Name: "equalsTwo"} assertNoError(proc.Continue(p), t, "Continue()") @@ -3996,7 +3996,7 @@ func TestIssue1264(t *testing.T) { } func TestReadDefer(t *testing.T) { - withTestProcess("deferstack", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("deferstack", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue") frames, err := p.SelectedGoroutine().Stacktrace(10, proc.StacktraceReadDefers) assertNoError(err, t, "Stacktrace") @@ -4061,7 +4061,7 @@ func TestNextUnknownInstr(t *testing.T) { if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 10) { t.Skip("versions of Go before 1.10 can't assemble the instruction VPUNPCKLWD") } - withTestProcess("nodisasm/", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("nodisasm/", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.asmFunc") assertNoError(proc.Continue(p), t, "Continue()") assertNoError(proc.Next(p), t, "Next()") @@ -4080,7 +4080,7 @@ func TestReadDeferArgs(t *testing.T) { {2, 2, 1, -1}, } - withTestProcess("deferstack", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("deferstack", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue()") for _, test := range tests { @@ -4120,7 +4120,7 @@ func TestIssue1374(t *testing.T) { } // Continue did not work when stopped at a breakpoint immediately after calling CallFunction. protest.MustSupportFunctionCalls(t, testBackend) - withTestProcess("issue1374", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue1374", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 7) assertNoError(proc.Continue(p), t, "First Continue") assertLineNumber(p, t, 7, "Did not continue to correct location (first continue),") @@ -4139,7 +4139,7 @@ func TestIssue1432(t *testing.T) { // the struct's type and then accessing a member field will still: // - perform auto-dereferencing on struct member access // - yield a Variable that's ultimately assignable (i.e. has an address) - withTestProcess("issue1432", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue1432", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue") svar := evalVariable(p, t, "s") t.Logf("%#x", svar.Addr) @@ -4153,7 +4153,7 @@ func TestIssue1432(t *testing.T) { } func TestGoroutinesInfoLimit(t *testing.T) { - withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 37) assertNoError(proc.Continue(p), t, "Continue()") @@ -4182,7 +4182,7 @@ func TestGoroutinesInfoLimit(t *testing.T) { } func TestIssue1469(t *testing.T) { - withTestProcess("issue1469", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue1469", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 13) assertNoError(proc.Continue(p), t, "Continue()") @@ -4221,7 +4221,7 @@ func TestDeadlockBreakpoint(t *testing.T) { if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) { deadlockBp = proc.UnrecoveredPanic } - withTestProcess("testdeadlock", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testdeadlock", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue()") bp := p.CurrentThread().Breakpoint() @@ -4234,7 +4234,7 @@ func TestDeadlockBreakpoint(t *testing.T) { func TestListImages(t *testing.T) { pluginFixtures := protest.WithPlugins(t, protest.AllNonOptimized, "plugin1/", "plugin2/") - withTestProcessArgs("plugintest", t, ".", []string{pluginFixtures[0].Path, pluginFixtures[1].Path}, protest.AllNonOptimized, func(p proc.Process, fixture protest.Fixture) { + withTestProcessArgs("plugintest", t, ".", []string{pluginFixtures[0].Path, pluginFixtures[1].Path}, protest.AllNonOptimized, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "first continue") f, l := currentLineNumber(p, t) plugin1Found := false @@ -4277,7 +4277,7 @@ func TestAncestors(t *testing.T) { savedGodebug := os.Getenv("GODEBUG") os.Setenv("GODEBUG", "tracebackancestors=100") defer os.Setenv("GODEBUG", savedGodebug) - withTestProcess("testnextprog", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("testnextprog", t, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "main.testgoroutine") assertNoError(proc.Continue(p), t, "Continue()") as, err := proc.Ancestors(p, p.SelectedGoroutine(), 1000) @@ -4304,7 +4304,7 @@ func TestAncestors(t *testing.T) { }) } -func testCallConcurrentCheckReturns(p proc.Process, t *testing.T, gid1, gid2 int) int { +func testCallConcurrentCheckReturns(p *proc.Target, t *testing.T, gid1, gid2 int) int { found := 0 for _, thread := range p.ThreadList() { g, _ := proc.GetG(thread) @@ -4341,7 +4341,7 @@ func TestCallConcurrent(t *testing.T) { t.Skip("arm64 does not support FunctionCall for now") } protest.MustSupportFunctionCalls(t, testBackend) - withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture.Source, 24) assertNoError(proc.Continue(p), t, "Continue()") //_, err := p.ClearBreakpoint(bp.Addr) @@ -4398,7 +4398,7 @@ func TestPluginStepping(t *testing.T) { func TestIssue1601(t *testing.T) { //Tests that recursive types involving C qualifiers and typedefs are parsed correctly - withTestProcess("issue1601", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue1601", t, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue") evalVariable(p, t, "C.globalq") }) @@ -4407,7 +4407,7 @@ func TestIssue1601(t *testing.T) { func TestIssue1615(t *testing.T) { // A breakpoint condition that tests for string equality with a constant string shouldn't fail with 'string too long for comparison' error - withTestProcess("issue1615", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue1615", t, func(p *proc.Target, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture.Source, 19) bp.Cond = &ast.BinaryExpr{ Op: token.EQL, @@ -4426,7 +4426,7 @@ func TestCgoStacktrace2(t *testing.T) { } // If a panic happens during cgo execution the stacktrace should show the C // function that caused the problem. - withTestProcess("cgosigsegvstack", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("cgosigsegvstack", t, func(p *proc.Target, fixture protest.Fixture) { proc.Continue(p) frames, err := proc.ThreadStacktrace(p.CurrentThread(), 100) assertNoError(err, t, "Stacktrace()") @@ -4439,7 +4439,7 @@ func TestIssue1656(t *testing.T) { if runtime.GOARCH != "amd64" { t.Skip("amd64 only") } - withTestProcess("issue1656/", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue1656/", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, filepath.ToSlash(filepath.Join(fixture.BuildDir, "main.s")), 5) assertNoError(proc.Continue(p), t, "Continue()") t.Logf("step1\n") @@ -4459,7 +4459,7 @@ func TestBreakpointConfusionOnResume(t *testing.T) { if runtime.GOARCH != "amd64" { t.Skip("amd64 only") } - withTestProcess("nopbreakpoint/", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("nopbreakpoint/", t, func(p *proc.Target, fixture protest.Fixture) { maindots := filepath.ToSlash(filepath.Join(fixture.BuildDir, "main.s")) maindotgo := filepath.ToSlash(filepath.Join(fixture.BuildDir, "main.go")) setFileBreakpoint(p, t, maindots, 5) // line immediately after the NOP @@ -4476,7 +4476,7 @@ func TestBreakpointConfusionOnResume(t *testing.T) { } func TestIssue1736(t *testing.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()") ch1BufVar := evalVariable(p, t, "*(ch1.buf)") q := fmt.Sprintf("*(*%q)(%d)", ch1BufVar.DwarfType.Common().Name, ch1BufVar.Addr) @@ -4491,13 +4491,13 @@ func TestIssue1736(t *testing.T) { func TestIssue1817(t *testing.T) { // Setting a breakpoint on a line that doesn't have any PC addresses marked // is_stmt should work. - withTestProcess("issue1817", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("issue1817", t, func(p *proc.Target, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture.Source, 16) }) } func TestListPackagesBuildInfo(t *testing.T) { - withTestProcess("pkgrenames", t, func(p proc.Process, fixture protest.Fixture) { + withTestProcess("pkgrenames", t, func(p *proc.Target, fixture protest.Fixture) { pkgs := p.BinInfo().ListPackagesBuildInfo(true) t.Logf("returned %d", len(pkgs)) if len(pkgs) < 10 { @@ -4529,13 +4529,13 @@ func TestIssue1795(t *testing.T) { if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 13) { t.Skip("Test not relevant to Go < 1.13") } - withTestProcessArgs("issue1795", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p proc.Process, fixture protest.Fixture) { + withTestProcessArgs("issue1795", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p *proc.Target, fixture protest.Fixture) { assertNoError(proc.Continue(p), t, "Continue()") assertLineNumber(p, t, 12, "wrong line number after Continue,") assertNoError(proc.Next(p), t, "Next()") assertLineNumber(p, t, 13, "wrong line number after Next,") }) - withTestProcessArgs("issue1795", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p proc.Process, fixture protest.Fixture) { + withTestProcessArgs("issue1795", t, ".", []string{}, protest.EnableInlining|protest.EnableOptimization, func(p *proc.Target, fixture protest.Fixture) { setFunctionBreakpoint(p, t, "regexp.(*Regexp).doExecute") assertNoError(proc.Continue(p), t, "Continue()") assertLineNumber(p, t, 12, "wrong line number after Continue (1),") diff --git a/pkg/proc/proc_unix_test.go b/pkg/proc/proc_unix_test.go index 881eedfc..b97f45df 100644 --- a/pkg/proc/proc_unix_test.go +++ b/pkg/proc/proc_unix_test.go @@ -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()") diff --git a/pkg/proc/scope_test.go b/pkg/proc/scope_test.go index d616a71c..392f04b8 100644 --- a/pkg/proc/scope_test.go +++ b/pkg/proc/scope_test.go @@ -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()") diff --git a/pkg/proc/target.go b/pkg/proc/target.go new file mode 100644 index 00000000..a2b46de5 --- /dev/null +++ b/pkg/proc/target.go @@ -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) +} diff --git a/pkg/proc/threads.go b/pkg/proc/threads.go index 3b140cf8..aeec2388 100644 --- a/pkg/proc/threads.go +++ b/pkg/proc/threads.go @@ -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 diff --git a/pkg/proc/variable_test.go b/pkg/proc/variable_test.go index 750dd1d4..6b555e86 100644 --- a/pkg/proc/variable_test.go +++ b/pkg/proc/variable_test.go @@ -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()") diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index 56ec1645..da6a64bc 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -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 diff --git a/service/test/variables_test.go b/service/test/variables_test.go index 9e91a6fe..347b8ecc 100644 --- a/service/test/variables_test.go +++ b/service/test/variables_test.go @@ -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")