proc,service/debugger: introduce TargetGroup abstraction (#3030)
Introduces a new TargetGroup abstraction that can be used to manage multiple related targets. No actual management of child processes is implemented here, this is just a refactoring to make it possible to do that in the future. Updates #2551
This commit is contained in:
parent
1fe03e728c
commit
6f34add5db
@ -639,7 +639,7 @@ func (t *Target) SetWatchpoint(logicalID int, scope *EvalScope, expr string, wty
|
||||
|
||||
func (t *Target) setBreakpointInternal(logicalID int, addr uint64, kind BreakpointKind, wtype WatchType, cond ast.Expr) (*Breakpoint, error) {
|
||||
if valid, err := t.Valid(); !valid {
|
||||
recorded, _ := t.Recorded()
|
||||
recorded, _ := t.recman.Recorded()
|
||||
if !recorded {
|
||||
return nil, err
|
||||
}
|
||||
@ -744,7 +744,7 @@ func (bp *Breakpoint) canOverlap(kind BreakpointKind) bool {
|
||||
// ClearBreakpoint clears the breakpoint at addr.
|
||||
func (t *Target) ClearBreakpoint(addr uint64) error {
|
||||
if valid, err := t.Valid(); !valid {
|
||||
recorded, _ := t.Recorded()
|
||||
recorded, _ := t.recman.Recorded()
|
||||
if !recorded {
|
||||
return err
|
||||
}
|
||||
|
@ -227,7 +227,9 @@ func OpenCore(corePath, exePath string, debugInfoDirs []string) (*proc.Target, e
|
||||
DebugInfoDirs: debugInfoDirs,
|
||||
DisableAsyncPreempt: false,
|
||||
StopReason: proc.StopAttached,
|
||||
CanDump: false})
|
||||
CanDump: false,
|
||||
ContinueOnce: continueOnce,
|
||||
})
|
||||
}
|
||||
|
||||
// BinInfo will return the binary info.
|
||||
@ -417,9 +419,7 @@ func (p *process) ClearInternalBreakpoints() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContinueOnce will always return an error because you
|
||||
// cannot control execution of a core file.
|
||||
func (p *process) ContinueOnce(cctx *proc.ContinueOnceContext) (proc.Thread, proc.StopReason, error) {
|
||||
func continueOnce(procs []proc.ProcessInternal, cctx *proc.ContinueOnceContext) (proc.Thread, proc.StopReason, error) {
|
||||
return nil, proc.StopUnknown, ErrContinueCore
|
||||
}
|
||||
|
||||
|
@ -256,8 +256,9 @@ func TestCore(t *testing.T) {
|
||||
t.Skip("disabled on linux, Github Actions, with PIE buildmode")
|
||||
}
|
||||
p := withCoreFile(t, "panic", "")
|
||||
grp := proc.NewGroup(p)
|
||||
|
||||
recorded, _ := p.Recorded()
|
||||
recorded, _ := grp.Recorded()
|
||||
if !recorded {
|
||||
t.Fatalf("expecting recorded to be true")
|
||||
}
|
||||
|
@ -146,7 +146,8 @@ 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(t *Target, g *G, expr string, retLoadCfg LoadConfig, checkEscape bool) error {
|
||||
func EvalExpressionWithCalls(grp *TargetGroup, g *G, expr string, retLoadCfg LoadConfig, checkEscape bool) error {
|
||||
t := grp.Selected
|
||||
bi := t.BinInfo()
|
||||
if !t.SupportsFunctionCalls() {
|
||||
return errFuncCallUnsupportedBackend
|
||||
@ -201,7 +202,7 @@ func EvalExpressionWithCalls(t *Target, g *G, expr string, retLoadCfg LoadConfig
|
||||
|
||||
contReq, ok := <-continueRequest
|
||||
if contReq.cont {
|
||||
return t.Continue()
|
||||
return grp.Continue()
|
||||
}
|
||||
|
||||
return finishEvalExpressionWithCalls(t, g, contReq, ok)
|
||||
|
@ -709,7 +709,9 @@ func (p *gdbProcess) initialize(path string, debugInfoDirs []string, stopReason
|
||||
DebugInfoDirs: debugInfoDirs,
|
||||
DisableAsyncPreempt: runtime.GOOS == "darwin",
|
||||
StopReason: stopReason,
|
||||
CanDump: runtime.GOOS == "darwin"})
|
||||
CanDump: runtime.GOOS == "darwin",
|
||||
ContinueOnce: continueOnce,
|
||||
})
|
||||
if err != nil {
|
||||
p.Detach(true)
|
||||
return nil, err
|
||||
@ -799,9 +801,11 @@ const (
|
||||
debugServerTargetExcBreakpoint = 0x96
|
||||
)
|
||||
|
||||
// ContinueOnce will continue execution of the process until
|
||||
// a breakpoint is hit or signal is received.
|
||||
func (p *gdbProcess) ContinueOnce(cctx *proc.ContinueOnceContext) (proc.Thread, proc.StopReason, error) {
|
||||
func continueOnce(procs []proc.ProcessInternal, cctx *proc.ContinueOnceContext) (proc.Thread, proc.StopReason, error) {
|
||||
if len(procs) != 1 {
|
||||
panic("not implemented")
|
||||
}
|
||||
p := procs[0].(*gdbProcess)
|
||||
if p.exited {
|
||||
return nil, proc.StopExited, proc.ErrProcessExited{Pid: p.conn.pid}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ func TestMain(m *testing.M) {
|
||||
os.Exit(protest.RunTestsWithFixtures(m))
|
||||
}
|
||||
|
||||
func withTestRecording(name string, t testing.TB, fn func(t *proc.Target, fixture protest.Fixture)) {
|
||||
func withTestRecording(name string, t testing.TB, fn func(grp *proc.TargetGroup, fixture protest.Fixture)) {
|
||||
fixture := protest.BuildFixture(name, 0)
|
||||
protest.MustHaveRecordingAllowed(t)
|
||||
if path, _ := exec.LookPath("rr"); path == "" {
|
||||
@ -36,9 +36,11 @@ func withTestRecording(name string, t testing.TB, fn func(t *proc.Target, fixtur
|
||||
}
|
||||
t.Logf("replaying %q", tracedir)
|
||||
|
||||
defer p.Detach(true)
|
||||
grp := proc.NewGroup(p)
|
||||
|
||||
fn(p, fixture)
|
||||
defer grp.Detach(true)
|
||||
|
||||
fn(grp, fixture)
|
||||
}
|
||||
|
||||
func assertNoError(err error, t testing.TB, s string) {
|
||||
@ -69,25 +71,26 @@ func setFunctionBreakpoint(p *proc.Target, t *testing.T, fname string) *proc.Bre
|
||||
|
||||
func TestRestartAfterExit(t *testing.T) {
|
||||
protest.AllowRecording(t)
|
||||
withTestRecording("testnextprog", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
withTestRecording("testnextprog", t, func(grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||
p := grp.Selected
|
||||
setFunctionBreakpoint(p, t, "main.main")
|
||||
assertNoError(p.Continue(), t, "Continue")
|
||||
assertNoError(grp.Continue(), t, "Continue")
|
||||
loc, err := p.CurrentThread().Location()
|
||||
assertNoError(err, t, "CurrentThread().Location()")
|
||||
err = p.Continue()
|
||||
err = grp.Continue()
|
||||
if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited {
|
||||
t.Fatalf("program did not exit: %v", err)
|
||||
}
|
||||
|
||||
assertNoError(p.Restart(""), t, "Restart")
|
||||
assertNoError(grp.Restart(""), t, "Restart")
|
||||
|
||||
assertNoError(p.Continue(), t, "Continue (after restart)")
|
||||
assertNoError(grp.Continue(), t, "Continue (after restart)")
|
||||
loc2, err := p.CurrentThread().Location()
|
||||
assertNoError(err, t, "CurrentThread().Location() (after restart)")
|
||||
if loc2.Line != loc.Line {
|
||||
t.Fatalf("stopped at %d (expected %d)", loc2.Line, loc.Line)
|
||||
}
|
||||
err = p.Continue()
|
||||
err = grp.Continue()
|
||||
if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited {
|
||||
t.Fatalf("program did not exit (after exit): %v", err)
|
||||
}
|
||||
@ -96,21 +99,22 @@ func TestRestartAfterExit(t *testing.T) {
|
||||
|
||||
func TestRestartDuringStop(t *testing.T) {
|
||||
protest.AllowRecording(t)
|
||||
withTestRecording("testnextprog", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
withTestRecording("testnextprog", t, func(grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||
p := grp.Selected
|
||||
setFunctionBreakpoint(p, t, "main.main")
|
||||
assertNoError(p.Continue(), t, "Continue")
|
||||
assertNoError(grp.Continue(), t, "Continue")
|
||||
loc, err := p.CurrentThread().Location()
|
||||
assertNoError(err, t, "CurrentThread().Location()")
|
||||
|
||||
assertNoError(p.Restart(""), t, "Restart")
|
||||
assertNoError(grp.Restart(""), t, "Restart")
|
||||
|
||||
assertNoError(p.Continue(), t, "Continue (after restart)")
|
||||
assertNoError(grp.Continue(), t, "Continue (after restart)")
|
||||
loc2, err := p.CurrentThread().Location()
|
||||
assertNoError(err, t, "CurrentThread().Location() (after restart)")
|
||||
if loc2.Line != loc.Line {
|
||||
t.Fatalf("stopped at %d (expected %d)", loc2.Line, loc.Line)
|
||||
}
|
||||
err = p.Continue()
|
||||
err = grp.Continue()
|
||||
if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited {
|
||||
t.Fatalf("program did not exit (after exit): %v", err)
|
||||
}
|
||||
@ -137,22 +141,23 @@ func setFileBreakpoint(p *proc.Target, t *testing.T, fixture protest.Fixture, li
|
||||
|
||||
func TestReverseBreakpointCounts(t *testing.T) {
|
||||
protest.AllowRecording(t)
|
||||
withTestRecording("bpcountstest", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
withTestRecording("bpcountstest", t, func(grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||
p := grp.Selected
|
||||
endbp := setFileBreakpoint(p, t, fixture, 28)
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
loc, _ := p.CurrentThread().Location()
|
||||
if loc.PC != endbp.Addr {
|
||||
t.Fatalf("did not reach end of main.main function: %s:%d (%#x)", loc.File, loc.Line, loc.PC)
|
||||
}
|
||||
|
||||
p.ClearBreakpoint(endbp.Addr)
|
||||
assertNoError(p.ChangeDirection(proc.Backward), t, "Switching to backward direction")
|
||||
assertNoError(grp.ChangeDirection(proc.Backward), t, "Switching to backward direction")
|
||||
bp := setFileBreakpoint(p, t, fixture, 12)
|
||||
startbp := setFileBreakpoint(p, t, fixture, 20)
|
||||
|
||||
countLoop:
|
||||
for {
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
loc, _ := p.CurrentThread().Location()
|
||||
switch loc.PC {
|
||||
case startbp.Addr:
|
||||
@ -181,40 +186,41 @@ func TestReverseBreakpointCounts(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func getPosition(p *proc.Target, t *testing.T) (when string, loc *proc.Location) {
|
||||
func getPosition(grp *proc.TargetGroup, t *testing.T) (when string, loc *proc.Location) {
|
||||
var err error
|
||||
when, err = p.When()
|
||||
when, err = grp.When()
|
||||
assertNoError(err, t, "When")
|
||||
loc, err = p.CurrentThread().Location()
|
||||
loc, err = grp.Selected.CurrentThread().Location()
|
||||
assertNoError(err, t, "Location")
|
||||
return
|
||||
}
|
||||
|
||||
func TestCheckpoints(t *testing.T) {
|
||||
protest.AllowRecording(t)
|
||||
withTestRecording("continuetestprog", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
withTestRecording("continuetestprog", t, func(grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||
p := grp.Selected
|
||||
// Continues until start of main.main, record output of 'when'
|
||||
bp := setFunctionBreakpoint(p, t, "main.main")
|
||||
assertNoError(p.Continue(), t, "Continue")
|
||||
when0, loc0 := getPosition(p, t)
|
||||
assertNoError(grp.Continue(), t, "Continue")
|
||||
when0, loc0 := getPosition(grp, t)
|
||||
t.Logf("when0: %q (%#x) %x", when0, loc0.PC, p.CurrentThread().ThreadID())
|
||||
|
||||
// Create a checkpoint and check that the list of checkpoints reflects this
|
||||
cpid, err := p.Checkpoint("checkpoint1")
|
||||
cpid, err := grp.Checkpoint("checkpoint1")
|
||||
if cpid != 1 {
|
||||
t.Errorf("unexpected checkpoint id %d", cpid)
|
||||
}
|
||||
assertNoError(err, t, "Checkpoint")
|
||||
checkpoints, err := p.Checkpoints()
|
||||
checkpoints, err := grp.Checkpoints()
|
||||
assertNoError(err, t, "Checkpoints")
|
||||
if len(checkpoints) != 1 {
|
||||
t.Fatalf("wrong number of checkpoints %v (one expected)", checkpoints)
|
||||
}
|
||||
|
||||
// Move forward with next, check that the output of 'when' changes
|
||||
assertNoError(p.Next(), t, "First Next")
|
||||
assertNoError(p.Next(), t, "Second Next")
|
||||
when1, loc1 := getPosition(p, t)
|
||||
assertNoError(grp.Next(), t, "First Next")
|
||||
assertNoError(grp.Next(), t, "Second Next")
|
||||
when1, loc1 := getPosition(grp, t)
|
||||
t.Logf("when1: %q (%#x) %x", when1, loc1.PC, p.CurrentThread().ThreadID())
|
||||
if loc0.PC == loc1.PC {
|
||||
t.Fatalf("next did not move process %#x", loc0.PC)
|
||||
@ -225,10 +231,10 @@ func TestCheckpoints(t *testing.T) {
|
||||
|
||||
// Move back to checkpoint, check that the output of 'when' is the same as
|
||||
// what it was when we set the breakpoint
|
||||
p.Restart(fmt.Sprintf("c%d", cpid))
|
||||
grp.Restart(fmt.Sprintf("c%d", cpid))
|
||||
g, _ := proc.FindGoroutine(p, 1)
|
||||
p.SwitchGoroutine(g)
|
||||
when2, loc2 := getPosition(p, t)
|
||||
when2, loc2 := getPosition(grp, t)
|
||||
t.Logf("when2: %q (%#x) %x", when2, loc2.PC, p.CurrentThread().ThreadID())
|
||||
if loc2.PC != loc0.PC {
|
||||
t.Fatalf("PC address mismatch %#x != %#x", loc0.PC, loc2.PC)
|
||||
@ -238,9 +244,9 @@ func TestCheckpoints(t *testing.T) {
|
||||
}
|
||||
|
||||
// Move forward with next again, check that the output of 'when' matches
|
||||
assertNoError(p.Next(), t, "First Next")
|
||||
assertNoError(p.Next(), t, "Second Next")
|
||||
when3, loc3 := getPosition(p, t)
|
||||
assertNoError(grp.Next(), t, "First Next")
|
||||
assertNoError(grp.Next(), t, "Second Next")
|
||||
when3, loc3 := getPosition(grp, t)
|
||||
t.Logf("when3: %q (%#x)", when3, loc3.PC)
|
||||
if loc3.PC != loc1.PC {
|
||||
t.Fatalf("PC address mismatch %#x != %#x", loc1.PC, loc3.PC)
|
||||
@ -253,12 +259,12 @@ func TestCheckpoints(t *testing.T) {
|
||||
// output of 'when' again
|
||||
err = p.ClearBreakpoint(bp.Addr)
|
||||
assertNoError(err, t, "ClearBreakpoint")
|
||||
p.Restart(fmt.Sprintf("c%d", cpid))
|
||||
grp.Restart(fmt.Sprintf("c%d", cpid))
|
||||
g, _ = proc.FindGoroutine(p, 1)
|
||||
p.SwitchGoroutine(g)
|
||||
assertNoError(p.Next(), t, "First Next")
|
||||
assertNoError(p.Next(), t, "Second Next")
|
||||
when4, loc4 := getPosition(p, t)
|
||||
assertNoError(grp.Next(), t, "First Next")
|
||||
assertNoError(grp.Next(), t, "Second Next")
|
||||
when4, loc4 := getPosition(grp, t)
|
||||
t.Logf("when4: %q (%#x)", when4, loc4.PC)
|
||||
if loc4.PC != loc1.PC {
|
||||
t.Fatalf("PC address mismatch %#x != %#x", loc1.PC, loc4.PC)
|
||||
@ -268,8 +274,8 @@ func TestCheckpoints(t *testing.T) {
|
||||
}
|
||||
|
||||
// Delete checkpoint, check that the list of checkpoints is updated
|
||||
assertNoError(p.ClearCheckpoint(cpid), t, "ClearCheckpoint")
|
||||
checkpoints, err = p.Checkpoints()
|
||||
assertNoError(grp.ClearCheckpoint(cpid), t, "ClearCheckpoint")
|
||||
checkpoints, err = grp.Checkpoints()
|
||||
assertNoError(err, t, "Checkpoints")
|
||||
if len(checkpoints) != 0 {
|
||||
t.Fatalf("wrong number of checkpoints %v (zero expected)", checkpoints)
|
||||
@ -280,12 +286,13 @@ 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 *proc.Target, fixture protest.Fixture) {
|
||||
withTestRecording("continuetestprog", t, func(grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||
p := grp.Selected
|
||||
bp := setFunctionBreakpoint(p, t, "main.main")
|
||||
assertNoError(p.Continue(), t, "Continue (forward)")
|
||||
assertNoError(grp.Continue(), t, "Continue (forward)")
|
||||
err := p.ClearBreakpoint(bp.Addr)
|
||||
assertNoError(err, t, "ClearBreakpoint")
|
||||
assertNoError(p.ChangeDirection(proc.Backward), t, "Switching to backward direction")
|
||||
assertNoError(p.Continue(), t, "Continue (backward)")
|
||||
assertNoError(grp.ChangeDirection(proc.Backward), t, "Switching to backward direction")
|
||||
assertNoError(grp.Continue(), t, "Continue (backward)")
|
||||
})
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ type ProcessInternal interface {
|
||||
// ErrProcessExited or ErrProcessDetached).
|
||||
Valid() (bool, error)
|
||||
Detach(bool) error
|
||||
ContinueOnce(*ContinueOnceContext) (trapthread Thread, stopReason StopReason, err error)
|
||||
|
||||
// RequestManualStop attempts to stop all the process' threads.
|
||||
RequestManualStop(cctx *ContinueOnceContext) error
|
||||
|
@ -172,9 +172,11 @@ func (dbp *nativeProcess) EraseBreakpoint(bp *proc.Breakpoint) error {
|
||||
return dbp.memthread.clearSoftwareBreakpoint(bp)
|
||||
}
|
||||
|
||||
// ContinueOnce will continue the target until it stops.
|
||||
// This could be the result of a breakpoint or signal.
|
||||
func (dbp *nativeProcess) ContinueOnce(cctx *proc.ContinueOnceContext) (proc.Thread, proc.StopReason, error) {
|
||||
func continueOnce(procs []proc.ProcessInternal, cctx *proc.ContinueOnceContext) (proc.Thread, proc.StopReason, error) {
|
||||
if len(procs) != 1 {
|
||||
panic("not implemented")
|
||||
}
|
||||
dbp := procs[0].(*nativeProcess)
|
||||
if dbp.exited {
|
||||
return nil, proc.StopExited, proc.ErrProcessExited{Pid: dbp.pid}
|
||||
}
|
||||
@ -253,8 +255,9 @@ func (dbp *nativeProcess) initialize(path string, debugInfoDirs []string) (*proc
|
||||
// See: https://go-review.googlesource.com/c/go/+/208126
|
||||
DisableAsyncPreempt: runtime.GOOS == "windows" || runtime.GOOS == "freebsd" || (runtime.GOOS == "linux" && runtime.GOARCH == "arm64"),
|
||||
|
||||
StopReason: stopReason,
|
||||
CanDump: runtime.GOOS == "linux" || runtime.GOOS == "windows",
|
||||
StopReason: stopReason,
|
||||
CanDump: runtime.GOOS == "linux" || runtime.GOOS == "windows",
|
||||
ContinueOnce: continueOnce,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
42
pkg/proc/proc_export_test.go
Normal file
42
pkg/proc/proc_export_test.go
Normal file
@ -0,0 +1,42 @@
|
||||
package proc
|
||||
|
||||
// Wrapper functions so that most tests proc_test.go don't need to worry
|
||||
// about TargetGroup when they just need to resume a single process.
|
||||
|
||||
func newGroupTransient(tgt *Target) *TargetGroup {
|
||||
grp := NewGroup(tgt)
|
||||
tgt.partOfGroup = false
|
||||
return grp
|
||||
}
|
||||
|
||||
func (tgt *Target) Detach(kill bool) error {
|
||||
return tgt.detach(kill)
|
||||
}
|
||||
|
||||
func (tgt *Target) Continue() error {
|
||||
return newGroupTransient(tgt).Continue()
|
||||
}
|
||||
|
||||
func (tgt *Target) Next() error {
|
||||
return newGroupTransient(tgt).Next()
|
||||
}
|
||||
|
||||
func (tgt *Target) Step() error {
|
||||
return newGroupTransient(tgt).Step()
|
||||
}
|
||||
|
||||
func (tgt *Target) StepOut() error {
|
||||
return newGroupTransient(tgt).StepOut()
|
||||
}
|
||||
|
||||
func (tgt *Target) ChangeDirection(dir Direction) error {
|
||||
return tgt.recman.ChangeDirection(dir)
|
||||
}
|
||||
|
||||
func (tgt *Target) StepInstruction() error {
|
||||
return newGroupTransient(tgt).StepInstruction()
|
||||
}
|
||||
|
||||
func (tgt *Target) Recorded() (bool, string) {
|
||||
return tgt.recman.Recorded()
|
||||
}
|
@ -272,16 +272,17 @@ func findFileLocation(p *proc.Target, t *testing.T, file string, lineno int) uin
|
||||
func TestHalt(t *testing.T) {
|
||||
stopChan := make(chan interface{}, 1)
|
||||
withTestProcess("loopprog", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
grp := proc.NewGroup(p)
|
||||
setFunctionBreakpoint(p, t, "main.loop")
|
||||
assertNoError(p.Continue(), t, "Continue")
|
||||
assertNoError(grp.Continue(), t, "Continue")
|
||||
resumeChan := make(chan struct{}, 1)
|
||||
go func() {
|
||||
<-resumeChan
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
stopChan <- p.RequestManualStop()
|
||||
stopChan <- grp.RequestManualStop()
|
||||
}()
|
||||
p.ResumeNotify(resumeChan)
|
||||
assertNoError(p.Continue(), t, "Continue")
|
||||
grp.ResumeNotify(resumeChan)
|
||||
assertNoError(grp.Continue(), t, "Continue")
|
||||
retVal := <-stopChan
|
||||
|
||||
if err, ok := retVal.(error); ok && err != nil {
|
||||
@ -2064,6 +2065,7 @@ func TestCmdLineArgs(t *testing.T) {
|
||||
func TestIssue462(t *testing.T) {
|
||||
skipOn(t, "broken", "windows") // Stacktrace of Goroutine 0 fails with an error
|
||||
withTestProcess("testnextnethttp", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
grp := proc.NewGroup(p)
|
||||
go func() {
|
||||
// Wait for program to start listening.
|
||||
for {
|
||||
@ -2075,10 +2077,10 @@ func TestIssue462(t *testing.T) {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
p.RequestManualStop()
|
||||
grp.RequestManualStop()
|
||||
}()
|
||||
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
_, err := proc.ThreadStacktrace(p.CurrentThread(), 40)
|
||||
assertNoError(err, t, "Stacktrace()")
|
||||
})
|
||||
@ -2638,22 +2640,23 @@ func TestNextBreakpoint(t *testing.T) {
|
||||
func TestNextBreakpointKeepsSteppingBreakpoints(t *testing.T) {
|
||||
protest.AllowRecording(t)
|
||||
withTestProcess("testnextprog", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
p.KeepSteppingBreakpoints = proc.TracepointKeepsSteppingBreakpoints
|
||||
grp := proc.NewGroup(p)
|
||||
grp.KeepSteppingBreakpoints = proc.TracepointKeepsSteppingBreakpoints
|
||||
bp := setFileBreakpoint(p, t, fixture.Source, 34)
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
p.ClearBreakpoint(bp.Addr)
|
||||
|
||||
// Next should be interrupted by a tracepoint on the same goroutine.
|
||||
bp = setFileBreakpoint(p, t, fixture.Source, 14)
|
||||
bp.Logical.Tracepoint = true
|
||||
assertNoError(p.Next(), t, "Next()")
|
||||
assertNoError(grp.Next(), t, "Next()")
|
||||
assertLineNumber(p, t, 14, "wrong line number")
|
||||
if !p.Breakpoints().HasSteppingBreakpoints() {
|
||||
t.Fatal("does not have internal breakpoints after hitting tracepoint on same goroutine")
|
||||
}
|
||||
|
||||
// Continue to complete next.
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
assertLineNumber(p, t, 35, "wrong line number")
|
||||
if p.Breakpoints().HasSteppingBreakpoints() {
|
||||
t.Fatal("has internal breakpoints after completing next")
|
||||
@ -3713,17 +3716,18 @@ func TestIssue1101(t *testing.T) {
|
||||
|
||||
func TestIssue1145(t *testing.T) {
|
||||
withTestProcess("sleep", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
grp := proc.NewGroup(p)
|
||||
setFileBreakpoint(p, t, fixture.Source, 18)
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
resumeChan := make(chan struct{}, 1)
|
||||
p.ResumeNotify(resumeChan)
|
||||
grp.ResumeNotify(resumeChan)
|
||||
go func() {
|
||||
<-resumeChan
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
p.RequestManualStop()
|
||||
grp.RequestManualStop()
|
||||
}()
|
||||
|
||||
assertNoError(p.Next(), t, "Next()")
|
||||
assertNoError(grp.Next(), t, "Next()")
|
||||
if p.Breakpoints().HasSteppingBreakpoints() {
|
||||
t.Fatal("has internal breakpoints after manual stop request")
|
||||
}
|
||||
@ -3732,18 +3736,19 @@ func TestIssue1145(t *testing.T) {
|
||||
|
||||
func TestHaltKeepsSteppingBreakpoints(t *testing.T) {
|
||||
withTestProcess("sleep", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
p.KeepSteppingBreakpoints = proc.HaltKeepsSteppingBreakpoints
|
||||
grp := proc.NewGroup(p)
|
||||
grp.KeepSteppingBreakpoints = proc.HaltKeepsSteppingBreakpoints
|
||||
setFileBreakpoint(p, t, fixture.Source, 18)
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
resumeChan := make(chan struct{}, 1)
|
||||
p.ResumeNotify(resumeChan)
|
||||
grp.ResumeNotify(resumeChan)
|
||||
go func() {
|
||||
<-resumeChan
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
p.RequestManualStop()
|
||||
grp.RequestManualStop()
|
||||
}()
|
||||
|
||||
assertNoError(p.Next(), t, "Next()")
|
||||
assertNoError(grp.Next(), t, "Next()")
|
||||
if !p.Breakpoints().HasSteppingBreakpoints() {
|
||||
t.Fatal("does not have internal breakpoints after manual stop request")
|
||||
}
|
||||
@ -4346,11 +4351,12 @@ 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.Target, fixture protest.Fixture) {
|
||||
grp := proc.NewGroup(p)
|
||||
setFileBreakpoint(p, t, fixture.Source, 7)
|
||||
assertNoError(p.Continue(), t, "First Continue")
|
||||
assertNoError(grp.Continue(), t, "First Continue")
|
||||
assertLineNumber(p, t, 7, "Did not continue to correct location (first continue),")
|
||||
assertNoError(proc.EvalExpressionWithCalls(p, p.SelectedGoroutine(), "getNum()", normalLoadConfig, true), t, "Call")
|
||||
err := p.Continue()
|
||||
assertNoError(proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "getNum()", normalLoadConfig, true), t, "Call")
|
||||
err := grp.Continue()
|
||||
if _, isexited := err.(proc.ErrProcessExited); !isexited {
|
||||
regs, _ := p.CurrentThread().Registers()
|
||||
f, l, _ := p.BinInfo().PCToLine(regs.PC())
|
||||
@ -4577,14 +4583,15 @@ func TestCallConcurrent(t *testing.T) {
|
||||
skipOn(t, "broken", "freebsd")
|
||||
protest.MustSupportFunctionCalls(t, testBackend)
|
||||
withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
grp := proc.NewGroup(p)
|
||||
bp := setFileBreakpoint(p, t, fixture.Source, 24)
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
//_, err := p.ClearBreakpoint(bp.Addr)
|
||||
//assertNoError(err, t, "ClearBreakpoint() returned an error")
|
||||
|
||||
gid1 := p.SelectedGoroutine().ID
|
||||
t.Logf("starting injection in %d / %d", p.SelectedGoroutine().ID, p.CurrentThread().ThreadID())
|
||||
assertNoError(proc.EvalExpressionWithCalls(p, p.SelectedGoroutine(), "Foo(10, 1)", normalLoadConfig, false), t, "EvalExpressionWithCalls()")
|
||||
assertNoError(proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "Foo(10, 1)", normalLoadConfig, false), t, "EvalExpressionWithCalls()")
|
||||
|
||||
returned := testCallConcurrentCheckReturns(p, t, gid1, -1)
|
||||
|
||||
@ -4599,7 +4606,7 @@ func TestCallConcurrent(t *testing.T) {
|
||||
|
||||
gid2 := p.SelectedGoroutine().ID
|
||||
t.Logf("starting second injection in %d / %d", p.SelectedGoroutine().ID, p.CurrentThread().ThreadID())
|
||||
assertNoError(proc.EvalExpressionWithCalls(p, p.SelectedGoroutine(), "Foo(10, 2)", normalLoadConfig, false), t, "EvalExpressioniWithCalls")
|
||||
assertNoError(proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "Foo(10, 2)", normalLoadConfig, false), t, "EvalExpressioniWithCalls")
|
||||
|
||||
for {
|
||||
returned += testCallConcurrentCheckReturns(p, t, gid1, gid2)
|
||||
@ -4607,10 +4614,10 @@ func TestCallConcurrent(t *testing.T) {
|
||||
break
|
||||
}
|
||||
t.Logf("Continuing... %d", returned)
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
}
|
||||
|
||||
p.Continue()
|
||||
grp.Continue()
|
||||
})
|
||||
}
|
||||
|
||||
@ -4964,8 +4971,9 @@ func TestIssue1925(t *testing.T) {
|
||||
// altering the state of the target process.
|
||||
protest.MustSupportFunctionCalls(t, testBackend)
|
||||
withTestProcess("testvariables2", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(proc.EvalExpressionWithCalls(p, p.SelectedGoroutine(), "afunc(2)", normalLoadConfig, true), t, "Call")
|
||||
grp := proc.NewGroup(p)
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
assertNoError(proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "afunc(2)", normalLoadConfig, true), t, "Call")
|
||||
t.Logf("%v\n", p.SelectedGoroutine().CurrentLoc)
|
||||
if loc := p.SelectedGoroutine().CurrentLoc; loc.File != fixture.Source {
|
||||
t.Errorf("wrong location for selected goroutine after call: %s:%d", loc.File, loc.Line)
|
||||
@ -5059,31 +5067,32 @@ func TestStepoutOneliner(t *testing.T) {
|
||||
func TestRequestManualStopWhileStopped(t *testing.T) {
|
||||
// Requesting a manual stop while stopped shouldn't cause problems (issue #2138).
|
||||
withTestProcess("issue2138", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
grp := proc.NewGroup(p)
|
||||
resumed := make(chan struct{})
|
||||
setFileBreakpoint(p, t, fixture.Source, 8)
|
||||
assertNoError(p.Continue(), t, "Continue() 1")
|
||||
p.ResumeNotify(resumed)
|
||||
assertNoError(grp.Continue(), t, "Continue() 1")
|
||||
grp.ResumeNotify(resumed)
|
||||
go func() {
|
||||
<-resumed
|
||||
time.Sleep(1 * time.Second)
|
||||
p.RequestManualStop()
|
||||
grp.RequestManualStop()
|
||||
}()
|
||||
t.Logf("at time.Sleep call")
|
||||
assertNoError(p.Continue(), t, "Continue() 2")
|
||||
assertNoError(grp.Continue(), t, "Continue() 2")
|
||||
t.Logf("manually stopped")
|
||||
p.RequestManualStop()
|
||||
p.RequestManualStop()
|
||||
p.RequestManualStop()
|
||||
grp.RequestManualStop()
|
||||
grp.RequestManualStop()
|
||||
grp.RequestManualStop()
|
||||
|
||||
resumed = make(chan struct{})
|
||||
p.ResumeNotify(resumed)
|
||||
grp.ResumeNotify(resumed)
|
||||
go func() {
|
||||
<-resumed
|
||||
time.Sleep(1 * time.Second)
|
||||
p.RequestManualStop()
|
||||
grp.RequestManualStop()
|
||||
}()
|
||||
t.Logf("resuming sleep")
|
||||
assertNoError(p.Continue(), t, "Continue() 3")
|
||||
assertNoError(grp.Continue(), t, "Continue() 3")
|
||||
t.Logf("done")
|
||||
})
|
||||
}
|
||||
@ -5546,9 +5555,10 @@ func TestWatchpointCounts(t *testing.T) {
|
||||
func TestManualStopWhileStopped(t *testing.T) {
|
||||
// Checks that RequestManualStop sent to a stopped thread does not cause the target process to die.
|
||||
withTestProcess("loopprog", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
grp := proc.NewGroup(p)
|
||||
asyncCont := func(done chan struct{}) {
|
||||
defer close(done)
|
||||
err := p.Continue()
|
||||
err := grp.Continue()
|
||||
t.Logf("%v\n", err)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -5571,9 +5581,9 @@ func TestManualStopWhileStopped(t *testing.T) {
|
||||
done := make(chan struct{})
|
||||
go asyncCont(done)
|
||||
time.Sleep(1 * time.Second)
|
||||
p.RequestManualStop()
|
||||
grp.RequestManualStop()
|
||||
time.Sleep(1 * time.Second)
|
||||
p.RequestManualStop()
|
||||
grp.RequestManualStop()
|
||||
time.Sleep(1 * time.Second)
|
||||
<-done
|
||||
}
|
||||
@ -5581,11 +5591,11 @@ func TestManualStopWhileStopped(t *testing.T) {
|
||||
t.Logf("Continue %d (fast)", i)
|
||||
rch := make(chan struct{})
|
||||
done := make(chan struct{})
|
||||
p.ResumeNotify(rch)
|
||||
grp.ResumeNotify(rch)
|
||||
go asyncCont(done)
|
||||
<-rch
|
||||
p.RequestManualStop()
|
||||
p.RequestManualStop()
|
||||
grp.RequestManualStop()
|
||||
grp.RequestManualStop()
|
||||
<-done
|
||||
}
|
||||
})
|
||||
@ -5865,6 +5875,8 @@ func TestCallInjectionFlagCorruption(t *testing.T) {
|
||||
withTestProcessArgs("badflags", t, ".", []string{"0"}, 0, func(p *proc.Target, fixture protest.Fixture) {
|
||||
mainfn := p.BinInfo().LookupFunc["main.main"]
|
||||
|
||||
grp := proc.NewGroup(p)
|
||||
|
||||
// Find JNZ instruction on line :14
|
||||
var addr uint64
|
||||
text, err := proc.Disassemble(p.Memory(), nil, p.Breakpoints(), p.BinInfo(), mainfn.Entry, mainfn.End)
|
||||
@ -5886,7 +5898,7 @@ func TestCallInjectionFlagCorruption(t *testing.T) {
|
||||
assertNoError(err, t, "SetBreakpoint")
|
||||
|
||||
// Continue to breakpoint
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
assertLineNumber(p, t, 14, "expected line :14")
|
||||
|
||||
// Save RFLAGS register
|
||||
@ -5894,7 +5906,7 @@ func TestCallInjectionFlagCorruption(t *testing.T) {
|
||||
t.Logf("rflags before = %#x", rflagsBeforeCall)
|
||||
|
||||
// Inject call to main.g()
|
||||
assertNoError(proc.EvalExpressionWithCalls(p, p.SelectedGoroutine(), "g()", normalLoadConfig, true), t, "Call")
|
||||
assertNoError(proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "g()", normalLoadConfig, true), t, "Call")
|
||||
|
||||
// Check RFLAGS register after the call
|
||||
rflagsAfterCall := p.BinInfo().Arch.RegistersToDwarfRegisters(0, getRegisters(p, t)).Uint64Val(regnum.AMD64_Rflags)
|
||||
@ -5905,7 +5917,7 @@ func TestCallInjectionFlagCorruption(t *testing.T) {
|
||||
}
|
||||
|
||||
// Single step and check where we end up
|
||||
assertNoError(p.Step(), t, "Step()")
|
||||
assertNoError(grp.Step(), t, "Step()")
|
||||
assertLineNumber(p, t, 17, "expected line :17") // since we passed "0" as argument we should be going into the false branch at line :17
|
||||
})
|
||||
}
|
||||
|
@ -37,24 +37,25 @@ func TestIssue419(t *testing.T) {
|
||||
|
||||
// SIGINT directed at the inferior should be passed along not swallowed by delve
|
||||
withTestProcess("issue419", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||
grp := proc.NewGroup(p)
|
||||
setFunctionBreakpoint(p, t, "main.main")
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
resumeChan := make(chan struct{}, 1)
|
||||
go func() {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
<-resumeChan
|
||||
if p.Pid() <= 0 {
|
||||
// if we don't stop the inferior the test will never finish
|
||||
p.RequestManualStop()
|
||||
err := p.Detach(true)
|
||||
grp.RequestManualStop()
|
||||
err := grp.Detach(true)
|
||||
errChan <- errIssue419{pid: p.Pid(), err: err}
|
||||
return
|
||||
}
|
||||
err := syscall.Kill(p.Pid(), syscall.SIGINT)
|
||||
errChan <- errIssue419{pid: p.Pid(), err: err}
|
||||
}()
|
||||
p.ResumeNotify(resumeChan)
|
||||
errChan <- p.Continue()
|
||||
grp.ResumeNotify(resumeChan)
|
||||
errChan <- grp.Continue()
|
||||
})
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
|
@ -72,7 +72,7 @@ func (t *Target) setStackWatchBreakpoints(scope *EvalScope, watchpoint *Breakpoi
|
||||
retbreaklet.watchpoint = watchpoint
|
||||
retbreaklet.callback = woos
|
||||
|
||||
if recorded, _ := t.Recorded(); recorded && retframe.Current.Fn != nil {
|
||||
if recorded, _ := t.recman.Recorded(); recorded && retframe.Current.Fn != nil {
|
||||
// Must also set a breakpoint on the call instruction immediately
|
||||
// preceding retframe.Current.PC, because the watchpoint could also go out
|
||||
// of scope while we are running backwards.
|
||||
|
@ -36,10 +36,10 @@ const (
|
||||
// Target represents the process being debugged.
|
||||
type Target struct {
|
||||
Process
|
||||
RecordingManipulation
|
||||
|
||||
proc ProcessInternal
|
||||
recman RecordingManipulationInternal
|
||||
proc ProcessInternal
|
||||
recman RecordingManipulationInternal
|
||||
continueOnce ContinueOnceFunc
|
||||
|
||||
pid int
|
||||
|
||||
@ -51,10 +51,6 @@ type Target struct {
|
||||
// CanDump is true if core dumping is supported.
|
||||
CanDump bool
|
||||
|
||||
// KeepSteppingBreakpoints determines whether certain stop reasons (e.g. manual halts)
|
||||
// will keep the stepping breakpoints instead of clearing them.
|
||||
KeepSteppingBreakpoints KeepSteppingBreakpoints
|
||||
|
||||
// currentThread is the thread that will be used by next/step/stepout and to evaluate variables if no goroutine is selected.
|
||||
currentThread Thread
|
||||
|
||||
@ -84,7 +80,7 @@ type Target struct {
|
||||
fakeMemoryRegistry []*compositeMemory
|
||||
fakeMemoryRegistryMap map[string]*compositeMemory
|
||||
|
||||
cctx *ContinueOnceContext
|
||||
partOfGroup bool
|
||||
}
|
||||
|
||||
type KeepSteppingBreakpoints uint8
|
||||
@ -151,6 +147,8 @@ const (
|
||||
StopWatchpoint // The target process hit one or more watchpoints
|
||||
)
|
||||
|
||||
type ContinueOnceFunc func([]ProcessInternal, *ContinueOnceContext) (trapthread Thread, stopReason StopReason, err error)
|
||||
|
||||
// NewTargetConfig contains the configuration for a new Target object,
|
||||
type NewTargetConfig struct {
|
||||
Path string // path of the main executable
|
||||
@ -158,6 +156,7 @@ type NewTargetConfig struct {
|
||||
DisableAsyncPreempt bool // Go 1.14 asynchronous preemption should be disabled
|
||||
StopReason StopReason // Initial stop reason
|
||||
CanDump bool // Can create core dumps (must implement ProcessInternal.MemoryMap)
|
||||
ContinueOnce ContinueOnceFunc
|
||||
}
|
||||
|
||||
// DisableAsyncPreemptEnv returns a process environment (like os.Environ)
|
||||
@ -200,7 +199,7 @@ func NewTarget(p ProcessInternal, pid int, currentThread Thread, cfg NewTargetCo
|
||||
currentThread: currentThread,
|
||||
CanDump: cfg.CanDump,
|
||||
pid: pid,
|
||||
cctx: &ContinueOnceContext{},
|
||||
continueOnce: cfg.ContinueOnce,
|
||||
}
|
||||
|
||||
if recman, ok := p.(RecordingManipulationInternal); ok {
|
||||
@ -208,7 +207,6 @@ func NewTarget(p ProcessInternal, pid int, currentThread Thread, cfg NewTargetCo
|
||||
} else {
|
||||
t.recman = &dummyRecordingManipulation{}
|
||||
}
|
||||
t.RecordingManipulation = t.recman
|
||||
|
||||
g, _ := GetG(currentThread)
|
||||
t.selectedGoroutine = g
|
||||
@ -281,12 +279,18 @@ func (t *Target) ClearCaches() {
|
||||
}
|
||||
}
|
||||
|
||||
// Restart will start the process over from the location specified by the "from" locspec.
|
||||
// Restart will start the process group over from the location specified by the "from" locspec.
|
||||
// This is only useful for recorded targets.
|
||||
// Restarting of a normal process happens at a higher level (debugger.Restart).
|
||||
func (t *Target) Restart(from string) error {
|
||||
t.ClearCaches()
|
||||
currentThread, err := t.recman.Restart(t.cctx, from)
|
||||
func (grp *TargetGroup) Restart(from string) error {
|
||||
if len(grp.targets) != 1 {
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
for _, t := range grp.targets {
|
||||
t.ClearCaches()
|
||||
}
|
||||
t := grp.Selected
|
||||
currentThread, err := t.recman.Restart(grp.cctx, from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -333,11 +337,11 @@ func (p *Target) SwitchThread(tid int) error {
|
||||
return fmt.Errorf("thread %d does not exist", tid)
|
||||
}
|
||||
|
||||
// Detach will detach the target from the underylying process.
|
||||
// detach will detach the target from the underylying process.
|
||||
// This means the debugger will no longer receive events from the process
|
||||
// we were previously debugging.
|
||||
// If kill is true then the process will be killed when we detach.
|
||||
func (t *Target) Detach(kill bool) error {
|
||||
func (t *Target) detach(kill bool) error {
|
||||
if !kill {
|
||||
if t.asyncPreemptChanged {
|
||||
setAsyncPreemptOff(t, t.asyncPreemptOff)
|
||||
@ -475,17 +479,17 @@ func (t *Target) GetBufferedTracepoints() []*UProbeTraceResult {
|
||||
}
|
||||
|
||||
// ResumeNotify specifies a channel that will be closed the next time
|
||||
// Continue finishes resuming the target.
|
||||
func (t *Target) ResumeNotify(ch chan<- struct{}) {
|
||||
// Continue finishes resuming the targets.
|
||||
func (t *TargetGroup) ResumeNotify(ch chan<- struct{}) {
|
||||
t.cctx.ResumeChan = ch
|
||||
}
|
||||
|
||||
// RequestManualStop attempts to stop all the process' threads.
|
||||
func (t *Target) RequestManualStop() error {
|
||||
// RequestManualStop attempts to stop all the processes' threads.
|
||||
func (t *TargetGroup) RequestManualStop() error {
|
||||
t.cctx.StopMu.Lock()
|
||||
defer t.cctx.StopMu.Unlock()
|
||||
t.cctx.manualStopRequested = true
|
||||
return t.proc.RequestManualStop(t.cctx)
|
||||
return t.Selected.proc.RequestManualStop(t.cctx)
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -26,27 +26,32 @@ func (err *ErrNoSourceForPC) Error() string {
|
||||
return fmt.Sprintf("no source for PC %#x", err.pc)
|
||||
}
|
||||
|
||||
// Next continues execution until the next source line.
|
||||
func (dbp *Target) Next() (err error) {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
// Next resumes the processes in the group, continuing the selected target
|
||||
// until the next source line.
|
||||
func (grp *TargetGroup) Next() (err error) {
|
||||
if _, err := grp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if dbp.Breakpoints().HasSteppingBreakpoints() {
|
||||
if grp.HasSteppingBreakpoints() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
|
||||
if err = next(dbp, false, false); err != nil {
|
||||
dbp.ClearSteppingBreakpoints()
|
||||
if err = next(grp.Selected, false, false); err != nil {
|
||||
grp.Selected.ClearSteppingBreakpoints()
|
||||
return
|
||||
}
|
||||
|
||||
return dbp.Continue()
|
||||
return grp.Continue()
|
||||
}
|
||||
|
||||
// Continue continues execution of the debugged
|
||||
// process. It will continue until it hits a breakpoint
|
||||
// processes. It will continue until it hits a breakpoint
|
||||
// or is otherwise stopped.
|
||||
func (dbp *Target) Continue() error {
|
||||
func (grp *TargetGroup) Continue() error {
|
||||
if len(grp.targets) != 1 {
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
dbp := grp.Selected
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -56,29 +61,29 @@ func (dbp *Target) Continue() error {
|
||||
}
|
||||
dbp.Breakpoints().WatchOutOfScope = nil
|
||||
dbp.clearHardcodedBreakpoints()
|
||||
dbp.cctx.CheckAndClearManualStopRequest()
|
||||
grp.cctx.CheckAndClearManualStopRequest()
|
||||
defer func() {
|
||||
// Make sure we clear internal breakpoints if we simultaneously receive a
|
||||
// manual stop request and hit a breakpoint.
|
||||
if dbp.cctx.CheckAndClearManualStopRequest() {
|
||||
if grp.cctx.CheckAndClearManualStopRequest() {
|
||||
dbp.StopReason = StopManual
|
||||
dbp.clearHardcodedBreakpoints()
|
||||
if dbp.KeepSteppingBreakpoints&HaltKeepsSteppingBreakpoints == 0 {
|
||||
if grp.KeepSteppingBreakpoints&HaltKeepsSteppingBreakpoints == 0 {
|
||||
dbp.ClearSteppingBreakpoints()
|
||||
}
|
||||
}
|
||||
}()
|
||||
for {
|
||||
if dbp.cctx.CheckAndClearManualStopRequest() {
|
||||
if grp.cctx.CheckAndClearManualStopRequest() {
|
||||
dbp.StopReason = StopManual
|
||||
dbp.clearHardcodedBreakpoints()
|
||||
if dbp.KeepSteppingBreakpoints&HaltKeepsSteppingBreakpoints == 0 {
|
||||
if grp.KeepSteppingBreakpoints&HaltKeepsSteppingBreakpoints == 0 {
|
||||
dbp.ClearSteppingBreakpoints()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
dbp.ClearCaches()
|
||||
trapthread, stopReason, contOnceErr := dbp.proc.ContinueOnce(dbp.cctx)
|
||||
trapthread, stopReason, contOnceErr := grp.continueOnce([]ProcessInternal{grp.targets[0].proc}, grp.cctx)
|
||||
dbp.StopReason = stopReason
|
||||
|
||||
threads := dbp.ThreadList()
|
||||
@ -136,7 +141,7 @@ func (dbp *Target) Continue() error {
|
||||
if err := conditionErrors(threads); err != nil {
|
||||
return err
|
||||
}
|
||||
if dbp.GetDirection() == Forward {
|
||||
if grp.GetDirection() == Forward {
|
||||
text, err := disassembleCurrentInstruction(dbp, curthread, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -155,7 +160,7 @@ func (dbp *Target) Continue() error {
|
||||
if err := dbp.ClearSteppingBreakpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
return dbp.StepInstruction()
|
||||
return grp.StepInstruction()
|
||||
}
|
||||
} else {
|
||||
curthread.Common().returnValues = curbp.Breakpoint.returnInfo.Collect(dbp, curthread)
|
||||
@ -171,7 +176,7 @@ func (dbp *Target) Continue() error {
|
||||
return err
|
||||
}
|
||||
if onNextGoroutine &&
|
||||
(!isTraceOrTraceReturn(curbp.Breakpoint) || dbp.KeepSteppingBreakpoints&TracepointKeepsSteppingBreakpoints == 0) {
|
||||
(!isTraceOrTraceReturn(curbp.Breakpoint) || grp.KeepSteppingBreakpoints&TracepointKeepsSteppingBreakpoints == 0) {
|
||||
err := dbp.ClearSteppingBreakpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -287,27 +292,27 @@ func stepInstructionOut(dbp *Target, curthread Thread, fnname1, fnname2 string)
|
||||
}
|
||||
}
|
||||
|
||||
// Step will continue until another source line is reached.
|
||||
// Will step into functions.
|
||||
func (dbp *Target) Step() (err error) {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
// Step resumes the processes in the group, continuing the selected target
|
||||
// until the next source line. Will step into functions.
|
||||
func (grp *TargetGroup) Step() (err error) {
|
||||
if _, err := grp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if dbp.Breakpoints().HasSteppingBreakpoints() {
|
||||
if grp.HasSteppingBreakpoints() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
|
||||
if err = next(dbp, true, false); err != nil {
|
||||
_ = dbp.ClearSteppingBreakpoints()
|
||||
if err = next(grp.Selected, true, false); err != nil {
|
||||
_ = grp.Selected.ClearSteppingBreakpoints()
|
||||
return err
|
||||
}
|
||||
|
||||
if bpstate := dbp.CurrentThread().Breakpoint(); bpstate.Breakpoint != nil && bpstate.Active && bpstate.SteppingInto && dbp.GetDirection() == Backward {
|
||||
dbp.ClearSteppingBreakpoints()
|
||||
return dbp.StepInstruction()
|
||||
if bpstate := grp.Selected.CurrentThread().Breakpoint(); bpstate.Breakpoint != nil && bpstate.Active && bpstate.SteppingInto && grp.GetDirection() == Backward {
|
||||
grp.Selected.ClearSteppingBreakpoints()
|
||||
return grp.StepInstruction()
|
||||
}
|
||||
|
||||
return dbp.Continue()
|
||||
return grp.Continue()
|
||||
}
|
||||
|
||||
// sameGoroutineCondition returns an expression that evaluates to true when
|
||||
@ -323,17 +328,19 @@ func frameoffCondition(frame *Stackframe) ast.Expr {
|
||||
return astutil.Eql(astutil.PkgVar("runtime", "frameoff"), astutil.Int(frame.FrameOffset()))
|
||||
}
|
||||
|
||||
// StepOut will continue until the current goroutine exits the
|
||||
// function currently being executed or a deferred function is executed
|
||||
func (dbp *Target) StepOut() error {
|
||||
backward := dbp.GetDirection() == Backward
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
// StepOut resumes the processes in the group, continuing the selected target
|
||||
// until until the current goroutine exits the function currently being
|
||||
// executed or a deferred function is executed
|
||||
func (grp *TargetGroup) StepOut() error {
|
||||
backward := grp.GetDirection() == Backward
|
||||
if _, err := grp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if dbp.Breakpoints().HasSteppingBreakpoints() {
|
||||
if grp.HasSteppingBreakpoints() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
|
||||
dbp := grp.Selected
|
||||
selg := dbp.SelectedGoroutine()
|
||||
curthread := dbp.CurrentThread()
|
||||
|
||||
@ -355,7 +362,7 @@ func (dbp *Target) StepOut() error {
|
||||
}
|
||||
|
||||
success = true
|
||||
return dbp.Continue()
|
||||
return grp.Continue()
|
||||
}
|
||||
|
||||
sameGCond := sameGoroutineCondition(selg)
|
||||
@ -366,7 +373,7 @@ func (dbp *Target) StepOut() error {
|
||||
}
|
||||
|
||||
success = true
|
||||
return dbp.Continue()
|
||||
return grp.Continue()
|
||||
}
|
||||
|
||||
deferpc, err := setDeferBreakpoint(dbp, nil, topframe, sameGCond, false)
|
||||
@ -395,14 +402,15 @@ func (dbp *Target) StepOut() error {
|
||||
}
|
||||
|
||||
success = true
|
||||
return dbp.Continue()
|
||||
return grp.Continue()
|
||||
}
|
||||
|
||||
// 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 *Target) StepInstruction() (err error) {
|
||||
func (grp *TargetGroup) StepInstruction() (err error) {
|
||||
dbp := grp.Selected
|
||||
thread := dbp.CurrentThread()
|
||||
g := dbp.SelectedGoroutine()
|
||||
if g != nil {
|
||||
@ -412,7 +420,7 @@ func (dbp *Target) StepInstruction() (err error) {
|
||||
sameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
|
||||
return err
|
||||
}
|
||||
return dbp.Continue()
|
||||
return grp.Continue()
|
||||
}
|
||||
thread = g.Thread
|
||||
}
|
||||
@ -464,7 +472,7 @@ func (dbp *Target) StepInstruction() (err error) {
|
||||
// when removing instructions belonging to inlined calls we also remove all
|
||||
// instructions belonging to the current inlined call.
|
||||
func next(dbp *Target, stepInto, inlinedStepOut bool) error {
|
||||
backward := dbp.GetDirection() == Backward
|
||||
backward := dbp.recman.GetDirection() == Backward
|
||||
selg := dbp.SelectedGoroutine()
|
||||
curthread := dbp.CurrentThread()
|
||||
topframe, retframe, err := topframe(selg, curthread)
|
||||
@ -1086,7 +1094,7 @@ func (tgt *Target) clearHardcodedBreakpoints() {
|
||||
func (tgt *Target) handleHardcodedBreakpoints(trapthread Thread, threads []Thread) error {
|
||||
mem := tgt.Memory()
|
||||
arch := tgt.BinInfo().Arch
|
||||
recorded, _ := tgt.Recorded()
|
||||
recorded, _ := tgt.recman.Recorded()
|
||||
|
||||
isHardcodedBreakpoint := func(thread Thread, pc uint64) uint64 {
|
||||
for _, bpinstr := range [][]byte{arch.BreakpointInstruction(), arch.AltBreakpointInstruction()} {
|
||||
@ -1150,7 +1158,7 @@ func (tgt *Target) handleHardcodedBreakpoints(trapthread Thread, threads []Threa
|
||||
|
||||
switch {
|
||||
case loc.Fn.Name == "runtime.breakpoint":
|
||||
if recorded, _ := tgt.Recorded(); recorded {
|
||||
if recorded, _ := tgt.recman.Recorded(); recorded {
|
||||
setHardcodedBreakpoint(thread, loc)
|
||||
continue
|
||||
}
|
||||
|
123
pkg/proc/target_group.go
Normal file
123
pkg/proc/target_group.go
Normal file
@ -0,0 +1,123 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TargetGroup reperesents a group of target processes being debugged that
|
||||
// will be resumed and stopped simultaneously.
|
||||
// New targets are automatically added to the group if exec catching is
|
||||
// enabled and the backend supports it, otherwise the group will always
|
||||
// contain a single target process.
|
||||
type TargetGroup struct {
|
||||
targets []*Target
|
||||
Selected *Target
|
||||
|
||||
RecordingManipulation
|
||||
recman RecordingManipulationInternal
|
||||
|
||||
// KeepSteppingBreakpoints determines whether certain stop reasons (e.g. manual halts)
|
||||
// will keep the stepping breakpoints instead of clearing them.
|
||||
KeepSteppingBreakpoints KeepSteppingBreakpoints
|
||||
|
||||
LogicalBreakpoints map[int]*LogicalBreakpoint
|
||||
|
||||
continueOnce ContinueOnceFunc
|
||||
cctx *ContinueOnceContext
|
||||
}
|
||||
|
||||
// NewGroup creates a TargetGroup containing the specified Target.
|
||||
func NewGroup(t *Target) *TargetGroup {
|
||||
if t.partOfGroup {
|
||||
panic("internal error: target is already part of a group")
|
||||
}
|
||||
t.partOfGroup = true
|
||||
return &TargetGroup{
|
||||
RecordingManipulation: t.recman,
|
||||
targets: []*Target{t},
|
||||
Selected: t,
|
||||
cctx: &ContinueOnceContext{},
|
||||
recman: t.recman,
|
||||
LogicalBreakpoints: t.Breakpoints().Logical,
|
||||
continueOnce: t.continueOnce,
|
||||
}
|
||||
}
|
||||
|
||||
// Targets returns a slice of targets in the group.
|
||||
func (grp *TargetGroup) Targets() []*Target {
|
||||
return grp.targets
|
||||
}
|
||||
|
||||
// Valid returns true if any target in the target group is valid.
|
||||
func (grp *TargetGroup) Valid() (bool, error) {
|
||||
var err0 error
|
||||
for _, t := range grp.targets {
|
||||
ok, err := t.Valid()
|
||||
if ok {
|
||||
return true, nil
|
||||
}
|
||||
err0 = err
|
||||
}
|
||||
return false, err0
|
||||
}
|
||||
|
||||
// Detach detaches all targets in the group.
|
||||
func (grp *TargetGroup) Detach(kill bool) error {
|
||||
var errs []string
|
||||
for _, t := range grp.targets {
|
||||
isvalid, _ := t.Valid()
|
||||
if !isvalid {
|
||||
continue
|
||||
}
|
||||
err := t.detach(kill)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Sprintf("could not detach process %d: %v", t.Pid(), err))
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("%s", strings.Join(errs, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasSteppingBreakpoints returns true if any of the targets has stepping breakpoints set.
|
||||
func (grp *TargetGroup) HasSteppingBreakpoints() bool {
|
||||
for _, t := range grp.targets {
|
||||
if t.Breakpoints().HasSteppingBreakpoints() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ClearSteppingBreakpoints removes all stepping breakpoints.
|
||||
func (grp *TargetGroup) ClearSteppingBreakpoints() error {
|
||||
for _, t := range grp.targets {
|
||||
if t.Breakpoints().HasSteppingBreakpoints() {
|
||||
return t.ClearSteppingBreakpoints()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ThreadList returns a list of all threads in all target processes.
|
||||
func (grp *TargetGroup) ThreadList() []Thread {
|
||||
r := []Thread{}
|
||||
for _, t := range grp.targets {
|
||||
r = append(r, t.ThreadList()...)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// TargetForThread returns the target containing the given thread.
|
||||
func (grp *TargetGroup) TargetForThread(thread Thread) *Target {
|
||||
for _, t := range grp.targets {
|
||||
for _, th := range t.ThreadList() {
|
||||
if th == thread {
|
||||
return t
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1232,44 +1232,45 @@ func TestCallFunction(t *testing.T) {
|
||||
}
|
||||
|
||||
withTestProcessArgs("fncall", t, ".", nil, protest.AllNonOptimized, func(p *proc.Target, fixture protest.Fixture) {
|
||||
grp := proc.NewGroup(p)
|
||||
testCallFunctionSetBreakpoint(t, p, fixture)
|
||||
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(grp.Continue(), t, "Continue()")
|
||||
for _, tc := range testcases {
|
||||
testCallFunction(t, p, tc)
|
||||
testCallFunction(t, grp, p, tc)
|
||||
}
|
||||
|
||||
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 12) {
|
||||
for _, tc := range testcases112 {
|
||||
testCallFunction(t, p, tc)
|
||||
testCallFunction(t, grp, p, tc)
|
||||
}
|
||||
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 14) {
|
||||
for _, tc := range testcasesBefore114After112 {
|
||||
testCallFunction(t, p, tc)
|
||||
testCallFunction(t, grp, p, tc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 13) {
|
||||
for _, tc := range testcases113 {
|
||||
testCallFunction(t, p, tc)
|
||||
testCallFunction(t, grp, p, tc)
|
||||
}
|
||||
}
|
||||
|
||||
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 14) {
|
||||
for _, tc := range testcases114 {
|
||||
testCallFunction(t, p, tc)
|
||||
testCallFunction(t, grp, p, tc)
|
||||
}
|
||||
}
|
||||
|
||||
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 17) {
|
||||
for _, tc := range testcases117 {
|
||||
testCallFunction(t, p, tc)
|
||||
testCallFunction(t, grp, p, tc)
|
||||
}
|
||||
}
|
||||
|
||||
// LEAVE THIS AS THE LAST ITEM, IT BREAKS THE TARGET PROCESS!!!
|
||||
testCallFunction(t, p, testCaseCallFunction{"-unsafe escapeArg(&a2)", nil, nil})
|
||||
testCallFunction(t, grp, p, testCaseCallFunction{"-unsafe escapeArg(&a2)", nil, nil})
|
||||
})
|
||||
}
|
||||
|
||||
@ -1284,7 +1285,7 @@ func testCallFunctionSetBreakpoint(t *testing.T, p *proc.Target, fixture protest
|
||||
}
|
||||
}
|
||||
|
||||
func testCallFunction(t *testing.T, p *proc.Target, tc testCaseCallFunction) {
|
||||
func testCallFunction(t *testing.T, grp *proc.TargetGroup, p *proc.Target, tc testCaseCallFunction) {
|
||||
const unsafePrefix = "-unsafe "
|
||||
|
||||
var callExpr, varExpr string
|
||||
@ -1302,7 +1303,7 @@ func testCallFunction(t *testing.T, p *proc.Target, tc testCaseCallFunction) {
|
||||
checkEscape = false
|
||||
}
|
||||
t.Logf("call %q", tc.expr)
|
||||
err := proc.EvalExpressionWithCalls(p, p.SelectedGoroutine(), callExpr, pnormalLoadConfig, checkEscape)
|
||||
err := proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), callExpr, pnormalLoadConfig, checkEscape)
|
||||
if tc.err != nil {
|
||||
t.Logf("\terr = %v\n", err)
|
||||
if err == nil {
|
||||
|
@ -1558,7 +1558,7 @@ func (s *Session) onConfigurationDoneRequest(request *dap.ConfigurationDoneReque
|
||||
}
|
||||
s.send(e)
|
||||
}
|
||||
s.debugger.Target().KeepSteppingBreakpoints = proc.HaltKeepsSteppingBreakpoints | proc.TracepointKeepsSteppingBreakpoints
|
||||
s.debugger.TargetGroup().KeepSteppingBreakpoints = proc.HaltKeepsSteppingBreakpoints | proc.TracepointKeepsSteppingBreakpoints
|
||||
|
||||
s.logToConsole("Type 'dlv help' for list of commands.")
|
||||
s.send(&dap.ConfigurationDoneResponse{Response: *newResponse(request.Request)})
|
||||
|
@ -61,7 +61,7 @@ type Debugger struct {
|
||||
processArgs []string
|
||||
|
||||
targetMutex sync.Mutex
|
||||
target *proc.Target
|
||||
target *proc.TargetGroup
|
||||
|
||||
log *logrus.Entry
|
||||
|
||||
@ -161,7 +161,7 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
||||
err = noDebugErrorWarning(err)
|
||||
return nil, attachErrorMessage(d.config.AttachPid, err)
|
||||
}
|
||||
d.target = p
|
||||
d.target = proc.NewGroup(p)
|
||||
|
||||
case d.config.CoreFile != "":
|
||||
var p *proc.Target
|
||||
@ -178,7 +178,7 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
||||
err = go11DecodeErrorCheck(err)
|
||||
return nil, err
|
||||
}
|
||||
d.target = p
|
||||
d.target = proc.NewGroup(p)
|
||||
if err := d.checkGoVersion(); err != nil {
|
||||
d.target.Detach(true)
|
||||
return nil, err
|
||||
@ -197,7 +197,7 @@ func New(config *Config, processArgs []string) (*Debugger, error) {
|
||||
}
|
||||
if p != nil {
|
||||
// if p == nil and err == nil then we are doing a recording, don't touch d.target
|
||||
d.target = p
|
||||
d.target = proc.NewGroup(p)
|
||||
}
|
||||
if err := d.checkGoVersion(); err != nil {
|
||||
d.target.Detach(true)
|
||||
@ -225,7 +225,7 @@ func (d *Debugger) checkGoVersion() error {
|
||||
// do not do anything if we are still recording
|
||||
return nil
|
||||
}
|
||||
producer := d.target.BinInfo().Producer()
|
||||
producer := d.target.Selected.BinInfo().Producer()
|
||||
if producer == "" {
|
||||
return nil
|
||||
}
|
||||
@ -235,7 +235,7 @@ func (d *Debugger) checkGoVersion() error {
|
||||
func (d *Debugger) TargetGoVersion() string {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
return d.target.BinInfo().Producer()
|
||||
return d.target.Selected.BinInfo().Producer()
|
||||
}
|
||||
|
||||
// Launch will start a process with the given args and working directory.
|
||||
@ -285,7 +285,7 @@ func (d *Debugger) Launch(processArgs []string, wd string) (*proc.Target, error)
|
||||
os.Exit(1)
|
||||
}
|
||||
d.recordingDone()
|
||||
d.target = p
|
||||
d.target = proc.NewGroup(p)
|
||||
if err := d.checkGoVersion(); err != nil {
|
||||
d.log.Error(err)
|
||||
err := d.target.Detach(true)
|
||||
@ -368,7 +368,7 @@ func betterGdbserialLaunchError(p *proc.Target, err error) (*proc.Target, error)
|
||||
func (d *Debugger) ProcessPid() int {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
return d.target.Pid()
|
||||
return d.target.Selected.Pid()
|
||||
}
|
||||
|
||||
// LastModified returns the time that the process' executable was last
|
||||
@ -376,7 +376,7 @@ func (d *Debugger) ProcessPid() int {
|
||||
func (d *Debugger) LastModified() time.Time {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
return d.target.BinInfo().LastModified()
|
||||
return d.target.Selected.BinInfo().LastModified()
|
||||
}
|
||||
|
||||
// FunctionReturnLocations returns all return locations
|
||||
@ -387,7 +387,7 @@ func (d *Debugger) FunctionReturnLocations(fnName string) ([]uint64, error) {
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
var (
|
||||
p = d.target
|
||||
p = d.target.Selected
|
||||
g = p.SelectedGoroutine()
|
||||
)
|
||||
|
||||
@ -463,12 +463,6 @@ func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []
|
||||
return nil, ErrCanNotRestart
|
||||
}
|
||||
|
||||
if valid, _ := d.target.Valid(); valid && !recorded {
|
||||
// Ensure the process is in a PTRACE_STOP.
|
||||
if err := stopProcess(d.target.Pid()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := d.detach(true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -514,9 +508,9 @@ func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []
|
||||
}
|
||||
|
||||
discarded := []api.DiscardedBreakpoint{}
|
||||
p.Breakpoints().Logical = d.target.Breakpoints().Logical
|
||||
d.target = p
|
||||
for _, oldBp := range d.target.Breakpoints().Logical {
|
||||
p.Breakpoints().Logical = d.target.LogicalBreakpoints
|
||||
d.target = proc.NewGroup(p)
|
||||
for _, oldBp := range d.target.LogicalBreakpoints {
|
||||
if oldBp.LogicalID < 0 || !oldBp.Enabled {
|
||||
continue
|
||||
}
|
||||
@ -566,16 +560,19 @@ func (d *Debugger) state(retLoadCfg *proc.LoadConfig) (*api.DebuggerState, error
|
||||
goroutine *api.Goroutine
|
||||
)
|
||||
|
||||
if d.target.SelectedGoroutine() != nil {
|
||||
goroutine = api.ConvertGoroutine(d.target, d.target.SelectedGoroutine())
|
||||
tgt := d.target.Selected
|
||||
|
||||
if tgt.SelectedGoroutine() != nil {
|
||||
goroutine = api.ConvertGoroutine(tgt, tgt.SelectedGoroutine())
|
||||
}
|
||||
|
||||
exited := false
|
||||
if _, err := d.target.Valid(); err != nil {
|
||||
if _, err := tgt.Valid(); err != nil {
|
||||
_, exited = err.(proc.ErrProcessExited)
|
||||
}
|
||||
|
||||
state = &api.DebuggerState{
|
||||
Pid: tgt.Pid(),
|
||||
SelectedGoroutine: goroutine,
|
||||
Exited: exited,
|
||||
}
|
||||
@ -589,22 +586,23 @@ func (d *Debugger) state(retLoadCfg *proc.LoadConfig) (*api.DebuggerState, error
|
||||
}
|
||||
|
||||
state.Threads = append(state.Threads, th)
|
||||
if thread.ThreadID() == d.target.CurrentThread().ThreadID() {
|
||||
if thread.ThreadID() == tgt.CurrentThread().ThreadID() {
|
||||
state.CurrentThread = th
|
||||
}
|
||||
}
|
||||
|
||||
state.NextInProgress = d.target.Breakpoints().HasSteppingBreakpoints()
|
||||
state.NextInProgress = d.target.HasSteppingBreakpoints()
|
||||
|
||||
if recorded, _ := d.target.Recorded(); recorded {
|
||||
state.When, _ = d.target.When()
|
||||
}
|
||||
|
||||
state.WatchOutOfScope = make([]*api.Breakpoint, 0, len(d.target.Breakpoints().WatchOutOfScope))
|
||||
for _, bp := range d.target.Breakpoints().WatchOutOfScope {
|
||||
abp := api.ConvertLogicalBreakpoint(bp.Logical)
|
||||
api.ConvertPhysicalBreakpoints(abp, []*proc.Breakpoint{bp})
|
||||
state.WatchOutOfScope = append(state.WatchOutOfScope, abp)
|
||||
for _, t := range d.target.Targets() {
|
||||
for _, bp := range t.Breakpoints().WatchOutOfScope {
|
||||
abp := api.ConvertLogicalBreakpoint(bp.Logical)
|
||||
api.ConvertPhysicalBreakpoints(abp, []*proc.Breakpoint{bp})
|
||||
state.WatchOutOfScope = append(state.WatchOutOfScope, abp)
|
||||
}
|
||||
}
|
||||
|
||||
return state, nil
|
||||
@ -641,6 +639,15 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
if len(d.target.Targets()) != 1 {
|
||||
//TODO(aarzilli):
|
||||
// - the calls to FindFileLocation and FindFunctionLocation need to be done on all targets
|
||||
// - the Addrs slice and the Addr field need to be converted to a format
|
||||
// that can specify to which target an address belongs when there are
|
||||
// multiple targets (but this must happen in a backwards compatible way)
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
|
||||
var (
|
||||
addrs []uint64
|
||||
err error
|
||||
@ -660,16 +667,19 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
|
||||
if runtime.GOOS == "windows" {
|
||||
// Accept fileName which is case-insensitive and slash-insensitive match
|
||||
fileNameNormalized := strings.ToLower(filepath.ToSlash(fileName))
|
||||
for _, symFile := range d.target.BinInfo().Sources {
|
||||
if fileNameNormalized == strings.ToLower(filepath.ToSlash(symFile)) {
|
||||
fileName = symFile
|
||||
break
|
||||
caseInsensitiveSearch:
|
||||
for _, t := range d.target.Targets() {
|
||||
for _, symFile := range t.BinInfo().Sources {
|
||||
if fileNameNormalized == strings.ToLower(filepath.ToSlash(symFile)) {
|
||||
fileName = symFile
|
||||
break caseInsensitiveSearch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addrs, err = proc.FindFileLocation(d.target, fileName, requestedBp.Line)
|
||||
addrs, err = proc.FindFileLocation(d.target.Selected, fileName, requestedBp.Line)
|
||||
case len(requestedBp.FunctionName) > 0:
|
||||
addrs, err = proc.FindFunctionLocation(d.target, requestedBp.FunctionName, requestedBp.Line)
|
||||
addrs, err = proc.FindFunctionLocation(d.target.Selected, requestedBp.FunctionName, requestedBp.Line)
|
||||
case len(requestedBp.Addrs) > 0:
|
||||
addrs = requestedBp.Addrs
|
||||
default:
|
||||
@ -704,7 +714,10 @@ func (d *Debugger) ConvertThreadBreakpoint(thread proc.Thread) *api.Breakpoint {
|
||||
// createLogicalBreakpoint creates one physical breakpoint for each address
|
||||
// in addrs and associates all of them with the same logical breakpoint.
|
||||
func createLogicalBreakpoint(d *Debugger, addrs []uint64, requestedBp *api.Breakpoint, id int) (*api.Breakpoint, error) {
|
||||
p := d.target
|
||||
if len(d.target.Targets()) != 1 {
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
p := d.target.Selected
|
||||
|
||||
if lbp := p.Breakpoints().Logical[requestedBp.ID]; lbp != nil {
|
||||
abp := d.convertBreakpoint(lbp)
|
||||
@ -752,13 +765,17 @@ func createLogicalBreakpoint(d *Debugger, addrs []uint64, requestedBp *api.Break
|
||||
}
|
||||
|
||||
func (d *Debugger) createPhysicalBreakpoints(lbp *proc.LogicalBreakpoint) error {
|
||||
addrs, err := proc.FindFileLocation(d.target, lbp.File, lbp.Line)
|
||||
if len(d.target.Targets()) != 1 {
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
p := d.target.Selected
|
||||
addrs, err := proc.FindFileLocation(p, lbp.File, lbp.Line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bps := make([]*proc.Breakpoint, len(addrs))
|
||||
for i := range addrs {
|
||||
bps[i], err = d.target.SetBreakpoint(lbp.LogicalID, addrs[i], proc.UserBreakpoint, nil)
|
||||
bps[i], err = p.SetBreakpoint(lbp.LogicalID, addrs[i], proc.UserBreakpoint, nil)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
@ -771,7 +788,7 @@ func (d *Debugger) createPhysicalBreakpoints(lbp *proc.LogicalBreakpoint) error
|
||||
if bp == nil {
|
||||
continue
|
||||
}
|
||||
if err1 := d.target.ClearBreakpoint(bp.Addr); err1 != nil {
|
||||
if err1 := p.ClearBreakpoint(bp.Addr); err1 != nil {
|
||||
return fmt.Errorf("error while creating breakpoint: %v, additionally the breakpoint could not be properly rolled back: %v", err, err1)
|
||||
}
|
||||
}
|
||||
@ -781,12 +798,16 @@ func (d *Debugger) createPhysicalBreakpoints(lbp *proc.LogicalBreakpoint) error
|
||||
}
|
||||
|
||||
func (d *Debugger) clearPhysicalBreakpoints(id int) error {
|
||||
if len(d.target.Targets()) != 1 {
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
p := d.target.Selected
|
||||
var errs []error
|
||||
n := 0
|
||||
for _, bp := range d.target.Breakpoints().M {
|
||||
for _, bp := range p.Breakpoints().M {
|
||||
if bp.LogicalID() == id {
|
||||
n++
|
||||
err := d.target.ClearBreakpoint(bp.Addr)
|
||||
err := p.ClearBreakpoint(bp.Addr)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
@ -817,15 +838,21 @@ func isBreakpointExistsErr(err error) bool {
|
||||
func (d *Debugger) CreateEBPFTracepoint(fnName string) error {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
return d.target.SetEBPFTracepoint(fnName)
|
||||
if len(d.target.Targets()) != 1 {
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
p := d.target.Selected
|
||||
return p.SetEBPFTracepoint(fnName)
|
||||
}
|
||||
|
||||
// amendBreakpoint will update the breakpoint with the matching ID.
|
||||
// It also enables or disables the breakpoint.
|
||||
// We can consume this function to avoid locking a goroutine.
|
||||
func (d *Debugger) amendBreakpoint(amend *api.Breakpoint) error {
|
||||
original := d.target.Breakpoints().Logical[amend.ID]
|
||||
if len(d.target.Targets()) != 1 {
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
original := d.target.LogicalBreakpoints[amend.ID]
|
||||
if original == nil {
|
||||
return fmt.Errorf("no breakpoint with ID %d", amend.ID)
|
||||
}
|
||||
@ -956,15 +983,20 @@ func (d *Debugger) ClearBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint
|
||||
|
||||
// clearBreakpoint clears a breakpoint, we can consume this function to avoid locking a goroutine
|
||||
func (d *Debugger) clearBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint, error) {
|
||||
if len(d.target.Targets()) != 1 {
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
p := d.target.Selected
|
||||
|
||||
if requestedBp.ID <= 0 {
|
||||
bp := d.target.Breakpoints().M[requestedBp.Addr]
|
||||
bp := p.Breakpoints().M[requestedBp.Addr]
|
||||
requestedBp.ID = bp.LogicalID()
|
||||
}
|
||||
|
||||
lbp := d.target.Breakpoints().Logical[requestedBp.ID]
|
||||
lbp := d.target.LogicalBreakpoints[requestedBp.ID]
|
||||
clearedBp := d.convertBreakpoint(lbp)
|
||||
|
||||
delete(d.target.Breakpoints().Logical, requestedBp.ID)
|
||||
delete(d.target.LogicalBreakpoints, requestedBp.ID)
|
||||
|
||||
err := d.clearPhysicalBreakpoints(requestedBp.ID)
|
||||
if err != nil {
|
||||
@ -1006,10 +1038,14 @@ func isBpHitCondNotSatisfiable(bp *api.Breakpoint) bool {
|
||||
func (d *Debugger) Breakpoints(all bool) []*api.Breakpoint {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
if len(d.target.Targets()) != 1 {
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
p := d.target.Selected
|
||||
|
||||
abps := []*api.Breakpoint{}
|
||||
if all {
|
||||
for _, bp := range d.target.Breakpoints().M {
|
||||
for _, bp := range p.Breakpoints().M {
|
||||
var abp *api.Breakpoint
|
||||
if bp.Logical != nil {
|
||||
abp = api.ConvertLogicalBreakpoint(bp.Logical)
|
||||
@ -1021,7 +1057,7 @@ func (d *Debugger) Breakpoints(all bool) []*api.Breakpoint {
|
||||
abps = append(abps, abp)
|
||||
}
|
||||
} else {
|
||||
for _, lbp := range d.target.Breakpoints().Logical {
|
||||
for _, lbp := range d.target.LogicalBreakpoints {
|
||||
abps = append(abps, d.convertBreakpoint(lbp))
|
||||
}
|
||||
}
|
||||
@ -1032,7 +1068,7 @@ func (d *Debugger) Breakpoints(all bool) []*api.Breakpoint {
|
||||
func (d *Debugger) FindBreakpoint(id int) *api.Breakpoint {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
lbp := d.target.Breakpoints().Logical[id]
|
||||
lbp := d.target.LogicalBreakpoints[id]
|
||||
if lbp == nil {
|
||||
return nil
|
||||
}
|
||||
@ -1040,8 +1076,13 @@ func (d *Debugger) FindBreakpoint(id int) *api.Breakpoint {
|
||||
}
|
||||
|
||||
func (d *Debugger) findBreakpoint(id int) []*proc.Breakpoint {
|
||||
if len(d.target.Targets()) != 1 {
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
p := d.target.Selected
|
||||
|
||||
var bps []*proc.Breakpoint
|
||||
for _, bp := range d.target.Breakpoints().M {
|
||||
for _, bp := range p.Breakpoints().M {
|
||||
if bp.LogicalID() == id {
|
||||
bps = append(bps, bp)
|
||||
}
|
||||
@ -1057,7 +1098,7 @@ func (d *Debugger) FindBreakpointByName(name string) *api.Breakpoint {
|
||||
}
|
||||
|
||||
func (d *Debugger) findBreakpointByName(name string) *api.Breakpoint {
|
||||
for _, lbp := range d.target.Breakpoints().Logical {
|
||||
for _, lbp := range d.target.LogicalBreakpoints {
|
||||
if lbp.Name == name {
|
||||
return d.convertBreakpoint(lbp)
|
||||
}
|
||||
@ -1067,12 +1108,17 @@ func (d *Debugger) findBreakpointByName(name string) *api.Breakpoint {
|
||||
|
||||
// CreateWatchpoint creates a watchpoint on the specified expression.
|
||||
func (d *Debugger) CreateWatchpoint(goid, frame, deferredCall int, expr string, wtype api.WatchType) (*api.Breakpoint, error) {
|
||||
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
if len(d.target.Targets()) != 1 {
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
p := d.target.Selected
|
||||
|
||||
s, err := proc.ConvertEvalScope(p, goid, frame, deferredCall)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.breakpointIDCounter++
|
||||
bp, err := d.target.SetWatchpoint(d.breakpointIDCounter, s, expr, proc.WatchType(wtype), nil)
|
||||
bp, err := p.SetWatchpoint(d.breakpointIDCounter, s, expr, proc.WatchType(wtype), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1116,7 +1162,7 @@ func (d *Debugger) FindGoroutine(id int) (*proc.G, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
return proc.FindGoroutine(d.target, id)
|
||||
return proc.FindGoroutine(d.target.Selected, id)
|
||||
}
|
||||
|
||||
func (d *Debugger) setRunning(running bool) {
|
||||
@ -1184,9 +1230,9 @@ func (d *Debugger) Command(command *api.DebuggerCommand, resumeNotify chan struc
|
||||
if command.ReturnInfoLoadConfig == nil {
|
||||
return nil, errors.New("can not call function with nil ReturnInfoLoadConfig")
|
||||
}
|
||||
g := d.target.SelectedGoroutine()
|
||||
g := d.target.Selected.SelectedGoroutine()
|
||||
if command.GoroutineID > 0 {
|
||||
g, err = proc.FindGoroutine(d.target, command.GoroutineID)
|
||||
g, err = proc.FindGoroutine(d.target.Selected, command.GoroutineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1248,14 +1294,14 @@ func (d *Debugger) Command(command *api.DebuggerCommand, resumeNotify chan struc
|
||||
err = d.target.StepOut()
|
||||
case api.SwitchThread:
|
||||
d.log.Debugf("switching to thread %d", command.ThreadID)
|
||||
err = d.target.SwitchThread(command.ThreadID)
|
||||
err = d.target.Selected.SwitchThread(command.ThreadID)
|
||||
withBreakpointInfo = false
|
||||
case api.SwitchGoroutine:
|
||||
d.log.Debugf("switching to goroutine %d", command.GoroutineID)
|
||||
var g *proc.G
|
||||
g, err = proc.FindGoroutine(d.target, command.GoroutineID)
|
||||
g, err = proc.FindGoroutine(d.target.Selected, command.GoroutineID)
|
||||
if err == nil {
|
||||
err = d.target.SwitchGoroutine(g)
|
||||
err = d.target.Selected.SwitchGoroutine(g)
|
||||
}
|
||||
withBreakpointInfo = false
|
||||
case api.Halt:
|
||||
@ -1266,7 +1312,7 @@ func (d *Debugger) Command(command *api.DebuggerCommand, resumeNotify chan struc
|
||||
if err != nil {
|
||||
if pe, ok := err.(proc.ErrProcessExited); ok && command.Name != api.SwitchGoroutine && command.Name != api.SwitchThread {
|
||||
state := &api.DebuggerState{}
|
||||
state.Pid = d.target.Pid()
|
||||
state.Pid = d.target.Selected.Pid()
|
||||
state.Exited = true
|
||||
state.ExitStatus = pe.Status
|
||||
state.Err = pe
|
||||
@ -1298,6 +1344,8 @@ func (d *Debugger) Command(command *api.DebuggerCommand, resumeNotify chan struc
|
||||
}
|
||||
|
||||
func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error {
|
||||
//TODO(aarzilli): this doesn't work when there are multiple targets because the state.Threads slice will contain threads from all targets, not just d.target .Selected
|
||||
|
||||
if state == nil {
|
||||
return nil
|
||||
}
|
||||
@ -1312,15 +1360,15 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
||||
state.Threads[i].BreakpointInfo = bpi
|
||||
|
||||
if bp.Goroutine {
|
||||
g, err := proc.GetG(d.target.CurrentThread())
|
||||
g, err := proc.GetG(d.target.Selected.CurrentThread())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bpi.Goroutine = api.ConvertGoroutine(d.target, g)
|
||||
bpi.Goroutine = api.ConvertGoroutine(d.target.Selected, g)
|
||||
}
|
||||
|
||||
if bp.Stacktrace > 0 {
|
||||
rawlocs, err := proc.ThreadStacktrace(d.target.CurrentThread(), bp.Stacktrace)
|
||||
rawlocs, err := proc.ThreadStacktrace(d.target.Selected.CurrentThread(), bp.Stacktrace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1330,7 +1378,7 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
||||
}
|
||||
}
|
||||
|
||||
thread, found := d.target.FindThread(state.Threads[i].ID)
|
||||
thread, found := d.target.Selected.FindThread(state.Threads[i].ID)
|
||||
if !found {
|
||||
return fmt.Errorf("could not find thread %d", state.Threads[i].ID)
|
||||
}
|
||||
@ -1340,7 +1388,7 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
||||
continue
|
||||
}
|
||||
|
||||
s, err := proc.GoroutineScope(d.target, thread)
|
||||
s, err := proc.GoroutineScope(d.target.Selected, thread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1382,14 +1430,33 @@ func (d *Debugger) Sources(filter string) ([]string, error) {
|
||||
}
|
||||
|
||||
files := []string{}
|
||||
for _, f := range d.target.BinInfo().Sources {
|
||||
if regex.Match([]byte(f)) {
|
||||
files = append(files, f)
|
||||
for _, t := range d.target.Targets() {
|
||||
for _, f := range t.BinInfo().Sources {
|
||||
if regex.Match([]byte(f)) {
|
||||
files = append(files, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(files)
|
||||
files = uniq(files)
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func uniq(s []string) []string {
|
||||
if len(s) <= 0 {
|
||||
return s
|
||||
}
|
||||
src, dst := 1, 1
|
||||
for src < len(s) {
|
||||
if s[src] != s[dst-1] {
|
||||
s[dst] = s[src]
|
||||
dst++
|
||||
}
|
||||
src++
|
||||
}
|
||||
return s[:dst]
|
||||
}
|
||||
|
||||
// Functions returns a list of functions in the target process.
|
||||
func (d *Debugger) Functions(filter string) ([]string, error) {
|
||||
d.targetMutex.Lock()
|
||||
@ -1401,11 +1468,15 @@ func (d *Debugger) Functions(filter string) ([]string, error) {
|
||||
}
|
||||
|
||||
funcs := []string{}
|
||||
for _, f := range d.target.BinInfo().Functions {
|
||||
if regex.MatchString(f.Name) {
|
||||
funcs = append(funcs, f.Name)
|
||||
for _, t := range d.target.Targets() {
|
||||
for _, f := range t.BinInfo().Functions {
|
||||
if regex.MatchString(f.Name) {
|
||||
funcs = append(funcs, f.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(funcs)
|
||||
funcs = uniq(funcs)
|
||||
return funcs, nil
|
||||
}
|
||||
|
||||
@ -1419,17 +1490,22 @@ func (d *Debugger) Types(filter string) ([]string, error) {
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
|
||||
types, err := d.target.BinInfo().Types()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := []string{}
|
||||
|
||||
r := make([]string, 0, len(types))
|
||||
for _, typ := range types {
|
||||
if regex.Match([]byte(typ)) {
|
||||
r = append(r, typ)
|
||||
for _, t := range d.target.Targets() {
|
||||
types, err := t.BinInfo().Types()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, typ := range types {
|
||||
if regex.Match([]byte(typ)) {
|
||||
r = append(r, typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(r)
|
||||
r = uniq(r)
|
||||
|
||||
return r, nil
|
||||
}
|
||||
@ -1439,13 +1515,14 @@ func (d *Debugger) Types(filter string) ([]string, error) {
|
||||
func (d *Debugger) PackageVariables(filter string, cfg proc.LoadConfig) ([]*proc.Variable, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
p := d.target.Selected
|
||||
|
||||
regex, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
|
||||
scope, err := proc.ThreadScope(d.target, d.target.CurrentThread())
|
||||
scope, err := proc.ThreadScope(p, p.CurrentThread())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1467,7 +1544,7 @@ func (d *Debugger) ThreadRegisters(threadID int, floatingPoint bool) (*op.DwarfR
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
thread, found := d.target.FindThread(threadID)
|
||||
thread, found := d.target.Selected.FindThread(threadID)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
||||
}
|
||||
@ -1475,7 +1552,7 @@ func (d *Debugger) ThreadRegisters(threadID int, floatingPoint bool) (*op.DwarfR
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.target.BinInfo().Arch.RegistersToDwarfRegisters(0, regs), nil
|
||||
return d.target.Selected.BinInfo().Arch.RegistersToDwarfRegisters(0, regs), nil
|
||||
}
|
||||
|
||||
// ScopeRegisters returns registers for the specified scope.
|
||||
@ -1483,7 +1560,7 @@ func (d *Debugger) ScopeRegisters(goid, frame, deferredCall int, floatingPoint b
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
s, err := proc.ConvertEvalScope(d.target.Selected, goid, frame, deferredCall)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1492,7 +1569,7 @@ func (d *Debugger) ScopeRegisters(goid, frame, deferredCall int, floatingPoint b
|
||||
|
||||
// DwarfRegisterToString returns the name and value representation of the given register.
|
||||
func (d *Debugger) DwarfRegisterToString(i int, reg *op.DwarfRegister) (string, bool, string) {
|
||||
return d.target.BinInfo().Arch.DwarfRegisterToString(i, reg)
|
||||
return d.target.Selected.BinInfo().Arch.DwarfRegisterToString(i, reg)
|
||||
}
|
||||
|
||||
// LocalVariables returns a list of the local variables.
|
||||
@ -1500,7 +1577,7 @@ func (d *Debugger) LocalVariables(goid, frame, deferredCall int, cfg proc.LoadCo
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
s, err := proc.ConvertEvalScope(d.target.Selected, goid, frame, deferredCall)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1512,7 +1589,7 @@ func (d *Debugger) FunctionArguments(goid, frame, deferredCall int, cfg proc.Loa
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
s, err := proc.ConvertEvalScope(d.target.Selected, goid, frame, deferredCall)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1524,7 +1601,7 @@ func (d *Debugger) Function(goid, frame, deferredCall int, cfg proc.LoadConfig)
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
s, err := proc.ConvertEvalScope(d.target.Selected, goid, frame, deferredCall)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1537,7 +1614,7 @@ func (d *Debugger) EvalVariableInScope(goid, frame, deferredCall int, expr strin
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
s, err := proc.ConvertEvalScope(d.target.Selected, goid, frame, deferredCall)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1558,7 +1635,7 @@ func (d *Debugger) SetVariableInScope(goid, frame, deferredCall int, symbol, val
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
s, err := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
s, err := proc.ConvertEvalScope(d.target.Selected, goid, frame, deferredCall)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1569,7 +1646,7 @@ func (d *Debugger) SetVariableInScope(goid, frame, deferredCall int, symbol, val
|
||||
func (d *Debugger) Goroutines(start, count int) ([]*proc.G, int, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
return proc.GoroutinesInfo(d.target, start, count)
|
||||
return proc.GoroutinesInfo(d.target.Selected, start, count)
|
||||
}
|
||||
|
||||
// FilterGoroutines returns the goroutines in gs that satisfy the specified filters.
|
||||
@ -1583,7 +1660,7 @@ func (d *Debugger) FilterGoroutines(gs []*proc.G, filters []api.ListGoroutinesFi
|
||||
for _, g := range gs {
|
||||
ok := true
|
||||
for i := range filters {
|
||||
if !matchGoroutineFilter(d.target, g, &filters[i]) {
|
||||
if !matchGoroutineFilter(d.target.Selected, g, &filters[i]) {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
@ -1663,13 +1740,13 @@ func (d *Debugger) GroupGoroutines(gs []*proc.G, group *api.GoroutineGroupingOpt
|
||||
case api.GoroutineGoLoc:
|
||||
key = formatLoc(g.Go())
|
||||
case api.GoroutineStartLoc:
|
||||
key = formatLoc(g.StartLoc(d.target))
|
||||
key = formatLoc(g.StartLoc(d.target.Selected))
|
||||
case api.GoroutineLabel:
|
||||
key = fmt.Sprintf("%s=%s", group.GroupByKey, g.Labels()[group.GroupByKey])
|
||||
case api.GoroutineRunning:
|
||||
key = fmt.Sprintf("running=%v", g.Thread != nil)
|
||||
case api.GoroutineUser:
|
||||
key = fmt.Sprintf("user=%v", !g.System(d.target))
|
||||
key = fmt.Sprintf("user=%v", !g.System(d.target.Selected))
|
||||
}
|
||||
if len(groupMembers[key]) < group.MaxGroupMembers {
|
||||
groupMembers[key] = append(groupMembers[key], g)
|
||||
@ -1708,13 +1785,13 @@ func (d *Debugger) Stacktrace(goroutineID, depth int, opts api.StacktraceOptions
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g, err := proc.FindGoroutine(d.target, goroutineID)
|
||||
g, err := proc.FindGoroutine(d.target.Selected, goroutineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g == nil {
|
||||
return proc.ThreadStacktrace(d.target.CurrentThread(), depth)
|
||||
return proc.ThreadStacktrace(d.target.Selected.CurrentThread(), depth)
|
||||
} else {
|
||||
return g.Stacktrace(depth, proc.StacktraceOptions(opts))
|
||||
}
|
||||
@ -1729,7 +1806,7 @@ func (d *Debugger) Ancestors(goroutineID, numAncestors, depth int) ([]api.Ancest
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g, err := proc.FindGoroutine(d.target, goroutineID)
|
||||
g, err := proc.FindGoroutine(d.target.Selected, goroutineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1737,7 +1814,7 @@ func (d *Debugger) Ancestors(goroutineID, numAncestors, depth int) ([]api.Ancest
|
||||
return nil, errors.New("no selected goroutine")
|
||||
}
|
||||
|
||||
ancestors, err := proc.Ancestors(d.target, g, numAncestors)
|
||||
ancestors, err := proc.Ancestors(d.target.Selected, g, numAncestors)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1789,7 +1866,7 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo
|
||||
}
|
||||
if cfg != nil && rawlocs[i].Current.Fn != nil {
|
||||
var err error
|
||||
scope := proc.FrameToScope(d.target, d.target.Memory(), nil, rawlocs[i:]...)
|
||||
scope := proc.FrameToScope(d.target.Selected, d.target.Selected.Memory(), nil, rawlocs[i:]...)
|
||||
locals, err := scope.LocalVariables(*cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -1811,8 +1888,8 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo
|
||||
func (d *Debugger) convertDefers(defers []*proc.Defer) []api.Defer {
|
||||
r := make([]api.Defer, len(defers))
|
||||
for i := range defers {
|
||||
ddf, ddl, ddfn := defers[i].DeferredFunc(d.target)
|
||||
drf, drl, drfn := d.target.BinInfo().PCToLine(defers[i].DeferPC)
|
||||
ddf, ddl, ddfn := defers[i].DeferredFunc(d.target.Selected)
|
||||
drf, drl, drfn := d.target.Selected.BinInfo().PCToLine(defers[i].DeferPC)
|
||||
|
||||
r[i] = api.Defer{
|
||||
DeferredLoc: api.ConvertLocation(proc.Location{
|
||||
@ -1848,7 +1925,7 @@ func (d *Debugger) CurrentPackage() (string, error) {
|
||||
if _, err := d.target.Valid(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
loc, err := d.target.CurrentThread().Location()
|
||||
loc, err := d.target.Selected.CurrentThread().Location()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@ -1863,6 +1940,13 @@ func (d *Debugger) FindLocation(goid, frame, deferredCall int, locStr string, in
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
if len(d.target.Targets()) != 1 {
|
||||
//TODO(aarzilli): if there is more than one target process all must be
|
||||
//searched and the addresses returned need to specify which target process
|
||||
//they belong to.
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
|
||||
if _, err := d.target.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1872,7 +1956,7 @@ func (d *Debugger) FindLocation(goid, frame, deferredCall int, locStr string, in
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d.findLocation(goid, frame, deferredCall, locStr, loc, includeNonExecutableLines, substitutePathRules)
|
||||
return d.findLocation(d.target.Selected, goid, frame, deferredCall, locStr, loc, includeNonExecutableLines, substitutePathRules)
|
||||
}
|
||||
|
||||
// FindLocationSpec will find the location specified by 'locStr' and 'locSpec'.
|
||||
@ -1883,22 +1967,29 @@ func (d *Debugger) FindLocationSpec(goid, frame, deferredCall int, locStr string
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
if len(d.target.Targets()) != 1 {
|
||||
//TODO(aarzilli): if there is more than one target process all must be
|
||||
//searched and the addresses returned need to specify which target process
|
||||
//they belong to.
|
||||
panic("multiple targets not implemented")
|
||||
}
|
||||
|
||||
if _, err := d.target.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d.findLocation(goid, frame, deferredCall, locStr, locSpec, includeNonExecutableLines, substitutePathRules)
|
||||
return d.findLocation(d.target.Selected, goid, frame, deferredCall, locStr, locSpec, includeNonExecutableLines, substitutePathRules)
|
||||
}
|
||||
|
||||
func (d *Debugger) findLocation(goid, frame, deferredCall int, locStr string, locSpec locspec.LocationSpec, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, error) {
|
||||
s, _ := proc.ConvertEvalScope(d.target, goid, frame, deferredCall)
|
||||
func (d *Debugger) findLocation(p *proc.Target, goid, frame, deferredCall int, locStr string, locSpec locspec.LocationSpec, includeNonExecutableLines bool, substitutePathRules [][2]string) ([]api.Location, error) {
|
||||
s, _ := proc.ConvertEvalScope(p, goid, frame, deferredCall)
|
||||
|
||||
locs, err := locSpec.Find(d.target, d.processArgs, s, locStr, includeNonExecutableLines, substitutePathRules)
|
||||
locs, err := locSpec.Find(p, d.processArgs, s, locStr, includeNonExecutableLines, substitutePathRules)
|
||||
for i := range locs {
|
||||
if locs[i].PC == 0 {
|
||||
continue
|
||||
}
|
||||
file, line, fn := d.target.BinInfo().PCToLine(locs[i].PC)
|
||||
file, line, fn := p.BinInfo().PCToLine(locs[i].PC)
|
||||
locs[i].File = file
|
||||
locs[i].Line = line
|
||||
locs[i].Function = api.ConvertFunction(fn)
|
||||
@ -1917,7 +2008,7 @@ func (d *Debugger) Disassemble(goroutineID int, addr1, addr2 uint64) ([]proc.Asm
|
||||
}
|
||||
|
||||
if addr2 == 0 {
|
||||
fn := d.target.BinInfo().PCToFunc(addr1)
|
||||
fn := d.target.Selected.BinInfo().PCToFunc(addr1)
|
||||
if fn == nil {
|
||||
return nil, fmt.Errorf("address %#x does not belong to any function", addr1)
|
||||
}
|
||||
@ -1925,24 +2016,24 @@ func (d *Debugger) Disassemble(goroutineID int, addr1, addr2 uint64) ([]proc.Asm
|
||||
addr2 = fn.End
|
||||
}
|
||||
|
||||
g, err := proc.FindGoroutine(d.target, goroutineID)
|
||||
g, err := proc.FindGoroutine(d.target.Selected, goroutineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
curthread := d.target.CurrentThread()
|
||||
curthread := d.target.Selected.CurrentThread()
|
||||
if g != nil && g.Thread != nil {
|
||||
curthread = g.Thread
|
||||
}
|
||||
regs, _ := curthread.Registers()
|
||||
|
||||
return proc.Disassemble(d.target.Memory(), regs, d.target.Breakpoints(), d.target.BinInfo(), addr1, addr2)
|
||||
return proc.Disassemble(d.target.Selected.Memory(), regs, d.target.Selected.Breakpoints(), d.target.Selected.BinInfo(), addr1, addr2)
|
||||
}
|
||||
|
||||
func (d *Debugger) AsmInstructionText(inst *proc.AsmInstruction, flavour proc.AssemblyFlavour) string {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
return inst.Text(flavour, d.target.BinInfo())
|
||||
return inst.Text(flavour, d.target.Selected.BinInfo())
|
||||
}
|
||||
|
||||
// Recorded returns true if the target is a recording.
|
||||
@ -1962,7 +2053,7 @@ func (d *Debugger) FindThreadReturnValues(id int, cfg proc.LoadConfig) ([]*proc.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
thread, found := d.target.FindThread(id)
|
||||
thread, found := d.target.Selected.FindThread(id)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("could not find thread %d", id)
|
||||
}
|
||||
@ -1995,7 +2086,7 @@ func (d *Debugger) ClearCheckpoint(id int) error {
|
||||
func (d *Debugger) ListDynamicLibraries() []*proc.Image {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
return d.target.BinInfo().Images[1:] // skips the first image because it's the executable file
|
||||
return d.target.Selected.BinInfo().Images[1:] // skips the first image because it's the executable file
|
||||
|
||||
}
|
||||
|
||||
@ -2006,7 +2097,7 @@ func (d *Debugger) ExamineMemory(address uint64, length int) ([]byte, error) {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
|
||||
mem := d.target.Memory()
|
||||
mem := d.target.Selected.Memory()
|
||||
data := make([]byte, length)
|
||||
n, err := mem.ReadMemory(data, address)
|
||||
if err != nil {
|
||||
@ -2038,7 +2129,7 @@ func (d *Debugger) GetVersion(out *api.GetVersionOut) error {
|
||||
}
|
||||
|
||||
if !d.isRecording() && !d.IsRunning() {
|
||||
out.TargetGoVersion = d.target.BinInfo().Producer()
|
||||
out.TargetGoVersion = d.target.Selected.BinInfo().Producer()
|
||||
}
|
||||
|
||||
out.MinSupportedVersionOfGo = fmt.Sprintf("%d.%d.0", goversion.MinSupportedVersionOfGoMajor, goversion.MinSupportedVersionOfGoMinor)
|
||||
@ -2053,7 +2144,7 @@ func (d *Debugger) GetVersion(out *api.GetVersionOut) error {
|
||||
func (d *Debugger) ListPackagesBuildInfo(includeFiles bool) []*proc.PackageBuildInfo {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
return d.target.BinInfo().ListPackagesBuildInfo(includeFiles)
|
||||
return d.target.Selected.BinInfo().ListPackagesBuildInfo(includeFiles)
|
||||
}
|
||||
|
||||
// StopRecording stops a recording (if one is in progress)
|
||||
@ -2072,7 +2163,7 @@ func (d *Debugger) StopRecording() error {
|
||||
func (d *Debugger) StopReason() proc.StopReason {
|
||||
d.targetMutex.Lock()
|
||||
defer d.targetMutex.Unlock()
|
||||
return d.target.StopReason
|
||||
return d.target.Selected.StopReason
|
||||
}
|
||||
|
||||
// LockTarget acquires the target mutex.
|
||||
@ -2090,7 +2181,9 @@ func (d *Debugger) DumpStart(dest string) error {
|
||||
d.targetMutex.Lock()
|
||||
// targetMutex will only be unlocked when the dump is done
|
||||
|
||||
if !d.target.CanDump {
|
||||
//TODO(aarzilli): what do we do if the user switches to a different target after starting a dump but before it's finished?
|
||||
|
||||
if !d.target.Selected.CanDump {
|
||||
d.targetMutex.Unlock()
|
||||
return ErrCoreDumpNotSupported
|
||||
}
|
||||
@ -2120,7 +2213,7 @@ func (d *Debugger) DumpStart(dest string) error {
|
||||
d.dumpState.Err = nil
|
||||
go func() {
|
||||
defer d.targetMutex.Unlock()
|
||||
d.target.Dump(fh, 0, &d.dumpState)
|
||||
d.target.Selected.Dump(fh, 0, &d.dumpState)
|
||||
}()
|
||||
|
||||
return nil
|
||||
@ -2157,11 +2250,15 @@ func (d *Debugger) DumpCancel() error {
|
||||
}
|
||||
|
||||
func (d *Debugger) Target() *proc.Target {
|
||||
return d.target.Selected
|
||||
}
|
||||
|
||||
func (d *Debugger) TargetGroup() *proc.TargetGroup {
|
||||
return d.target
|
||||
}
|
||||
|
||||
func (d *Debugger) BuildID() string {
|
||||
return d.target.BinInfo().BuildID
|
||||
return d.target.Selected.BinInfo().BuildID
|
||||
}
|
||||
|
||||
func (d *Debugger) AttachPid() int {
|
||||
@ -2169,13 +2266,13 @@ func (d *Debugger) AttachPid() int {
|
||||
}
|
||||
|
||||
func (d *Debugger) GetBufferedTracepoints() []api.TracepointResult {
|
||||
traces := d.target.GetBufferedTracepoints()
|
||||
traces := d.target.Selected.GetBufferedTracepoints()
|
||||
if traces == nil {
|
||||
return nil
|
||||
}
|
||||
results := make([]api.TracepointResult, len(traces))
|
||||
for i, trace := range traces {
|
||||
f, l, fn := d.target.BinInfo().PCToLine(uint64(trace.FnAddr))
|
||||
f, l, fn := d.target.Selected.BinInfo().PCToLine(uint64(trace.FnAddr))
|
||||
|
||||
results[i].FunctionName = fn.Name
|
||||
results[i].Line = l
|
||||
|
@ -2,14 +2,9 @@ package debugger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func attachErrorMessage(pid int, err error) error {
|
||||
//TODO: mention certificates?
|
||||
return fmt.Errorf("could not attach to pid %d: %s", pid, err)
|
||||
}
|
||||
|
||||
func stopProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGSTOP)
|
||||
}
|
||||
|
@ -2,13 +2,8 @@ package debugger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func attachErrorMessage(pid int, err error) error {
|
||||
return fmt.Errorf("could not attach to pid %d: %s", pid, err)
|
||||
}
|
||||
|
||||
func stopProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGSTOP)
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
//lint:file-ignore ST1005 errors here can be capitalized
|
||||
@ -32,7 +30,3 @@ func attachErrorMessage(pid int, err error) error {
|
||||
}
|
||||
return fallbackerr
|
||||
}
|
||||
|
||||
func stopProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGSTOP)
|
||||
}
|
||||
|
@ -14,13 +14,6 @@ func attachErrorMessage(pid int, err error) error {
|
||||
return fmt.Errorf("could not attach to pid %d: %s", pid, err)
|
||||
}
|
||||
|
||||
func stopProcess(pid int) error {
|
||||
// We cannot gracefully stop a process on Windows,
|
||||
// so just ignore this request and let `Detach` kill
|
||||
// the process.
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyBinaryFormat(exePath string) error {
|
||||
f, err := os.Open(exePath)
|
||||
if err != nil {
|
||||
|
Loading…
Reference in New Issue
Block a user