From 3dacc25d2ee24c7e730e0043e6a0851e66e15e2c Mon Sep 17 00:00:00 2001 From: aarzilli Date: Wed, 22 Feb 2017 09:35:21 +0100 Subject: [PATCH] proc: refactor Continue to work on any Process implementation --- pkg/proc/breakpoints.go | 2 +- pkg/proc/proc.go | 237 +++++++++++++++++++------------- pkg/proc/proc_test.go | 240 ++++++++++++++++----------------- pkg/proc/proc_unix_test.go | 2 +- pkg/proc/threads.go | 63 ++++----- pkg/proc/threads_darwin.go | 7 +- pkg/proc/threads_linux.go | 10 +- pkg/proc/threads_windows.go | 7 +- pkg/target/target.go | 6 +- service/debugger/debugger.go | 10 +- service/test/variables_test.go | 24 ++-- 11 files changed, 325 insertions(+), 283 deletions(-) diff --git a/pkg/proc/breakpoints.go b/pkg/proc/breakpoints.go index 12ff6c23..3381c062 100644 --- a/pkg/proc/breakpoints.go +++ b/pkg/proc/breakpoints.go @@ -107,7 +107,7 @@ func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error { return err } -func (bp *Breakpoint) checkCondition(thread *Thread) (bool, error) { +func (bp *Breakpoint) checkCondition(thread IThread) (bool, error) { if bp.Cond == nil { return true, nil } diff --git a/pkg/proc/proc.go b/pkg/proc/proc.go index 219e66a2..f06a9c86 100644 --- a/pkg/proc/proc.go +++ b/pkg/proc/proc.go @@ -1,6 +1,7 @@ package proc import ( + "debug/gosym" "encoding/binary" "errors" "fmt" @@ -319,17 +320,17 @@ func (dbp *Process) Status() *WaitStatus { } // Next continues execution until the next source line. -func (dbp *Process) Next() (err error) { - if dbp.exited { +func Next(dbp Continuable) (err error) { + if dbp.Exited() { return &ProcessExitedError{} } - for i := range dbp.breakpoints { - if dbp.breakpoints[i].Internal() { + for _, bp := range dbp.Breakpoints() { + if bp.Internal() { return fmt.Errorf("next while nexting") } } - if err = dbp.next(false); err != nil { + if err = next(dbp, false); err != nil { switch err.(type) { case ThreadBlockedError, NoReturnAddr: // Noop default: @@ -338,91 +339,119 @@ func (dbp *Process) Next() (err error) { } } - return dbp.Continue() + return Continue(dbp) +} + +func (dbp *Process) ContinueOnce() (IThread, error) { + if dbp.exited { + return nil, &ProcessExitedError{} + } + + if err := dbp.resume(); err != nil { + return nil, err + } + + dbp.allGCache = nil + for _, th := range dbp.threads { + th.clearBreakpointState() + } + + trapthread, err := dbp.trapWait(-1) + if err != nil { + return nil, err + } + if err := dbp.Halt(); err != nil { + return nil, dbp.exitGuard(err) + } + if err := dbp.setCurrentBreakpoints(trapthread); err != nil { + return nil, err + } + return trapthread, err +} + +// Continuable is the subinterface of target.Interface used to implement +// Continue/Next/etc. +type Continuable interface { + ContinueOnce() (trapthread IThread, err error) + CurrentThread() IThread + SelectedGoroutine() *G + Breakpoints() map[uint64]*Breakpoint + ThreadList() []IThread + SwitchThread(int) error + BinInfo() *BinaryInfo + ClearInternalBreakpoints() error + FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) + SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) + Exited() bool } // Continue continues execution of the debugged // process. It will continue until it hits a breakpoint // or is otherwise stopped. -func (dbp *Process) Continue() error { - if dbp.exited { - return &ProcessExitedError{} - } +func Continue(dbp Continuable) error { for { - if err := dbp.resume(); err != nil { + trapthread, err := dbp.ContinueOnce() + if err != nil { return err } - dbp.allGCache = nil - for _, th := range dbp.threads { - th.clearBreakpointState() - } + threads := dbp.ThreadList() - trapthread, err := dbp.trapWait(-1) - if err != nil { - return err - } - if err := dbp.Halt(); err != nil { - return dbp.exitGuard(err) - } - if err := dbp.setCurrentBreakpoints(trapthread); err != nil { - return err - } - if err := dbp.pickCurrentThread(trapthread); err != nil { + if err := pickCurrentThread(dbp, trapthread, threads); err != nil { return err } + curthread := dbp.CurrentThread() + curbp, curbpActive, _ := curthread.Breakpoint() + switch { - case dbp.currentThread.CurrentBreakpoint == nil: + case curbp == nil: // runtime.Breakpoint or manual stop - if dbp.currentThread.onRuntimeBreakpoint() { + if onRuntimeBreakpoint(curthread) { // Single-step current thread until we exit runtime.breakpoint and // runtime.Breakpoint. // On go < 1.8 it was sufficient to single-step twice on go1.8 a change // to the compiler requires 4 steps. for { - if err = dbp.currentThread.StepInstruction(); err != nil { + if err = curthread.StepInstruction(); err != nil { return err } - loc, err := dbp.currentThread.Location() + loc, err := curthread.Location() if err != nil || loc.Fn == nil || (loc.Fn.Name != "runtime.breakpoint" && loc.Fn.Name != "runtime.Breakpoint") { break } } } - return dbp.conditionErrors() - case dbp.currentThread.onTriggeredInternalBreakpoint(): - if dbp.currentThread.CurrentBreakpoint.Kind == StepBreakpoint { + return conditionErrors(threads) + case curbpActive && curbp.Internal(): + if curbp.Kind == StepBreakpoint { // See description of proc.(*Process).next for the meaning of StepBreakpoints - if err := dbp.conditionErrors(); err != nil { - return err - } - pc, err := dbp.currentThread.PC() - if err != nil { + if err := conditionErrors(threads); err != nil { return err } - regs, err := dbp.currentThread.Registers(false) + regs, err := curthread.Registers(false) if err != nil { return err } - text, err := disassemble(dbp.currentThread, regs, dbp.breakpoints, dbp.BinInfo(), pc, pc+maxInstructionLength) + pc := regs.PC() + text, err := disassemble(curthread, regs, dbp.Breakpoints(), dbp.BinInfo(), pc, pc+maxInstructionLength) if err != nil { return err } // here we either set a breakpoint into the destination of the CALL // instruction or we determined that the called function is hidden, // either way we need to resume execution - if err = dbp.setStepIntoBreakpoint(text, sameGoroutineCondition(dbp.selectedGoroutine)); err != nil { + if err = setStepIntoBreakpoint(dbp, text, sameGoroutineCondition(dbp.SelectedGoroutine())); err != nil { return err } } else { if err := dbp.ClearInternalBreakpoints(); err != nil { return err } - return dbp.conditionErrors() + return conditionErrors(threads) } - case dbp.currentThread.onTriggeredBreakpoint(): - onNextGoroutine, err := dbp.currentThread.onNextGoroutine() + case curbpActive: + onNextGoroutine, err := onNextGoroutine(curthread, dbp.Breakpoints()) if err != nil { return err } @@ -432,19 +461,19 @@ func (dbp *Process) Continue() error { return err } } - return dbp.conditionErrors() + return conditionErrors(threads) default: // not a manual stop, not on runtime.Breakpoint, not on a breakpoint, just repeat } } } -func (dbp *Process) conditionErrors() error { +func conditionErrors(threads []IThread) error { var condErr error - for _, th := range dbp.threads { - if th.CurrentBreakpoint != nil && th.BreakpointConditionError != nil { + for _, th := range threads { + if bp, _, bperr := th.Breakpoint(); bp != nil && bperr != nil { if condErr == nil { - condErr = th.BreakpointConditionError + condErr = bperr } else { return fmt.Errorf("multiple errors evaluating conditions") } @@ -457,36 +486,36 @@ func (dbp *Process) conditionErrors() error { // - a thread with onTriggeredInternalBreakpoint() == true // - a thread with onTriggeredBreakpoint() == true (prioritizing trapthread) // - trapthread -func (dbp *Process) pickCurrentThread(trapthread *Thread) error { - for _, th := range dbp.threads { - if th.onTriggeredInternalBreakpoint() { - return dbp.SwitchThread(th.ID) +func pickCurrentThread(dbp Continuable, trapthread IThread, threads []IThread) error { + for _, th := range threads { + if bp, active, _ := th.Breakpoint(); active && bp.Internal() { + return dbp.SwitchThread(th.ThreadID()) } } - if trapthread.onTriggeredBreakpoint() { - return dbp.SwitchThread(trapthread.ID) + if _, active, _ := trapthread.Breakpoint(); active { + return dbp.SwitchThread(trapthread.ThreadID()) } - for _, th := range dbp.threads { - if th.onTriggeredBreakpoint() { - return dbp.SwitchThread(th.ID) + for _, th := range threads { + if _, active, _ := th.Breakpoint(); active { + return dbp.SwitchThread(th.ThreadID()) } } - return dbp.SwitchThread(trapthread.ID) + return dbp.SwitchThread(trapthread.ThreadID()) } // Step will continue until another source line is reached. // Will step into functions. -func (dbp *Process) Step() (err error) { - if dbp.exited { +func Step(dbp Continuable) (err error) { + if dbp.Exited() { return &ProcessExitedError{} } - for i := range dbp.breakpoints { - if dbp.breakpoints[i].Internal() { + for _, bp := range dbp.Breakpoints() { + if bp.Internal() { return fmt.Errorf("next while nexting") } } - if err = dbp.next(true); err != nil { + if err = next(dbp, true); err != nil { switch err.(type) { case ThreadBlockedError, NoReturnAddr: // Noop default: @@ -495,7 +524,7 @@ func (dbp *Process) Step() (err error) { } } - return dbp.Continue() + return Continue(dbp) } // Returns an expression that evaluates to true when the current goroutine is g @@ -529,7 +558,7 @@ func (dbp *Process) StepInstruction() (err error) { if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, NextBreakpoint, sameGoroutineCondition(dbp.selectedGoroutine)); err != nil { return err } - return dbp.Continue() + return Continue(dbp) } dbp.allGCache = nil if dbp.exited { @@ -545,10 +574,12 @@ func (dbp *Process) StepInstruction() (err error) { // StepOut will continue until the current goroutine exits the // function currently being executed or a deferred function is executed -func (dbp *Process) StepOut() error { - cond := sameGoroutineCondition(dbp.selectedGoroutine) +func StepOut(dbp Continuable) error { + selg := dbp.SelectedGoroutine() + curthread := dbp.CurrentThread() + cond := sameGoroutineCondition(selg) - topframe, err := topframe(dbp.selectedGoroutine, dbp.currentThread) + topframe, err := topframe(selg, curthread) if err != nil { return err } @@ -557,10 +588,10 @@ func (dbp *Process) StepOut() error { var deferpc uint64 = 0 if filepath.Ext(topframe.Current.File) == ".go" { - if dbp.selectedGoroutine != nil { - deferPCEntry := dbp.selectedGoroutine.DeferPC() + if selg != nil { + deferPCEntry := selg.DeferPC() if deferPCEntry != 0 { - _, _, deferfn := dbp.bi.goSymTable.PCToLine(deferPCEntry) + _, _, deferfn := dbp.BinInfo().PCToLine(deferPCEntry) deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false) if err != nil { return err @@ -591,12 +622,12 @@ func (dbp *Process) StepOut() error { } if topframe.Ret != 0 { - if err := dbp.setInternalBreakpoints(topframe.Current.PC, []uint64{topframe.Ret}, NextBreakpoint, cond); err != nil { + if err := setInternalBreakpoints(dbp, topframe.Current.PC, []uint64{topframe.Ret}, NextBreakpoint, cond); err != nil { return err } } - return dbp.Continue() + return Continue(dbp) } // SwitchThread changes from current thread to the thread specified by `tid`. @@ -633,29 +664,42 @@ func (dbp *Process) SwitchGoroutine(gid int) error { return nil } +// If the argument of GoroutinesInfo implements AllGCache GoroutinesInfo +// will use the pointer returned by AllGCache as a cache. +type AllGCache interface { + AllGCache() *[]*G +} + +func (dbp *Process) AllGCache() *[]*G { + return &dbp.allGCache +} + // GoroutinesInfo returns an array of G structures representing the information // Delve cares about from the internal runtime G structure. -func (dbp *Process) GoroutinesInfo() ([]*G, error) { - if dbp.exited { +func GoroutinesInfo(dbp EvalScopeConvertible) ([]*G, error) { + if dbp.Exited() { return nil, &ProcessExitedError{} } - if dbp.allGCache != nil { - return dbp.allGCache, nil + if dbp, ok := dbp.(AllGCache); ok { + if allGCache := dbp.AllGCache(); *allGCache != nil { + return *allGCache, nil + } } var ( - threadg = map[int]*Thread{} + threadg = map[int]IThread{} allg []*G - rdr = dbp.bi.DwarfReader() + rdr = dbp.BinInfo().DwarfReader() ) - for i := range dbp.threads { - if dbp.threads[i].blocked() { + threads := dbp.ThreadList() + for _, th := range threads { + if threadBlocked(th) { continue } - g, _ := GetG(dbp.threads[i]) + g, _ := GetG(th) if g != nil { - threadg[g.ID] = dbp.threads[i] + threadg[g.ID] = th } } @@ -663,7 +707,7 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) { if err != nil { return nil, err } - allglenBytes, err := dbp.currentThread.readMemory(uintptr(addr), 8) + allglenBytes, err := dbp.CurrentThread().readMemory(uintptr(addr), 8) if err != nil { return nil, err } @@ -678,11 +722,11 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) { return nil, err } } - faddr, err := dbp.currentThread.readMemory(uintptr(allgentryaddr), dbp.bi.arch.PtrSize()) + faddr, err := dbp.CurrentThread().readMemory(uintptr(allgentryaddr), dbp.BinInfo().arch.PtrSize()) allgptr := binary.LittleEndian.Uint64(faddr) for i := uint64(0); i < allglen; i++ { - gvar, err := newGVariable(dbp.currentThread, uintptr(allgptr+(i*uint64(dbp.bi.arch.PtrSize()))), true) + gvar, err := newGVariable(dbp.CurrentThread(), uintptr(allgptr+(i*uint64(dbp.BinInfo().arch.PtrSize()))), true) if err != nil { return nil, err } @@ -703,7 +747,11 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) { allg = append(allg, g) } } - dbp.allGCache = allg + if dbp, ok := dbp.(AllGCache); ok { + allGCache := dbp.AllGCache() + *allGCache = allg + } + return allg, nil } @@ -884,19 +932,14 @@ func (scope *EvalScope) getGoInformation() (ver GoVersion, isextld bool, err err return } -type GoroutinesInfo interface { - SelectedGoroutine() *G - GoroutinesInfo() ([]*G, error) -} - // FindGoroutine returns a G struct representing the goroutine // specified by `gid`. -func FindGoroutine(dbp GoroutinesInfo, gid int) (*G, error) { +func FindGoroutine(dbp EvalScopeConvertible, gid int) (*G, error) { if gid == -1 { return dbp.SelectedGoroutine(), nil } - gs, err := dbp.GoroutinesInfo() + gs, err := GoroutinesInfo(dbp) if err != nil { return nil, err } @@ -911,9 +954,11 @@ func FindGoroutine(dbp GoroutinesInfo, gid int) (*G, error) { // EvalScopeConvertible is a subset of target.Interface with the methods // used by ConvertEvalScope/GoroutinesInfo/etc. type EvalScopeConvertible interface { - GoroutinesInfo + Exited() bool + SelectedGoroutine() *G CurrentThread() IThread BinInfo() *BinaryInfo + ThreadList() []IThread } // ConvertEvalScope returns a new EvalScope in the context of the diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index c9c11a6e..1ffffa6f 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -101,7 +101,7 @@ func currentLineNumber(p *Process, t *testing.T) (string, int) { func TestExit(t *testing.T) { withTestProcess("continuetestprog", t, func(p *Process, fixture protest.Fixture) { - err := p.Continue() + err := Continue(p) pe, ok := err.(ProcessExitedError) if !ok { t.Fatalf("Continue() returned unexpected error type %s", err) @@ -119,8 +119,8 @@ func TestExitAfterContinue(t *testing.T) { withTestProcess("continuetestprog", t, func(p *Process, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.sayhi") assertNoError(err, t, "setFunctionBreakpoint()") - assertNoError(p.Continue(), t, "First Continue()") - err = p.Continue() + assertNoError(Continue(p), t, "First Continue()") + err = Continue(p) pe, ok := err.(ProcessExitedError) if !ok { t.Fatalf("Continue() returned unexpected error type %s", pe) @@ -159,7 +159,7 @@ func TestHalt(t *testing.T) { withTestProcess("loopprog", t, func(p *Process, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.loop") assertNoError(err, t, "SetBreakpoint") - assertNoError(p.Continue(), t, "Continue") + assertNoError(Continue(p), t, "Continue") for _, th := range p.threads { if th.running != false { t.Fatal("expected running = false for thread", th.ID) @@ -178,7 +178,7 @@ func TestHalt(t *testing.T) { } } }() - assertNoError(p.Continue(), t, "Continue") + assertNoError(Continue(p), t, "Continue") <-stopChan // Loop through threads and make sure they are all // actually stopped, err will not be nil if the process @@ -203,7 +203,7 @@ func TestStep(t *testing.T) { _, err := p.SetBreakpoint(helloworldaddr, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") regs := getRegisters(p, t) rip := regs.PC() @@ -225,7 +225,7 @@ func TestBreakpoint(t *testing.T) { bp, err := p.SetBreakpoint(helloworldaddr, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") pc, err := p.PC() if err != nil { @@ -255,7 +255,7 @@ func TestBreakpointInSeperateGoRoutine(t *testing.T) { t.Fatal(err) } - err = p.Continue() + err = Continue(p) if err != nil { t.Fatal(err) } @@ -340,7 +340,7 @@ func testseq(program string, contFunc contFunc, testcases []nextTest, initialLoc bp, err = p.SetBreakpoint(pc, UserBreakpoint, nil) } assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") p.ClearBreakpoint(bp.Addr) p.currentThread.SetPC(bp.Addr) @@ -353,9 +353,9 @@ func testseq(program string, contFunc contFunc, testcases []nextTest, initialLoc switch contFunc { case contNext: - assertNoError(p.Next(), t, "Next() returned an error") + assertNoError(Next(p), t, "Next() returned an error") case contStep: - assertNoError(p.Step(), t, "Step() returned an error") + assertNoError(Step(p), t, "Step() returned an error") } f, ln = currentLineNumber(p, t) @@ -427,7 +427,7 @@ func TestNextConcurrent(t *testing.T) { withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) { bp, err := setFunctionBreakpoint(p, "main.sayhi") assertNoError(err, t, "SetBreakpoint") - assertNoError(p.Continue(), t, "Continue") + assertNoError(Continue(p), t, "Continue") f, ln := currentLineNumber(p, t) initV, err := evalVariable(p, "n") initVval, _ := constant.Int64Val(initV.Value) @@ -443,7 +443,7 @@ func TestNextConcurrent(t *testing.T) { if ln != tc.begin { t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln) } - assertNoError(p.Next(), t, "Next() returned an error") + assertNoError(Next(p), t, "Next() returned an error") f, ln = currentLineNumber(p, t) if ln != tc.end { t.Fatalf("Program did not continue to correct next location expected %d was %s:%d", tc.end, filepath.Base(f), ln) @@ -468,7 +468,7 @@ func TestNextConcurrentVariant2(t *testing.T) { withTestProcess("parallel_next", t, func(p *Process, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.sayhi") assertNoError(err, t, "SetBreakpoint") - assertNoError(p.Continue(), t, "Continue") + assertNoError(Continue(p), t, "Continue") f, ln := currentLineNumber(p, t) initV, err := evalVariable(p, "n") initVval, _ := constant.Int64Val(initV.Value) @@ -482,7 +482,7 @@ func TestNextConcurrentVariant2(t *testing.T) { if ln != tc.begin { t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln) } - assertNoError(p.Next(), t, "Next() returned an error") + assertNoError(Next(p), t, "Next() returned an error") var vval int64 for { v, err := evalVariable(p, "n") @@ -497,7 +497,7 @@ func TestNextConcurrentVariant2(t *testing.T) { if vval == initVval { t.Fatal("Initial breakpoint triggered twice for the same goroutine") } - assertNoError(p.Continue(), t, "Continue 2") + assertNoError(Continue(p), t, "Continue 2") } } f, ln = currentLineNumber(p, t) @@ -550,7 +550,7 @@ func TestNextNetHTTP(t *testing.T) { } http.Get("http://localhost:9191") }() - if err := p.Continue(); err != nil { + if err := Continue(p); err != nil { t.Fatal(err) } f, ln := currentLineNumber(p, t) @@ -559,7 +559,7 @@ func TestNextNetHTTP(t *testing.T) { t.Fatalf("Program not stopped at correct spot expected %d was %s:%d", tc.begin, filepath.Base(f), ln) } - assertNoError(p.Next(), t, "Next() returned an error") + assertNoError(Next(p), t, "Next() returned an error") f, ln = currentLineNumber(p, t) if ln != tc.end { @@ -571,7 +571,7 @@ func TestNextNetHTTP(t *testing.T) { func TestRuntimeBreakpoint(t *testing.T) { withTestProcess("testruntimebreakpoint", t, func(p *Process, fixture protest.Fixture) { - err := p.Continue() + err := Continue(p) if err != nil { t.Fatal(err) } @@ -607,7 +607,7 @@ func TestFindReturnAddress(t *testing.T) { if err != nil { t.Fatal(err) } - err = p.Continue() + err = Continue(p) if err != nil { t.Fatal(err) } @@ -632,7 +632,7 @@ func TestFindReturnAddressTopOfStackFn(t *testing.T) { if _, err := p.SetBreakpoint(fn.Entry, UserBreakpoint, nil); err != nil { t.Fatal(err) } - if err := p.Continue(); err != nil { + if err := Continue(p); err != nil { t.Fatal(err) } if _, err := returnAddress(p.currentThread); err == nil { @@ -656,7 +656,7 @@ func TestSwitchThread(t *testing.T) { if err != nil { t.Fatal(err) } - err = p.Continue() + err = Continue(p) if err != nil { t.Fatal(err) } @@ -701,11 +701,11 @@ func TestCGONext(t *testing.T) { if err != nil { t.Fatal(err) } - err = p.Continue() + err = Continue(p) if err != nil { t.Fatal(err) } - err = p.Next() + err = Next(p) if err != nil { t.Fatal(err) } @@ -736,7 +736,7 @@ func TestStacktrace(t *testing.T) { assertNoError(err, t, "BreakByLocation()") for i := range stacks { - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") locations, err := ThreadStacktrace(p.currentThread, 40) assertNoError(err, t, "Stacktrace()") @@ -757,13 +757,13 @@ func TestStacktrace(t *testing.T) { } p.ClearBreakpoint(bp.Addr) - p.Continue() + Continue(p) }) } func TestStacktrace2(t *testing.T) { withTestProcess("retstack", t, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") locations, err := ThreadStacktrace(p.currentThread, 40) assertNoError(err, t, "Stacktrace()") @@ -774,7 +774,7 @@ func TestStacktrace2(t *testing.T) { t.Fatalf("Stack error at main.f()\n%v\n", locations) } - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") locations, err = ThreadStacktrace(p.currentThread, 40) assertNoError(err, t, "Stacktrace()") if !stackMatch([]loc{{-1, "main.g"}, {17, "main.main"}}, locations, false) { @@ -817,7 +817,7 @@ func TestStacktraceGoroutine(t *testing.T) { bp, err := setFunctionBreakpoint(p, "main.stacktraceme") assertNoError(err, t, "BreakByLocation()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") gs, err := p.GoroutinesInfo() assertNoError(err, t, "GoroutinesInfo") @@ -867,7 +867,7 @@ func TestStacktraceGoroutine(t *testing.T) { } p.ClearBreakpoint(bp.Addr) - p.Continue() + Continue(p) }) } @@ -892,7 +892,7 @@ func testGSupportFunc(name string, t *testing.T, p *Process, fixture protest.Fix bp, err := setFunctionBreakpoint(p, "main.main") assertNoError(err, t, name+": BreakByLocation()") - assertNoError(p.Continue(), t, name+": Continue()") + assertNoError(Continue(p), t, name+": Continue()") g, err := GetG(p.currentThread) assertNoError(err, t, name+": GetG()") @@ -935,7 +935,7 @@ func TestContinueMulti(t *testing.T) { mainCount := 0 sayhiCount := 0 for { - err := p.Continue() + err := Continue(p) if p.Exited() { break } @@ -993,7 +993,7 @@ func TestBreakpointOnFunctionEntry(t *testing.T) { assertNoError(err, t, "FindFunctionLocation()") _, err = p.SetBreakpoint(addr, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") _, ln := currentLineNumber(p, t) if ln != 17 { t.Fatalf("Wrong line number: %d (expected: 17)\n", ln) @@ -1003,7 +1003,7 @@ func TestBreakpointOnFunctionEntry(t *testing.T) { func TestProcessReceivesSIGCHLD(t *testing.T) { withTestProcess("sigchldprog", t, func(p *Process, fixture protest.Fixture) { - err := p.Continue() + err := Continue(p) _, ok := err.(ProcessExitedError) if !ok { t.Fatalf("Continue() returned unexpected error type %s", err) @@ -1017,7 +1017,7 @@ func TestIssue239(t *testing.T) { assertNoError(err, t, "LineToPC()") _, err = p.SetBreakpoint(pos, UserBreakpoint, nil) assertNoError(err, t, fmt.Sprintf("SetBreakpoint(%d)", pos)) - assertNoError(p.Continue(), t, fmt.Sprintf("Continue()")) + assertNoError(Continue(p), t, fmt.Sprintf("Continue()")) }) } @@ -1074,7 +1074,7 @@ func TestVariableEvaluation(t *testing.T) { } withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue() returned an error") + assertNoError(Continue(p), t, "Continue() returned an error") for _, tc := range testcases { v, err := evalVariable(p, tc.name) @@ -1126,7 +1126,7 @@ func TestFrameEvaluation(t *testing.T) { withTestProcess("goroutinestackprog", t, func(p *Process, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.stacktraceme") assertNoError(err, t, "setFunctionBreakpoint") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") // Testing evaluation on goroutines gs, err := p.GoroutinesInfo() @@ -1171,7 +1171,7 @@ func TestFrameEvaluation(t *testing.T) { } // Testing evaluation on frames - assertNoError(p.Continue(), t, "Continue() 2") + assertNoError(Continue(p), t, "Continue() 2") g, err := GetG(p.currentThread) assertNoError(err, t, "GetG()") @@ -1191,7 +1191,7 @@ func TestFrameEvaluation(t *testing.T) { func TestPointerSetting(t *testing.T) { withTestProcess("testvariables2", t, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue() returned an error") + assertNoError(Continue(p), t, "Continue() returned an error") pval := func(n int64) { variable, err := evalVariable(p, "p1") @@ -1220,7 +1220,7 @@ func TestPointerSetting(t *testing.T) { func TestVariableFunctionScoping(t *testing.T) { withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { - err := p.Continue() + err := Continue(p) assertNoError(err, t, "Continue() returned an error") _, err = evalVariable(p, "a1") @@ -1230,7 +1230,7 @@ func TestVariableFunctionScoping(t *testing.T) { assertNoError(err, t, "Unable to find variable a1") // Move scopes, a1 exists here by a2 does not - err = p.Continue() + err = Continue(p) assertNoError(err, t, "Continue() returned an error") _, err = evalVariable(p, "a1") @@ -1245,7 +1245,7 @@ func TestVariableFunctionScoping(t *testing.T) { func TestRecursiveStructure(t *testing.T) { withTestProcess("testvariables2", t, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") v, err := evalVariable(p, "aas") assertNoError(err, t, "EvalVariable()") t.Logf("v: %v\n", v) @@ -1255,7 +1255,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 withTestProcess("testvariables2", t, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") _, err := evalVariable(p, "iface5") assertNoError(err, t, "EvalVariable()") }) @@ -1264,7 +1264,7 @@ func TestIssue316(t *testing.T) { func TestIssue325(t *testing.T) { // nil pointer dereference when evaluating interfaces to function pointers withTestProcess("testvariables2", t, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") iface2fn1v, err := evalVariable(p, "iface2fn1") assertNoError(err, t, "EvalVariable()") t.Logf("iface2fn1: %v\n", iface2fn1v) @@ -1283,7 +1283,7 @@ func TestBreakpointCounts(t *testing.T) { assertNoError(err, t, "SetBreakpoint()") for { - if err := p.Continue(); err != nil { + if err := Continue(p); err != nil { if _, exited := err.(ProcessExitedError); exited { break } @@ -1312,7 +1312,7 @@ func BenchmarkArray(b *testing.B) { // each bencharr struct is 128 bytes, bencharr is 64 elements long b.SetBytes(int64(64 * 128)) withTestProcess("testvariables2", b, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), b, "Continue()") + assertNoError(Continue(p), b, "Continue()") for i := 0; i < b.N; i++ { _, err := evalVariable(p, "bencharr") assertNoError(err, b, "EvalVariable()") @@ -1334,7 +1334,7 @@ func TestBreakpointCountsWithDetection(t *testing.T) { assertNoError(err, t, "SetBreakpoint()") for { - if err := p.Continue(); err != nil { + if err := Continue(p); err != nil { if _, exited := err.(ProcessExitedError); exited { break } @@ -1387,7 +1387,7 @@ func BenchmarkArrayPointer(b *testing.B) { // each read will read 64 bencharr structs plus the 64 pointers of benchparr b.SetBytes(int64(64*128 + 64*8)) withTestProcess("testvariables2", b, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), b, "Continue()") + assertNoError(Continue(p), b, "Continue()") for i := 0; i < b.N; i++ { _, err := evalVariable(p, "bencharr") assertNoError(err, b, "EvalVariable()") @@ -1401,7 +1401,7 @@ func BenchmarkMap(b *testing.B) { // reading strings and the map structure imposes a overhead that we ignore here b.SetBytes(int64(41 * (2*8 + 9))) withTestProcess("testvariables2", b, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), b, "Continue()") + assertNoError(Continue(p), b, "Continue()") for i := 0; i < b.N; i++ { _, err := evalVariable(p, "m1") assertNoError(err, b, "EvalVariable()") @@ -1411,7 +1411,7 @@ func BenchmarkMap(b *testing.B) { func BenchmarkGoroutinesInfo(b *testing.B) { withTestProcess("testvariables2", b, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), b, "Continue()") + assertNoError(Continue(p), b, "Continue()") for i := 0; i < b.N; i++ { p.allGCache = nil _, err := p.GoroutinesInfo() @@ -1428,8 +1428,8 @@ func TestIssue262(t *testing.T) { _, err = p.SetBreakpoint(addr, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") - err = p.Continue() + assertNoError(Continue(p), t, "Continue()") + err = Continue(p) if err == nil { t.Fatalf("No error on second continue") } @@ -1450,13 +1450,13 @@ func TestIssue305(t *testing.T) { _, err = p.SetBreakpoint(addr, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") - assertNoError(p.Next(), t, "Next() 1") - assertNoError(p.Next(), t, "Next() 2") - assertNoError(p.Next(), t, "Next() 3") - assertNoError(p.Next(), t, "Next() 4") - assertNoError(p.Next(), t, "Next() 5") + assertNoError(Next(p), t, "Next() 1") + assertNoError(Next(p), t, "Next() 2") + assertNoError(Next(p), t, "Next() 3") + assertNoError(Next(p), t, "Next() 4") + assertNoError(Next(p), t, "Next() 5") }) } @@ -1464,7 +1464,7 @@ func TestPointerLoops(t *testing.T) { // Pointer loops through map entries, pointers and slices // Regression test for issue #341 withTestProcess("testvariables2", t, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") for _, expr := range []string{"mapinf", "ptrinf", "sliceinf"} { t.Logf("requesting %s", expr) v, err := evalVariable(p, expr) @@ -1476,7 +1476,7 @@ func TestPointerLoops(t *testing.T) { func BenchmarkLocalVariables(b *testing.B) { withTestProcess("testvariables", b, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), b, "Continue() returned an error") + assertNoError(Continue(p), b, "Continue() returned an error") scope, err := GoroutineScope(p.currentThread) assertNoError(err, b, "Scope()") for i := 0; i < b.N; i++ { @@ -1498,7 +1498,7 @@ func TestCondBreakpoint(t *testing.T) { Y: &ast.BasicLit{Kind: token.INT, Value: "7"}, } - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") nvar, err := evalVariable(p, "n") assertNoError(err, t, "EvalVariable()") @@ -1522,7 +1522,7 @@ func TestCondBreakpointError(t *testing.T) { Y: &ast.BasicLit{Kind: token.INT, Value: "7"}, } - err = p.Continue() + err = Continue(p) if err == nil { t.Fatalf("No error on first Continue()") } @@ -1537,7 +1537,7 @@ func TestCondBreakpointError(t *testing.T) { Y: &ast.BasicLit{Kind: token.INT, Value: "7"}, } - err = p.Continue() + err = Continue(p) if err != nil { if _, exited := err.(ProcessExitedError); !exited { t.Fatalf("Unexpected error on second Continue(): %v", err) @@ -1557,7 +1557,7 @@ func TestCondBreakpointError(t *testing.T) { func TestIssue356(t *testing.T) { // slice with a typedef does not get printed correctly withTestProcess("testvariables2", t, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue() returned an error") + assertNoError(Continue(p), t, "Continue() returned an error") mmvar, err := evalVariable(p, "mainMenu") assertNoError(err, t, "EvalVariable()") if mmvar.Kind != reflect.Slice { @@ -1569,9 +1569,9 @@ func TestIssue356(t *testing.T) { func TestStepIntoFunction(t *testing.T) { withTestProcess("teststep", t, func(p *Process, fixture protest.Fixture) { // Continue until breakpoint - assertNoError(p.Continue(), t, "Continue() returned an error") + assertNoError(Continue(p), t, "Continue() returned an error") // Step into function - assertNoError(p.Step(), t, "Step() returned an error") + assertNoError(Step(p), t, "Step() returned an error") // We should now be inside the function. loc, err := p.CurrentLocation() if err != nil { @@ -1596,7 +1596,7 @@ func TestIssue384(t *testing.T) { assertNoError(err, t, "LineToPC()") _, err = p.SetBreakpoint(start, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") _, err = evalVariable(p, "st") assertNoError(err, t, "EvalVariable()") }) @@ -1609,8 +1609,8 @@ func TestIssue332_Part1(t *testing.T) { assertNoError(err, t, "LineToPC()") _, err = p.SetBreakpoint(start, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") - assertNoError(p.Next(), t, "first Next()") + assertNoError(Continue(p), t, "Continue()") + assertNoError(Next(p), t, "first Next()") locations, err := ThreadStacktrace(p.currentThread, 2) assertNoError(err, t, "Stacktrace()") if locations[0].Call.Fn == nil { @@ -1635,11 +1635,11 @@ func TestIssue332_Part2(t *testing.T) { assertNoError(err, t, "LineToPC()") _, err = p.SetBreakpoint(start, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") // step until we enter changeMe for { - assertNoError(p.Step(), t, "Step()") + assertNoError(Step(p), t, "Step()") locations, err := ThreadStacktrace(p.currentThread, 2) assertNoError(err, t, "Stacktrace()") if locations[0].Call.Fn == nil { @@ -1662,10 +1662,10 @@ func TestIssue332_Part2(t *testing.T) { t.Fatalf("Step did not skip the prologue: current pc: %x, first instruction after prologue: %x", pc, pcAfterPrologue) } - assertNoError(p.Next(), t, "first Next()") - assertNoError(p.Next(), t, "second Next()") - assertNoError(p.Next(), t, "third Next()") - err = p.Continue() + assertNoError(Next(p), t, "first Next()") + assertNoError(Next(p), t, "second Next()") + assertNoError(Next(p), t, "third Next()") + err = Continue(p) if _, exited := err.(ProcessExitedError); !exited { assertNoError(err, t, "final Continue()") } @@ -1686,9 +1686,9 @@ func TestIssue414(t *testing.T) { assertNoError(err, t, "LineToPC()") _, err = p.SetBreakpoint(start, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") for { - err := p.Step() + err := Step(p) if err != nil { if _, exited := err.(ProcessExitedError); exited { break @@ -1701,7 +1701,7 @@ func TestIssue414(t *testing.T) { func TestPackageVariables(t *testing.T) { withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) { - err := p.Continue() + err := Continue(p) assertNoError(err, t, "Continue()") scope, err := GoroutineScope(p.currentThread) assertNoError(err, t, "Scope()") @@ -1734,7 +1734,7 @@ func TestIssue149(t *testing.T) { func TestPanicBreakpoint(t *testing.T) { withTestProcess("panic", t, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") bp := p.CurrentBreakpoint() if bp == nil || bp.Name != "unrecovered-panic" { t.Fatalf("not on unrecovered-panic breakpoint: %v", p.CurrentBreakpoint()) @@ -1744,7 +1744,7 @@ func TestPanicBreakpoint(t *testing.T) { func TestCmdLineArgs(t *testing.T) { expectSuccess := func(p *Process, fixture protest.Fixture) { - err := p.Continue() + err := Continue(p) bp := p.CurrentBreakpoint() if bp != nil && bp.Name == "unrecovered-panic" { t.Fatalf("testing args failed on unrecovered-panic breakpoint: %v", p.CurrentBreakpoint()) @@ -1760,7 +1760,7 @@ func TestCmdLineArgs(t *testing.T) { } expectPanic := func(p *Process, fixture protest.Fixture) { - p.Continue() + Continue(p) bp := p.CurrentBreakpoint() if bp == nil || bp.Name != "unrecovered-panic" { t.Fatalf("not on unrecovered-panic breakpoint: %v", p.CurrentBreakpoint()) @@ -1805,7 +1805,7 @@ func TestIssue462(t *testing.T) { p.RequestManualStop() }() - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") _, err := ThreadStacktrace(p.currentThread, 40) assertNoError(err, t, "Stacktrace()") }) @@ -1829,7 +1829,7 @@ func TestNextParked(t *testing.T) { var parkedg *G LookForParkedG: for { - err := p.Continue() + err := Continue(p) if _, exited := err.(ProcessExitedError); exited { t.Log("could not find parked goroutine") return @@ -1849,7 +1849,7 @@ func TestNextParked(t *testing.T) { assertNoError(p.SwitchGoroutine(parkedg.ID), t, "SwitchGoroutine()") p.ClearBreakpoint(bp.Addr) - assertNoError(p.Next(), t, "Next()") + assertNoError(Next(p), t, "Next()") if p.selectedGoroutine.ID != parkedg.ID { t.Fatalf("Next did not continue on the selected goroutine, expected %d got %d", parkedg.ID, p.selectedGoroutine.ID) @@ -1866,7 +1866,7 @@ func TestStepParked(t *testing.T) { var parkedg *G LookForParkedG: for { - err := p.Continue() + err := Continue(p) if _, exited := err.(ProcessExitedError); exited { t.Log("could not find parked goroutine") return @@ -1896,7 +1896,7 @@ func TestStepParked(t *testing.T) { assertNoError(p.SwitchGoroutine(parkedg.ID), t, "SwitchGoroutine()") p.ClearBreakpoint(bp.Addr) - assertNoError(p.Step(), t, "Step()") + assertNoError(Step(p), t, "Step()") if p.selectedGoroutine.ID != parkedg.ID { t.Fatalf("Step did not continue on the selected goroutine, expected %d got %d", parkedg.ID, p.selectedGoroutine.ID) @@ -1965,10 +1965,10 @@ func TestIssue573(t *testing.T) { f := p.BinInfo().goSymTable.LookupFunc("main.foo") _, err := p.SetBreakpoint(f.Entry, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") - assertNoError(p.Step(), t, "Step() #1") - assertNoError(p.Step(), t, "Step() #2") // Bug exits here. - assertNoError(p.Step(), t, "Step() #3") // Third step ought to be possible; program ought not have exited. + assertNoError(Continue(p), t, "Continue()") + assertNoError(Step(p), t, "Step() #1") + assertNoError(Step(p), t, "Step() #2") // Bug exits here. + assertNoError(Step(p), t, "Step() #3") // Third step ought to be possible; program ought not have exited. }) } @@ -2085,8 +2085,8 @@ func TestIssue561(t *testing.T) { // where a breakpoint is also set. withTestProcess("issue561", t, func(p *Process, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture, 10) - assertNoError(p.Continue(), t, "Continue()") - assertNoError(p.Step(), t, "Step()") + assertNoError(Continue(p), t, "Continue()") + assertNoError(Step(p), t, "Step()") _, ln := currentLineNumber(p, t) if ln != 5 { t.Fatalf("wrong line number after Step, expected 5 got %d", ln) @@ -2098,7 +2098,7 @@ func TestStepOut(t *testing.T) { withTestProcess("testnextprog", t, func(p *Process, fixture protest.Fixture) { bp, err := setFunctionBreakpoint(p, "main.helloworld") assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") p.ClearBreakpoint(bp.Addr) f, lno := currentLineNumber(p, t) @@ -2106,7 +2106,7 @@ func TestStepOut(t *testing.T) { t.Fatalf("wrong line number %s:%d, expected %d", f, lno, 13) } - assertNoError(p.StepOut(), t, "StepOut()") + assertNoError(StepOut(p), t, "StepOut()") f, lno = currentLineNumber(p, t) if lno != 35 { @@ -2122,7 +2122,7 @@ func TestStepConcurrentDirect(t *testing.T) { bp, err := p.SetBreakpoint(pc, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") _, err = p.ClearBreakpoint(bp.Addr) assertNoError(err, t, "ClearBreakpoint()") @@ -2171,7 +2171,7 @@ func TestStepConcurrentDirect(t *testing.T) { if i == 0 { count++ } - assertNoError(p.Step(), t, "Step()") + assertNoError(Step(p), t, "Step()") } if count != 100 { @@ -2207,7 +2207,7 @@ func TestStepConcurrentPtr(t *testing.T) { kvals := map[int]int64{} count := 0 for { - err := p.Continue() + err := Continue(p) _, exited := err.(ProcessExitedError) if exited { break @@ -2235,12 +2235,12 @@ func TestStepConcurrentPtr(t *testing.T) { } kvals[gid] = k - assertNoError(p.Step(), t, "Step()") + assertNoError(Step(p), t, "Step()") for nextInProgress(p) { if p.selectedGoroutine.ID == gid { t.Fatalf("step did not step into function call (but internal breakpoints still active?) (%d %d)", gid, p.selectedGoroutine.ID) } - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") } if p.selectedGoroutine.ID != gid { @@ -2272,7 +2272,7 @@ func TestStepOutDefer(t *testing.T) { assertNoError(err, t, "FindFileLocation()") bp, err := p.SetBreakpoint(pc, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") p.ClearBreakpoint(bp.Addr) f, lno := currentLineNumber(p, t) @@ -2280,7 +2280,7 @@ func TestStepOutDefer(t *testing.T) { t.Fatalf("worng line number %s:%d, expected %d", f, lno, 5) } - assertNoError(p.StepOut(), t, "StepOut()") + assertNoError(StepOut(p), t, "StepOut()") f, l, _ := p.BinInfo().goSymTable.PCToLine(currentPC(p, t)) if f == fixture.Source || l == 6 { @@ -2295,10 +2295,10 @@ func TestStepOutDeferReturnAndDirectCall(t *testing.T) { // Here we test the case where the function is called by a deferreturn withTestProcess("defercall", t, func(p *Process, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture, 11) - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") p.ClearBreakpoint(bp.Addr) - assertNoError(p.StepOut(), t, "StepOut()") + assertNoError(StepOut(p), t, "StepOut()") f, ln := currentLineNumber(p, t) if ln != 28 { @@ -2314,7 +2314,7 @@ func TestStepOnCallPtrInstr(t *testing.T) { _, err = p.SetBreakpoint(pc, UserBreakpoint, nil) assertNoError(err, t, "SetBreakpoint()") - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") found := false @@ -2340,7 +2340,7 @@ func TestStepOnCallPtrInstr(t *testing.T) { t.Fatal("Could not find CALL instruction") } - assertNoError(p.Step(), t, "Step()") + assertNoError(Step(p), t, "Step()") f, ln := currentLineNumber(p, t) if ln != 5 { @@ -2355,7 +2355,7 @@ func TestIssue594(t *testing.T) { // In particular the target should be able to cause a nil pointer // dereference panic and recover from it. withTestProcess("issue594", t, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") f, ln := currentLineNumber(p, t) if ln != 21 { t.Fatalf("Program stopped at %s:%d, expected :21", f, ln) @@ -2369,10 +2369,10 @@ func TestStepOutPanicAndDirectCall(t *testing.T) { // Here we test the case where the function is called by a panic withTestProcess("defercall", t, func(p *Process, fixture protest.Fixture) { bp := setFileBreakpoint(p, t, fixture, 17) - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") p.ClearBreakpoint(bp.Addr) - assertNoError(p.StepOut(), t, "StepOut()") + assertNoError(StepOut(p), t, "StepOut()") f, ln := currentLineNumber(p, t) if ln != 5 { @@ -2391,7 +2391,7 @@ func TestWorkDir(t *testing.T) { addr, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 14) assertNoError(err, t, "LineToPC") p.SetBreakpoint(addr, UserBreakpoint, nil) - p.Continue() + Continue(p) v, err := evalVariable(p, "pwd") assertNoError(err, t, "EvalVariable") str := constant.StringVal(v.Value) @@ -2412,7 +2412,7 @@ func TestNegativeIntEvaluation(t *testing.T) { {"ni32", "int32", int64(-5)}, } withTestProcess("testvariables2", t, func(p *Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue()") + assertNoError(Continue(p), t, "Continue()") for _, tc := range testcases { v, err := evalVariable(p, tc.name) assertNoError(err, t, "EvalVariable()") @@ -2431,11 +2431,11 @@ func TestIssue683(t *testing.T) { withTestProcess("issue683", t, func(p *Process, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "main.main") assertNoError(err, t, "setFunctionBreakpoint()") - assertNoError(p.Continue(), t, "First Continue()") + assertNoError(Continue(p), t, "First Continue()") for i := 0; i < 20; i++ { // eventually an error about the source file not being found will be // returned, the important thing is that we shouldn't panic - err := p.Step() + err := Step(p) if err != nil { break } @@ -2446,8 +2446,8 @@ func TestIssue683(t *testing.T) { func TestIssue664(t *testing.T) { withTestProcess("issue664", t, func(p *Process, fixture protest.Fixture) { setFileBreakpoint(p, t, fixture, 4) - assertNoError(p.Continue(), t, "Continue()") - assertNoError(p.Next(), t, "Next()") + assertNoError(Continue(p), t, "Continue()") + assertNoError(Next(p), t, "Next()") f, ln := currentLineNumber(p, t) if ln != 5 { t.Fatalf("Did not continue to line 5: %s:%d", f, ln) @@ -2462,7 +2462,7 @@ func BenchmarkTrace(b *testing.B) { assertNoError(err, b, "setFunctionBreakpoint()") b.ResetTimer() for i := 0; i < b.N; i++ { - assertNoError(p.Continue(), b, "Continue()") + assertNoError(Continue(p), b, "Continue()") s, err := GoroutineScope(p.currentThread) assertNoError(err, b, "Scope()") _, err = s.FunctionArguments(LoadConfig{false, 0, 64, 0, 3}) @@ -2480,9 +2480,9 @@ func TestNextInDeferReturn(t *testing.T) { withTestProcess("defercall", t, func(p *Process, fixture protest.Fixture) { _, err := setFunctionBreakpoint(p, "runtime.deferreturn") assertNoError(err, t, "setFunctionBreakpoint()") - assertNoError(p.Continue(), t, "First Continue()") + assertNoError(Continue(p), t, "First Continue()") for i := 0; i < 20; i++ { - assertNoError(p.Next(), t, fmt.Sprintf("Next() %d", i)) + assertNoError(Next(p), t, fmt.Sprintf("Next() %d", i)) } }) } @@ -2519,7 +2519,7 @@ func TestStacktraceWithBarriers(t *testing.T) { assertNoError(err, t, "setFunctionBreakpoint()") stackBarrierGoids := []int{} for len(stackBarrierGoids) == 0 { - err := p.Continue() + err := Continue(p) if _, exited := err.(ProcessExitedError); exited { t.Logf("Could not run test") return @@ -2555,7 +2555,7 @@ func TestStacktraceWithBarriers(t *testing.T) { t.Logf("stack barrier goids: %v\n", stackBarrierGoids) - assertNoError(p.StepOut(), t, "StepOut()") + assertNoError(StepOut(p), t, "StepOut()") gs, err := p.GoroutinesInfo() assertNoError(err, t, "GoroutinesInfo()") @@ -2628,7 +2628,7 @@ func TestAttachDetach(t *testing.T) { http.Get("http://localhost:9191") }() - assertNoError(p.Continue(), t, "Continue") + assertNoError(Continue(p), t, "Continue") f, ln := currentLineNumber(p, t) if ln != 11 { diff --git a/pkg/proc/proc_unix_test.go b/pkg/proc/proc_unix_test.go index ab45d7c8..fd76c92c 100644 --- a/pkg/proc/proc_unix_test.go +++ b/pkg/proc/proc_unix_test.go @@ -23,7 +23,7 @@ func TestIssue419(t *testing.T) { } } }() - err := p.Continue() + err := Continue(p) if _, exited := err.(ProcessExitedError); !exited { t.Fatalf("Unexpected error after Continue(): %v\n", err) } diff --git a/pkg/proc/threads.go b/pkg/proc/threads.go index 7ca0fb90..558803cf 100644 --- a/pkg/proc/threads.go +++ b/pkg/proc/threads.go @@ -46,6 +46,7 @@ type IThread interface { Registers(floatingPoint bool) (Registers, error) Arch() Arch BinInfo() *BinaryInfo + StepInstruction() error } // Location represents the location of a thread. @@ -149,12 +150,12 @@ func (tbe ThreadBlockedError) Error() string { } // returns topmost frame of g or thread if g is nil -func topframe(g *G, thread *Thread) (Stackframe, error) { +func topframe(g *G, thread IThread) (Stackframe, error) { var frames []Stackframe var err error if g == nil { - if thread.blocked() { + if threadBlocked(thread) { return Stackframe{}, ThreadBlockedError{} } frames, err = ThreadStacktrace(thread, 0) @@ -177,8 +178,10 @@ func topframe(g *G, thread *Thread) (Stackframe, error) { // a breakpoint of kind StepBreakpoint is set on the CALL instruction, // Continue will take care of setting a breakpoint to the destination // once the CALL is reached. -func (dbp *Process) next(stepInto bool) error { - topframe, err := topframe(dbp.selectedGoroutine, dbp.currentThread) +func next(dbp Continuable, stepInto bool) error { + selg := dbp.SelectedGoroutine() + curthread := dbp.CurrentThread() + topframe, err := topframe(selg, curthread) if err != nil { return err } @@ -191,22 +194,22 @@ func (dbp *Process) next(stepInto bool) error { }() csource := filepath.Ext(topframe.Current.File) != ".go" - var thread memoryReadWriter = dbp.currentThread + var thread memoryReadWriter = curthread var regs Registers - if dbp.selectedGoroutine != nil && dbp.selectedGoroutine.thread != nil { - thread = dbp.selectedGoroutine.thread - regs, err = dbp.selectedGoroutine.thread.Registers(false) + if selg != nil && selg.Thread() != nil { + thread = selg.Thread() + regs, err = selg.Thread().Registers(false) if err != nil { return err } } - text, err := disassemble(thread, regs, dbp.breakpoints, dbp.BinInfo(), topframe.FDE.Begin(), topframe.FDE.End()) + text, err := disassemble(thread, regs, dbp.Breakpoints(), dbp.BinInfo(), topframe.FDE.Begin(), topframe.FDE.End()) if err != nil && stepInto { return err } - cond := sameGoroutineCondition(dbp.selectedGoroutine) + cond := sameGoroutineCondition(selg) if stepInto { for _, instr := range text { @@ -215,7 +218,7 @@ func (dbp *Process) next(stepInto bool) error { } if instr.DestLoc != nil && instr.DestLoc.Fn != nil { - if err := dbp.setStepIntoBreakpoint([]AsmInstruction{instr}, cond); err != nil { + if err := setStepIntoBreakpoint(dbp, []AsmInstruction{instr}, cond); err != nil { return err } } else { @@ -242,10 +245,10 @@ func (dbp *Process) next(stepInto bool) error { // Set breakpoint on the most recently deferred function (if any) var deferpc uint64 = 0 - if dbp.selectedGoroutine != nil { - deferPCEntry := dbp.selectedGoroutine.DeferPC() + if selg != nil { + deferPCEntry := selg.DeferPC() if deferPCEntry != 0 { - _, _, deferfn := dbp.bi.goSymTable.PCToLine(deferPCEntry) + _, _, deferfn := dbp.BinInfo().PCToLine(deferPCEntry) var err error deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false) if err != nil { @@ -267,7 +270,7 @@ func (dbp *Process) next(stepInto bool) error { } // Add breakpoints on all the lines in the current function - pcs, err := dbp.bi.lineInfo.AllPCsBetween(topframe.FDE.Begin(), topframe.FDE.End()-1, topframe.Current.File) + pcs, err := dbp.BinInfo().lineInfo.AllPCsBetween(topframe.FDE.Begin(), topframe.FDE.End()-1, topframe.Current.File) if err != nil { return err } @@ -282,8 +285,8 @@ func (dbp *Process) next(stepInto bool) error { } if !covered { - fn := dbp.bi.goSymTable.PCToFunc(topframe.Ret) - if dbp.selectedGoroutine != nil && fn != nil && fn.Name == "runtime.goexit" { + fn := dbp.BinInfo().goSymTable.PCToFunc(topframe.Ret) + if selg != nil && fn != nil && fn.Name == "runtime.goexit" { return nil } } @@ -292,10 +295,10 @@ func (dbp *Process) next(stepInto bool) error { // Add a breakpoint on the return address for the current frame pcs = append(pcs, topframe.Ret) success = true - return dbp.setInternalBreakpoints(topframe.Current.PC, pcs, NextBreakpoint, cond) + return setInternalBreakpoints(dbp, topframe.Current.PC, pcs, NextBreakpoint, cond) } -func (dbp *Process) setStepIntoBreakpoint(text []AsmInstruction, cond ast.Expr) error { +func setStepIntoBreakpoint(dbp Continuable, text []AsmInstruction, cond ast.Expr) error { if len(text) <= 0 { return nil } @@ -337,7 +340,7 @@ func (dbp *Process) setStepIntoBreakpoint(text []AsmInstruction, cond ast.Expr) // setInternalBreakpoints sets a breakpoint to all addresses specified in pcs // skipping over curpc and curpc-1 -func (dbp *Process) setInternalBreakpoints(curpc uint64, pcs []uint64, kind BreakpointKind, cond ast.Expr) error { +func setInternalBreakpoints(dbp Continuable, curpc uint64, pcs []uint64, kind BreakpointKind, cond ast.Expr) error { for i := range pcs { if pcs[i] == curpc || pcs[i] == curpc-1 { continue @@ -495,7 +498,7 @@ func (thread *Thread) SetCurrentBreakpoint() error { return err } thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.checkCondition(thread) - if thread.onTriggeredBreakpoint() { + if thread.CurrentBreakpoint != nil && thread.BreakpointConditionMet { if g, err := GetG(thread); err == nil { thread.CurrentBreakpoint.HitCount[g.ID]++ } @@ -511,15 +514,7 @@ func (thread *Thread) clearBreakpointState() { thread.BreakpointConditionError = nil } -func (thread *Thread) onTriggeredBreakpoint() bool { - return (thread.CurrentBreakpoint != nil) && thread.BreakpointConditionMet -} - -func (thread *Thread) onTriggeredInternalBreakpoint() bool { - return thread.onTriggeredBreakpoint() && thread.CurrentBreakpoint.Internal() -} - -func (thread *Thread) onRuntimeBreakpoint() bool { +func onRuntimeBreakpoint(thread IThread) bool { loc, err := thread.Location() if err != nil { return false @@ -528,11 +523,11 @@ func (thread *Thread) onRuntimeBreakpoint() bool { } // onNextGorutine returns true if this thread is on the goroutine requested by the current 'next' command -func (thread *Thread) onNextGoroutine() (bool, error) { +func onNextGoroutine(thread IThread, breakpoints map[uint64]*Breakpoint) (bool, error) { var bp *Breakpoint - for i := range thread.dbp.breakpoints { - if thread.dbp.breakpoints[i].Internal() { - bp = thread.dbp.breakpoints[i] + for i := range breakpoints { + if breakpoints[i].Internal() { + bp = breakpoints[i] break } } diff --git a/pkg/proc/threads_darwin.go b/pkg/proc/threads_darwin.go index 1e176857..38a935a5 100644 --- a/pkg/proc/threads_darwin.go +++ b/pkg/proc/threads_darwin.go @@ -81,13 +81,14 @@ func (t *Thread) resume() error { return nil } -func (t *Thread) blocked() bool { +func threadBlocked(t IThread) bool { // TODO(dp) cache the func pc to remove this lookup - pc, err := t.PC() + regs, err := t.Registers(false) if err != nil { return false } - fn := t.dbp.bi.goSymTable.PCToFunc(pc) + pc := regs.PC() + fn := t.BinInfo().goSymTable.PCToFunc(pc) if fn == nil { return false } diff --git a/pkg/proc/threads_linux.go b/pkg/proc/threads_linux.go index 1eeaadec..2b276717 100644 --- a/pkg/proc/threads_linux.go +++ b/pkg/proc/threads_linux.go @@ -67,9 +67,13 @@ func (t *Thread) singleStep() (err error) { } } -func (t *Thread) blocked() bool { - pc, _ := t.PC() - fn := t.dbp.bi.goSymTable.PCToFunc(pc) +func threadBlocked(t IThread) bool { + regs, err := t.Registers(false) + if err != nil { + return false + } + pc := regs.PC() + fn := t.BinInfo().goSymTable.PCToFunc(pc) if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) { return true } diff --git a/pkg/proc/threads_windows.go b/pkg/proc/threads_windows.go index b7de8c1e..89445da3 100644 --- a/pkg/proc/threads_windows.go +++ b/pkg/proc/threads_windows.go @@ -103,14 +103,15 @@ func (t *Thread) resume() error { return err } -func (t *Thread) blocked() bool { +func threadBlocked(t IThread) bool { // TODO: Probably incorrect - what are the runtime functions that // indicate blocking on Windows? - pc, err := t.PC() + regs, err := t.Registers(false) if err != nil { return false } - fn := t.dbp.bi.goSymTable.PCToFunc(pc) + pc := regs.PC() + fn := t.BinInfo().goSymTable.PCToFunc(pc) if fn == nil { return false } diff --git a/pkg/target/target.go b/pkg/target/target.go index d6b48cbb..0abda627 100644 --- a/pkg/target/target.go +++ b/pkg/target/target.go @@ -60,16 +60,12 @@ type ThreadInfo interface { // GoroutineInfo is an interface for getting information on running goroutines. type GoroutineInfo interface { - GoroutinesInfo() ([]*proc.G, error) SelectedGoroutine() *proc.G } // ProcessManipulation is an interface for changing the execution state of a process. type ProcessManipulation interface { - Continue() error - Next() error - Step() error - StepOut() error + ContinueOnce() (trapthread proc.IThread, err error) StepInstruction() error SwitchThread(int) error SwitchGoroutine(int) error diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index 6c9eeaa8..b525b883 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -412,7 +412,7 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er switch command.Name { case api.Continue: log.Print("continuing") - err = d.target.Continue() + err = proc.Continue(d.target) if err != nil { if exitedErr, exited := err.(proc.ProcessExitedError); exited { state := &api.DebuggerState{} @@ -432,16 +432,16 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er case api.Next: log.Print("nexting") - err = d.target.Next() + err = proc.Next(d.target) case api.Step: log.Print("stepping") - err = d.target.Step() + err = proc.Step(d.target) case api.StepInstruction: log.Print("single stepping") err = d.target.StepInstruction() case api.StepOut: log.Print("step out") - err = d.target.StepOut() + err = proc.StepOut(d.target) case api.SwitchThread: log.Printf("switching to thread %d", command.ThreadID) err = d.target.SwitchThread(command.ThreadID) @@ -714,7 +714,7 @@ func (d *Debugger) Goroutines() ([]*api.Goroutine, error) { defer d.processMutex.Unlock() goroutines := []*api.Goroutine{} - gs, err := d.target.GoroutinesInfo() + gs, err := proc.GoroutinesInfo(d.target) if err != nil { return nil, err } diff --git a/service/test/variables_test.go b/service/test/variables_test.go index e6d0abf6..109b84da 100644 --- a/service/test/variables_test.go +++ b/service/test/variables_test.go @@ -138,7 +138,7 @@ func TestVariableEvaluation(t *testing.T) { } withTestProcess("testvariables", t, func(p *proc.Process, fixture protest.Fixture) { - err := p.Continue() + err := proc.Continue(p) assertNoError(err, t, "Continue() returned an error") for _, tc := range testcases { @@ -216,7 +216,7 @@ func TestVariableEvaluationShort(t *testing.T) { } withTestProcess("testvariables", t, func(p *proc.Process, fixture protest.Fixture) { - err := p.Continue() + err := proc.Continue(p) assertNoError(err, t, "Continue() returned an error") for _, tc := range testcases { @@ -271,7 +271,7 @@ func TestMultilineVariableEvaluation(t *testing.T) { } withTestProcess("testvariables", t, func(p *proc.Process, fixture protest.Fixture) { - err := p.Continue() + err := proc.Continue(p) assertNoError(err, t, "Continue() returned an error") for _, tc := range testcases { @@ -344,7 +344,7 @@ func TestLocalVariables(t *testing.T) { } withTestProcess("testvariables", t, func(p *proc.Process, fixture protest.Fixture) { - err := p.Continue() + err := proc.Continue(p) assertNoError(err, t, "Continue() returned an error") for _, tc := range testcases { @@ -377,7 +377,7 @@ func TestEmbeddedStruct(t *testing.T) { {"b.s", true, "\"hello\"", "\"hello\"", "string", nil}, {"b2", true, "main.B {main.A: main.A {val: 42}, *main.C: *main.C nil, a: main.A {val: 47}, ptr: *main.A nil}", "main.B {main.A: (*main.A)(0x…", "main.B", nil}, } - assertNoError(p.Continue(), t, "Continue()") + assertNoError(proc.Continue(p), t, "Continue()") for _, tc := range testcases { variable, err := evalVariable(p, tc.name, pnormalLoadConfig) @@ -398,7 +398,7 @@ func TestEmbeddedStruct(t *testing.T) { func TestComplexSetting(t *testing.T) { withTestProcess("testvariables", t, func(p *proc.Process, fixture protest.Fixture) { - err := p.Continue() + err := proc.Continue(p) assertNoError(err, t, "Continue() returned an error") h := func(setExpr, value string) { @@ -644,7 +644,7 @@ func TestEvalExpression(t *testing.T) { } withTestProcess("testvariables2", t, func(p *proc.Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue() returned an error") + assertNoError(proc.Continue(p), t, "Continue() returned an error") for _, tc := range testcases { variable, err := evalVariable(p, tc.name, pnormalLoadConfig) if tc.err == nil { @@ -668,7 +668,7 @@ func TestEvalExpression(t *testing.T) { func TestEvalAddrAndCast(t *testing.T) { withTestProcess("testvariables2", t, func(p *proc.Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue() returned an error") + assertNoError(proc.Continue(p), t, "Continue() returned an error") c1addr, err := evalVariable(p, "&c1", pnormalLoadConfig) assertNoError(err, t, "EvalExpression(&c1)") c1addrstr := api.ConvertVar(c1addr).SinglelineString() @@ -694,7 +694,7 @@ func TestEvalAddrAndCast(t *testing.T) { func TestMapEvaluation(t *testing.T) { withTestProcess("testvariables2", t, func(p *proc.Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue() returned an error") + assertNoError(proc.Continue(p), t, "Continue() returned an error") m1v, err := evalVariable(p, "m1", pnormalLoadConfig) assertNoError(err, t, "EvalVariable()") m1 := api.ConvertVar(m1v) @@ -728,7 +728,7 @@ func TestMapEvaluation(t *testing.T) { func TestUnsafePointer(t *testing.T) { withTestProcess("testvariables2", t, func(p *proc.Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue() returned an error") + assertNoError(proc.Continue(p), t, "Continue() returned an error") up1v, err := evalVariable(p, "up1", pnormalLoadConfig) assertNoError(err, t, "EvalVariable(up1)") up1 := api.ConvertVar(up1v) @@ -765,7 +765,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 withTestProcess("testvariables2", t, func(p *proc.Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue() returned an error") + assertNoError(proc.Continue(p), t, "Continue() returned an error") for _, testcase := range testcases { v, err := evalVariable(p, testcase.name, pnormalLoadConfig) assertNoError(err, t, fmt.Sprintf("EvalVariable(%s)", testcase.name)) @@ -816,7 +816,7 @@ func TestPackageRenames(t *testing.T) { } withTestProcess("pkgrenames", t, func(p *proc.Process, fixture protest.Fixture) { - assertNoError(p.Continue(), t, "Continue() returned an error") + assertNoError(proc.Continue(p), t, "Continue() returned an error") for _, tc := range testcases { variable, err := evalVariable(p, tc.name, pnormalLoadConfig) if tc.err == nil { -- GitLab