proc: introduce IThread interface to abstract threads
This commit is contained in:
parent
97cd3a0afe
commit
510b7db2a7
@ -8,6 +8,7 @@ type Arch interface {
|
||||
BreakpointInstruction() []byte
|
||||
BreakpointSize() int
|
||||
GStructOffset() uint64
|
||||
DerefTLS() bool
|
||||
}
|
||||
|
||||
// AMD64 represents the AMD64 CPU architecture.
|
||||
@ -77,3 +78,9 @@ func (a *AMD64) BreakpointSize() int {
|
||||
func (a *AMD64) GStructOffset() uint64 {
|
||||
return a.gStructOffset
|
||||
}
|
||||
|
||||
// If DerefTLS returns true the value of regs.TLS()+GStructOffset() is a
|
||||
// pointer to the G struct
|
||||
func (a *AMD64) DerefTLS() bool {
|
||||
return a.goos == "windows"
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ func (bp *Breakpoint) checkCondition(thread *Thread) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
if bp.Kind == NextDeferBreakpoint {
|
||||
frames, err := thread.Stacktrace(2)
|
||||
frames, err := ThreadStacktrace(thread, 2)
|
||||
if err == nil {
|
||||
ispanic := len(frames) >= 3 && frames[2].Current.Fn != nil && frames[2].Current.Fn.Name == "runtime.gopanic"
|
||||
isdeferreturn := false
|
||||
@ -129,7 +129,7 @@ func (bp *Breakpoint) checkCondition(thread *Thread) (bool, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
scope, err := thread.GoroutineScope()
|
||||
scope, err := GoroutineScope(thread)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
@ -16,26 +16,35 @@ const (
|
||||
IntelFlavour
|
||||
)
|
||||
|
||||
func (dbp *Process) Disassemble(g *G, startPC, endPC uint64) ([]AsmInstruction, error) {
|
||||
// DisassembleInfo is the subset of target.Interface used by Disassemble.
|
||||
type DisassembleInfo interface {
|
||||
CurrentThread() IThread
|
||||
Breakpoints() map[uint64]*Breakpoint
|
||||
BinInfo() *BinaryInfo
|
||||
}
|
||||
|
||||
// Disassemble disassembles target memory between startPC and endPC, marking
|
||||
// the current instruction being executed in goroutine g.
|
||||
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble will evaluate the argument of the CALL instruction using the thread's registers
|
||||
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC
|
||||
func Disassemble(dbp DisassembleInfo, g *G, startPC, endPC uint64) ([]AsmInstruction, error) {
|
||||
if g == nil {
|
||||
regs, _ := dbp.currentThread.Registers(false)
|
||||
return Disassemble(dbp.currentThread, regs, dbp.breakpoints, &dbp.bi, startPC, endPC)
|
||||
ct := dbp.CurrentThread()
|
||||
regs, _ := ct.Registers(false)
|
||||
return disassemble(ct, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC)
|
||||
}
|
||||
|
||||
var regs Registers
|
||||
thread := dbp.currentThread
|
||||
var mem memoryReadWriter = dbp.CurrentThread()
|
||||
if g.thread != nil {
|
||||
thread = g.thread
|
||||
mem = g.thread
|
||||
regs, _ = g.thread.Registers(false)
|
||||
}
|
||||
|
||||
return Disassemble(thread, regs, dbp.breakpoints, &dbp.bi, startPC, endPC)
|
||||
return disassemble(mem, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC)
|
||||
}
|
||||
|
||||
// Disassemble disassembles target memory between startPC and endPC
|
||||
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble will evaluate the argument of the CALL instruction using the thread's registers
|
||||
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC
|
||||
func Disassemble(memrw memoryReadWriter, regs Registers, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, startPC, endPC uint64) ([]AsmInstruction, error) {
|
||||
func disassemble(memrw memoryReadWriter, regs Registers, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, startPC, endPC uint64) ([]AsmInstruction, error) {
|
||||
mem, err := memrw.readMemory(uintptr(startPC), int(endPC-startPC))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -146,7 +146,7 @@ func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64,
|
||||
// FirstPCAfterPrologue returns the address of the first instruction after the prologue for function fn
|
||||
// If sameline is set FirstPCAfterPrologue will always return an address associated with the same line as fn.Entry
|
||||
func FirstPCAfterPrologue(mem memoryReadWriter, breakpoints map[uint64]*Breakpoint, bi *BinaryInfo, fn *gosym.Func, sameline bool) (uint64, error) {
|
||||
text, err := Disassemble(mem, nil, breakpoints, bi, fn.Entry, fn.End)
|
||||
text, err := disassemble(mem, nil, breakpoints, bi, fn.Entry, fn.End)
|
||||
if err != nil {
|
||||
return fn.Entry, err
|
||||
}
|
||||
|
@ -149,11 +149,20 @@ func (dbp *Process) SelectedGoroutine() *G {
|
||||
return dbp.selectedGoroutine
|
||||
}
|
||||
|
||||
func (dbp *Process) Threads() map[int]*Thread {
|
||||
return dbp.threads
|
||||
func (dbp *Process) ThreadList() []IThread {
|
||||
r := make([]IThread, 0, len(dbp.threads))
|
||||
for _, v := range dbp.threads {
|
||||
r = append(r, v)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (dbp *Process) CurrentThread() *Thread {
|
||||
func (dbp *Process) FindThread(threadID int) (IThread, bool) {
|
||||
th, ok := dbp.threads[threadID]
|
||||
return th, ok
|
||||
}
|
||||
|
||||
func (dbp *Process) CurrentThread() IThread {
|
||||
return dbp.currentThread
|
||||
}
|
||||
|
||||
@ -396,7 +405,7 @@ func (dbp *Process) Continue() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
text, err := Disassemble(dbp.currentThread, regs, dbp.breakpoints, &dbp.bi, pc, pc+maxInstructionLength)
|
||||
text, err := disassemble(dbp.currentThread, regs, dbp.breakpoints, dbp.BinInfo(), pc, pc+maxInstructionLength)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -526,12 +535,12 @@ func (dbp *Process) StepInstruction() (err error) {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
dbp.selectedGoroutine.thread.clearBreakpointState()
|
||||
err = dbp.selectedGoroutine.thread.StepInstruction()
|
||||
dbp.selectedGoroutine.thread.(*Thread).clearBreakpointState()
|
||||
err = dbp.selectedGoroutine.thread.(*Thread).StepInstruction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dbp.selectedGoroutine.thread.SetCurrentBreakpoint()
|
||||
return dbp.selectedGoroutine.thread.(*Thread).SetCurrentBreakpoint()
|
||||
}
|
||||
|
||||
// StepOut will continue until the current goroutine exits the
|
||||
@ -597,7 +606,7 @@ func (dbp *Process) SwitchThread(tid int) error {
|
||||
}
|
||||
if th, ok := dbp.threads[tid]; ok {
|
||||
dbp.currentThread = th
|
||||
dbp.selectedGoroutine, _ = dbp.currentThread.GetG()
|
||||
dbp.selectedGoroutine, _ = GetG(dbp.currentThread)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("thread %d does not exist", tid)
|
||||
@ -609,7 +618,7 @@ func (dbp *Process) SwitchGoroutine(gid int) error {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
g, err := dbp.FindGoroutine(gid)
|
||||
g, err := FindGoroutine(dbp, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -618,7 +627,7 @@ func (dbp *Process) SwitchGoroutine(gid int) error {
|
||||
return nil
|
||||
}
|
||||
if g.thread != nil {
|
||||
return dbp.SwitchThread(g.thread.ID)
|
||||
return dbp.SwitchThread(g.thread.ThreadID())
|
||||
}
|
||||
dbp.selectedGoroutine = g
|
||||
return nil
|
||||
@ -644,7 +653,7 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) {
|
||||
if dbp.threads[i].blocked() {
|
||||
continue
|
||||
}
|
||||
g, _ := dbp.threads[i].GetG()
|
||||
g, _ := GetG(dbp.threads[i])
|
||||
if g != nil {
|
||||
threadg[g.ID] = dbp.threads[i]
|
||||
}
|
||||
@ -673,7 +682,7 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) {
|
||||
allgptr := binary.LittleEndian.Uint64(faddr)
|
||||
|
||||
for i := uint64(0); i < allglen; i++ {
|
||||
gvar, err := dbp.currentThread.newGVariable(uintptr(allgptr+(i*uint64(dbp.bi.arch.PtrSize()))), true)
|
||||
gvar, err := newGVariable(dbp.currentThread, uintptr(allgptr+(i*uint64(dbp.bi.arch.PtrSize()))), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -698,7 +707,7 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) {
|
||||
return allg, nil
|
||||
}
|
||||
|
||||
func (g *G) Thread() *Thread {
|
||||
func (g *G) Thread() IThread {
|
||||
return g.thread
|
||||
}
|
||||
|
||||
@ -795,7 +804,7 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e
|
||||
// because without calling SetGStructOffset we can not read the G struct of currentThread
|
||||
// but without calling updateThreadList we can not examine memory to determine
|
||||
// the offset of g struct inside TLS
|
||||
dbp.selectedGoroutine, _ = dbp.currentThread.GetG()
|
||||
dbp.selectedGoroutine, _ = GetG(dbp.currentThread)
|
||||
|
||||
panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0)
|
||||
if err == nil {
|
||||
@ -875,11 +884,16 @@ func (scope *EvalScope) getGoInformation() (ver GoVersion, isextld bool, err err
|
||||
return
|
||||
}
|
||||
|
||||
type GoroutinesInfo interface {
|
||||
SelectedGoroutine() *G
|
||||
GoroutinesInfo() ([]*G, error)
|
||||
}
|
||||
|
||||
// FindGoroutine returns a G struct representing the goroutine
|
||||
// specified by `gid`.
|
||||
func (dbp *Process) FindGoroutine(gid int) (*G, error) {
|
||||
func FindGoroutine(dbp GoroutinesInfo, gid int) (*G, error) {
|
||||
if gid == -1 {
|
||||
return dbp.selectedGoroutine, nil
|
||||
return dbp.SelectedGoroutine(), nil
|
||||
}
|
||||
|
||||
gs, err := dbp.GoroutinesInfo()
|
||||
@ -894,23 +908,29 @@ func (dbp *Process) FindGoroutine(gid int) (*G, error) {
|
||||
return nil, fmt.Errorf("Unknown goroutine %d", gid)
|
||||
}
|
||||
|
||||
// EvalScopeConvertible is a subset of target.Interface with the methods
|
||||
// used by ConvertEvalScope/GoroutinesInfo/etc.
|
||||
type EvalScopeConvertible interface {
|
||||
GoroutinesInfo
|
||||
CurrentThread() IThread
|
||||
BinInfo() *BinaryInfo
|
||||
}
|
||||
|
||||
// ConvertEvalScope returns a new EvalScope in the context of the
|
||||
// specified goroutine ID and stack frame.
|
||||
func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) {
|
||||
if dbp.exited {
|
||||
return nil, &ProcessExitedError{}
|
||||
}
|
||||
g, err := dbp.FindGoroutine(gid)
|
||||
func ConvertEvalScope(dbp EvalScopeConvertible, gid, frame int) (*EvalScope, error) {
|
||||
ct := dbp.CurrentThread()
|
||||
g, err := FindGoroutine(dbp, gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if g == nil {
|
||||
return dbp.currentThread.ThreadScope()
|
||||
return ThreadScope(ct)
|
||||
}
|
||||
|
||||
var thread *Thread
|
||||
var thread memoryReadWriter
|
||||
if g.thread == nil {
|
||||
thread = dbp.currentThread
|
||||
thread = ct
|
||||
} else {
|
||||
thread = g.thread
|
||||
}
|
||||
@ -929,6 +949,11 @@ func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) {
|
||||
return &EvalScope{PC, CFA, thread, g.variable, dbp.BinInfo()}, nil
|
||||
}
|
||||
|
||||
// FrameToScope returns a new EvalScope for this frame
|
||||
func FrameToScope(p EvalScopeConvertible, frame Stackframe) *EvalScope {
|
||||
return &EvalScope{frame.Current.PC, frame.CFA, p.CurrentThread(), nil, p.BinInfo()}
|
||||
}
|
||||
|
||||
func (dbp *Process) postExit() {
|
||||
dbp.exited = true
|
||||
close(dbp.ptraceChan)
|
||||
|
@ -435,7 +435,7 @@ func TestNextConcurrent(t *testing.T) {
|
||||
_, err = p.ClearBreakpoint(bp.Addr)
|
||||
assertNoError(err, t, "ClearBreakpoint()")
|
||||
for _, tc := range testcases {
|
||||
g, err := p.currentThread.GetG()
|
||||
g, err := GetG(p.currentThread)
|
||||
assertNoError(err, t, "GetG()")
|
||||
if p.selectedGoroutine.ID != g.ID {
|
||||
t.Fatalf("SelectedGoroutine not CurrentThread's goroutine: %d %d", g.ID, p.selectedGoroutine.ID)
|
||||
@ -474,7 +474,7 @@ func TestNextConcurrentVariant2(t *testing.T) {
|
||||
initVval, _ := constant.Int64Val(initV.Value)
|
||||
assertNoError(err, t, "EvalVariable")
|
||||
for _, tc := range testcases {
|
||||
g, err := p.currentThread.GetG()
|
||||
g, err := GetG(p.currentThread)
|
||||
assertNoError(err, t, "GetG()")
|
||||
if p.selectedGoroutine.ID != g.ID {
|
||||
t.Fatalf("SelectedGoroutine not CurrentThread's goroutine: %d %d", g.ID, p.selectedGoroutine.ID)
|
||||
@ -586,6 +586,17 @@ func TestRuntimeBreakpoint(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func returnAddress(thread IThread) (uint64, error) {
|
||||
locations, err := ThreadStacktrace(thread, 2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(locations) < 2 {
|
||||
return 0, NoReturnAddr{locations[0].Current.Fn.BaseName()}
|
||||
}
|
||||
return locations[1].Current.PC, nil
|
||||
}
|
||||
|
||||
func TestFindReturnAddress(t *testing.T) {
|
||||
withTestProcess("testnextprog", t, func(p *Process, fixture protest.Fixture) {
|
||||
start, _, err := p.BinInfo().goSymTable.LineToPC(fixture.Source, 24)
|
||||
@ -600,7 +611,7 @@ func TestFindReturnAddress(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
addr, err := p.currentThread.ReturnAddress()
|
||||
addr, err := returnAddress(p.currentThread)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -624,7 +635,7 @@ func TestFindReturnAddressTopOfStackFn(t *testing.T) {
|
||||
if err := p.Continue(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := p.currentThread.ReturnAddress(); err == nil {
|
||||
if _, err := returnAddress(p.currentThread); err == nil {
|
||||
t.Fatal("expected error to be returned")
|
||||
}
|
||||
})
|
||||
@ -726,7 +737,7 @@ func TestStacktrace(t *testing.T) {
|
||||
|
||||
for i := range stacks {
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
locations, err := p.currentThread.Stacktrace(40)
|
||||
locations, err := ThreadStacktrace(p.currentThread, 40)
|
||||
assertNoError(err, t, "Stacktrace()")
|
||||
|
||||
if len(locations) != len(stacks[i])+2 {
|
||||
@ -754,7 +765,7 @@ func TestStacktrace2(t *testing.T) {
|
||||
withTestProcess("retstack", t, func(p *Process, fixture protest.Fixture) {
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
|
||||
locations, err := p.currentThread.Stacktrace(40)
|
||||
locations, err := ThreadStacktrace(p.currentThread, 40)
|
||||
assertNoError(err, t, "Stacktrace()")
|
||||
if !stackMatch([]loc{{-1, "main.f"}, {16, "main.main"}}, locations, false) {
|
||||
for i := range locations {
|
||||
@ -764,7 +775,7 @@ func TestStacktrace2(t *testing.T) {
|
||||
}
|
||||
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
locations, err = p.currentThread.Stacktrace(40)
|
||||
locations, err = ThreadStacktrace(p.currentThread, 40)
|
||||
assertNoError(err, t, "Stacktrace()")
|
||||
if !stackMatch([]loc{{-1, "main.g"}, {17, "main.main"}}, locations, false) {
|
||||
for i := range locations {
|
||||
@ -883,7 +894,7 @@ func testGSupportFunc(name string, t *testing.T, p *Process, fixture protest.Fix
|
||||
|
||||
assertNoError(p.Continue(), t, name+": Continue()")
|
||||
|
||||
g, err := p.currentThread.GetG()
|
||||
g, err := GetG(p.currentThread)
|
||||
assertNoError(err, t, name+": GetG()")
|
||||
|
||||
if g == nil {
|
||||
@ -1011,7 +1022,7 @@ func TestIssue239(t *testing.T) {
|
||||
}
|
||||
|
||||
func evalVariable(p *Process, symbol string) (*Variable, error) {
|
||||
scope, err := p.currentThread.GoroutineScope()
|
||||
scope, err := GoroutineScope(p.currentThread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -1019,7 +1030,7 @@ func evalVariable(p *Process, symbol string) (*Variable, error) {
|
||||
}
|
||||
|
||||
func setVariable(p *Process, symbol, value string) error {
|
||||
scope, err := p.currentThread.GoroutineScope()
|
||||
scope, err := GoroutineScope(p.currentThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1140,7 +1151,7 @@ func TestFrameEvaluation(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
|
||||
scope, err := p.ConvertEvalScope(g.ID, frame)
|
||||
scope, err := ConvertEvalScope(p, g.ID, frame)
|
||||
assertNoError(err, t, "ConvertEvalScope()")
|
||||
t.Logf("scope = %v", scope)
|
||||
v, err := scope.EvalVariable("i", normalLoadConfig)
|
||||
@ -1161,11 +1172,11 @@ func TestFrameEvaluation(t *testing.T) {
|
||||
|
||||
// Testing evaluation on frames
|
||||
assertNoError(p.Continue(), t, "Continue() 2")
|
||||
g, err := p.currentThread.GetG()
|
||||
g, err := GetG(p.currentThread)
|
||||
assertNoError(err, t, "GetG()")
|
||||
|
||||
for i := 0; i <= 3; i++ {
|
||||
scope, err := p.ConvertEvalScope(g.ID, i+1)
|
||||
scope, err := ConvertEvalScope(p, g.ID, i+1)
|
||||
assertNoError(err, t, fmt.Sprintf("ConvertEvalScope() on frame %d", i+1))
|
||||
v, err := scope.EvalVariable("n", normalLoadConfig)
|
||||
assertNoError(err, t, fmt.Sprintf("EvalVariable() on frame %d", i+1))
|
||||
@ -1194,7 +1205,7 @@ func TestPointerSetting(t *testing.T) {
|
||||
pval(1)
|
||||
|
||||
// change p1 to point to i2
|
||||
scope, err := p.currentThread.GoroutineScope()
|
||||
scope, err := GoroutineScope(p.currentThread)
|
||||
assertNoError(err, t, "Scope()")
|
||||
i2addr, err := scope.EvalExpression("i2", normalLoadConfig)
|
||||
assertNoError(err, t, "EvalExpression()")
|
||||
@ -1333,7 +1344,7 @@ func TestBreakpointCountsWithDetection(t *testing.T) {
|
||||
if th.CurrentBreakpoint == nil {
|
||||
continue
|
||||
}
|
||||
scope, err := th.GoroutineScope()
|
||||
scope, err := GoroutineScope(th)
|
||||
assertNoError(err, t, "Scope()")
|
||||
v, err := scope.EvalVariable("i", normalLoadConfig)
|
||||
assertNoError(err, t, "evalVariable")
|
||||
@ -1466,7 +1477,7 @@ func TestPointerLoops(t *testing.T) {
|
||||
func BenchmarkLocalVariables(b *testing.B) {
|
||||
withTestProcess("testvariables", b, func(p *Process, fixture protest.Fixture) {
|
||||
assertNoError(p.Continue(), b, "Continue() returned an error")
|
||||
scope, err := p.currentThread.GoroutineScope()
|
||||
scope, err := GoroutineScope(p.currentThread)
|
||||
assertNoError(err, b, "Scope()")
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := scope.LocalVariables(normalLoadConfig)
|
||||
@ -1600,7 +1611,7 @@ func TestIssue332_Part1(t *testing.T) {
|
||||
assertNoError(err, t, "SetBreakpoint()")
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
assertNoError(p.Next(), t, "first Next()")
|
||||
locations, err := p.currentThread.Stacktrace(2)
|
||||
locations, err := ThreadStacktrace(p.currentThread, 2)
|
||||
assertNoError(err, t, "Stacktrace()")
|
||||
if locations[0].Call.Fn == nil {
|
||||
t.Fatalf("Not on a function")
|
||||
@ -1629,7 +1640,7 @@ func TestIssue332_Part2(t *testing.T) {
|
||||
// step until we enter changeMe
|
||||
for {
|
||||
assertNoError(p.Step(), t, "Step()")
|
||||
locations, err := p.currentThread.Stacktrace(2)
|
||||
locations, err := ThreadStacktrace(p.currentThread, 2)
|
||||
assertNoError(err, t, "Stacktrace()")
|
||||
if locations[0].Call.Fn == nil {
|
||||
t.Fatalf("Not on a function")
|
||||
@ -1692,7 +1703,7 @@ func TestPackageVariables(t *testing.T) {
|
||||
withTestProcess("testvariables", t, func(p *Process, fixture protest.Fixture) {
|
||||
err := p.Continue()
|
||||
assertNoError(err, t, "Continue()")
|
||||
scope, err := p.currentThread.GoroutineScope()
|
||||
scope, err := GoroutineScope(p.currentThread)
|
||||
assertNoError(err, t, "Scope()")
|
||||
vars, err := scope.PackageVariables(normalLoadConfig)
|
||||
assertNoError(err, t, "PackageVariables()")
|
||||
@ -1795,7 +1806,7 @@ func TestIssue462(t *testing.T) {
|
||||
}()
|
||||
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
_, err := p.currentThread.Stacktrace(40)
|
||||
_, err := ThreadStacktrace(p.currentThread, 40)
|
||||
assertNoError(err, t, "Stacktrace()")
|
||||
})
|
||||
}
|
||||
@ -2141,7 +2152,7 @@ func TestStepConcurrentDirect(t *testing.T) {
|
||||
// loop exited
|
||||
break
|
||||
}
|
||||
frames, err := p.currentThread.Stacktrace(20)
|
||||
frames, err := ThreadStacktrace(p.currentThread, 20)
|
||||
if err != nil {
|
||||
t.Errorf("Could not get stacktrace of goroutine %d\n", p.selectedGoroutine.ID)
|
||||
} else {
|
||||
@ -2316,7 +2327,7 @@ func TestStepOnCallPtrInstr(t *testing.T) {
|
||||
assertNoError(err, t, "PC()")
|
||||
regs, err := p.currentThread.Registers(false)
|
||||
assertNoError(err, t, "Registers()")
|
||||
text, err := Disassemble(p.currentThread, regs, p.breakpoints, p.BinInfo(), pc, pc+maxInstructionLength)
|
||||
text, err := disassemble(p.currentThread, regs, p.breakpoints, p.BinInfo(), pc, pc+maxInstructionLength)
|
||||
assertNoError(err, t, "Disassemble()")
|
||||
if text[0].IsCall() {
|
||||
found = true
|
||||
@ -2452,7 +2463,7 @@ func BenchmarkTrace(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
assertNoError(p.Continue(), b, "Continue()")
|
||||
s, err := p.currentThread.GoroutineScope()
|
||||
s, err := GoroutineScope(p.currentThread)
|
||||
assertNoError(err, b, "Scope()")
|
||||
_, err = s.FunctionArguments(LoadConfig{false, 0, 64, 0, 3})
|
||||
assertNoError(err, b, "FunctionArguments()")
|
||||
|
@ -38,39 +38,14 @@ type Stackframe struct {
|
||||
addrret uint64
|
||||
}
|
||||
|
||||
// FrameToScope returns a new EvalScope for this frame
|
||||
func (p *Process) FrameToScope(frame Stackframe) *EvalScope {
|
||||
return &EvalScope{frame.Current.PC, frame.CFA, p.currentThread, nil, p.BinInfo()}
|
||||
}
|
||||
|
||||
// ReturnAddress returns the return address of the function
|
||||
// this thread is executing.
|
||||
func (t *Thread) ReturnAddress() (uint64, error) {
|
||||
locations, err := t.Stacktrace(2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(locations) < 2 {
|
||||
return 0, NoReturnAddr{locations[0].Current.Fn.BaseName()}
|
||||
}
|
||||
return locations[1].Current.PC, nil
|
||||
}
|
||||
|
||||
func (t *Thread) stackIterator(stkbar []savedLR, stkbarPos int) (*stackIterator, error) {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newStackIterator(&t.dbp.bi, t, regs.PC(), regs.SP(), regs.BP(), stkbar, stkbarPos), nil
|
||||
}
|
||||
|
||||
// Stacktrace returns the stack trace for thread.
|
||||
// Note the locations in the array are return addresses not call addresses.
|
||||
func (t *Thread) Stacktrace(depth int) ([]Stackframe, error) {
|
||||
it, err := t.stackIterator(nil, -1)
|
||||
func ThreadStacktrace(thread IThread, depth int) ([]Stackframe, error) {
|
||||
regs, err := thread.Registers(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
it := newStackIterator(thread.BinInfo(), thread, regs.PC(), regs.SP(), regs.BP(), nil, -1)
|
||||
return it.stacktrace(depth)
|
||||
}
|
||||
|
||||
@ -80,7 +55,11 @@ func (g *G) stackIterator() (*stackIterator, error) {
|
||||
return nil, err
|
||||
}
|
||||
if g.thread != nil {
|
||||
return g.thread.stackIterator(stkbar, g.stkbarPos)
|
||||
regs, err := g.thread.Registers(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newStackIterator(g.variable.bi, g.thread, regs.PC(), regs.SP(), regs.BP(), stkbar, g.stkbarPos), nil
|
||||
}
|
||||
return newStackIterator(g.variable.bi, g.variable.mem, g.PC, g.SP, 0, stkbar, g.stkbarPos), nil
|
||||
}
|
||||
@ -95,13 +74,6 @@ func (g *G) Stacktrace(depth int) ([]Stackframe, error) {
|
||||
return it.stacktrace(depth)
|
||||
}
|
||||
|
||||
// GoroutineLocation returns the location of the given
|
||||
// goroutine.
|
||||
func (dbp *Process) GoroutineLocation(g *G) *Location {
|
||||
f, l, fn := dbp.bi.PCToLine(g.PC)
|
||||
return &Location{PC: g.PC, File: f, Line: l, Fn: fn}
|
||||
}
|
||||
|
||||
// NullAddrError is an error for a null address.
|
||||
type NullAddrError struct{}
|
||||
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"go/ast"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/debug/dwarf"
|
||||
@ -32,6 +31,23 @@ type Thread struct {
|
||||
os *OSSpecificDetails
|
||||
}
|
||||
|
||||
// IThread represents a thread.
|
||||
type IThread interface {
|
||||
memoryReadWriter
|
||||
Location() (*Location, error)
|
||||
// Breakpoint will return the breakpoint that this thread is stopped at or
|
||||
// nil if the thread is not stopped at any breakpoint.
|
||||
// Active will be true if the thread is stopped at a breakpoint and the
|
||||
// breakpoint's condition is met.
|
||||
// If there was an error evaluating the breakpoint's condition it will be
|
||||
// returned as condErr
|
||||
Breakpoint() (breakpoint *Breakpoint, active bool, condErr error)
|
||||
ThreadID() int
|
||||
Registers(floatingPoint bool) (Registers, error)
|
||||
Arch() Arch
|
||||
BinInfo() *BinaryInfo
|
||||
}
|
||||
|
||||
// Location represents the location of a thread.
|
||||
// Holds information on the current instruction
|
||||
// address, the source file:line, and the function.
|
||||
@ -116,6 +132,14 @@ func (thread *Thread) Location() (*Location, error) {
|
||||
return &Location{PC: pc, File: f, Line: l, Fn: fn}, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) Arch() Arch {
|
||||
return thread.dbp.bi.arch
|
||||
}
|
||||
|
||||
func (thread *Thread) BinInfo() *BinaryInfo {
|
||||
return &thread.dbp.bi
|
||||
}
|
||||
|
||||
// ThreadBlockedError is returned when the thread
|
||||
// is blocked in the scheduler.
|
||||
type ThreadBlockedError struct{}
|
||||
@ -133,7 +157,7 @@ func topframe(g *G, thread *Thread) (Stackframe, error) {
|
||||
if thread.blocked() {
|
||||
return Stackframe{}, ThreadBlockedError{}
|
||||
}
|
||||
frames, err = thread.Stacktrace(0)
|
||||
frames, err = ThreadStacktrace(thread, 0)
|
||||
} else {
|
||||
frames, err = g.Stacktrace(0)
|
||||
}
|
||||
@ -167,17 +191,17 @@ func (dbp *Process) next(stepInto bool) error {
|
||||
}()
|
||||
|
||||
csource := filepath.Ext(topframe.Current.File) != ".go"
|
||||
thread := dbp.currentThread
|
||||
var thread memoryReadWriter = dbp.currentThread
|
||||
var regs Registers
|
||||
if dbp.selectedGoroutine != nil && dbp.selectedGoroutine.thread != nil {
|
||||
thread = dbp.selectedGoroutine.thread
|
||||
regs, err = thread.Registers(false)
|
||||
regs, err = dbp.selectedGoroutine.thread.Registers(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
text, err := Disassemble(thread, regs, dbp.breakpoints, &dbp.bi, topframe.FDE.Begin(), topframe.FDE.End())
|
||||
text, err := disassemble(thread, regs, dbp.breakpoints, dbp.BinInfo(), topframe.FDE.Begin(), topframe.FDE.End())
|
||||
if err != nil && stepInto {
|
||||
return err
|
||||
}
|
||||
@ -337,33 +361,30 @@ func (thread *Thread) SetPC(pc uint64) error {
|
||||
return regs.SetPC(thread, pc)
|
||||
}
|
||||
|
||||
func (thread *Thread) getGVariable() (*Variable, error) {
|
||||
func getGVariable(thread IThread) (*Variable, error) {
|
||||
arch := thread.Arch()
|
||||
regs, err := thread.Registers(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if thread.dbp.bi.arch.GStructOffset() == 0 {
|
||||
if arch.GStructOffset() == 0 {
|
||||
// GetG was called through SwitchThread / updateThreadList during initialization
|
||||
// thread.dbp.arch isn't setup yet (it needs a current thread to read global variables from)
|
||||
return nil, fmt.Errorf("g struct offset not initialized")
|
||||
}
|
||||
|
||||
gaddrbs, err := thread.readMemory(uintptr(regs.TLS()+thread.dbp.bi.arch.GStructOffset()), thread.dbp.bi.arch.PtrSize())
|
||||
gaddrbs, err := thread.readMemory(uintptr(regs.TLS()+arch.GStructOffset()), arch.PtrSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gaddr := uintptr(binary.LittleEndian.Uint64(gaddrbs))
|
||||
|
||||
// On Windows, the value at TLS()+GStructOffset() is a
|
||||
// pointer to the G struct.
|
||||
needsDeref := runtime.GOOS == "windows"
|
||||
|
||||
return thread.newGVariable(gaddr, needsDeref)
|
||||
return newGVariable(thread, gaddr, arch.DerefTLS())
|
||||
}
|
||||
|
||||
func (thread *Thread) newGVariable(gaddr uintptr, deref bool) (*Variable, error) {
|
||||
typ, err := thread.dbp.bi.findType("runtime.g")
|
||||
func newGVariable(thread IThread, gaddr uintptr, deref bool) (*Variable, error) {
|
||||
typ, err := thread.BinInfo().findType("runtime.g")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -371,12 +392,12 @@ func (thread *Thread) newGVariable(gaddr uintptr, deref bool) (*Variable, error)
|
||||
name := ""
|
||||
|
||||
if deref {
|
||||
typ = &dwarf.PtrType{dwarf.CommonType{int64(thread.dbp.bi.arch.PtrSize()), "", reflect.Ptr, 0}, typ}
|
||||
typ = &dwarf.PtrType{dwarf.CommonType{int64(thread.Arch().PtrSize()), "", reflect.Ptr, 0}, typ}
|
||||
} else {
|
||||
name = "runtime.curg"
|
||||
}
|
||||
|
||||
return thread.newVariable(name, gaddr, typ), nil
|
||||
return newVariableFromThread(thread, name, gaddr, typ), nil
|
||||
}
|
||||
|
||||
// GetG returns information on the G (goroutine) that is executing on this thread.
|
||||
@ -393,8 +414,8 @@ func (thread *Thread) newGVariable(gaddr uintptr, deref bool) (*Variable, error)
|
||||
//
|
||||
// In order to get around all this craziness, we read the address of the G structure for
|
||||
// the current thread from the thread local storage area.
|
||||
func (thread *Thread) GetG() (g *G, err error) {
|
||||
gaddr, err := thread.getGVariable()
|
||||
func GetG(thread IThread) (g *G, err error) {
|
||||
gaddr, err := getGVariable(thread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -433,31 +454,31 @@ func (thread *Thread) Halt() (err error) {
|
||||
}
|
||||
|
||||
// ThreadScope returns an EvalScope for this thread.
|
||||
func (thread *Thread) ThreadScope() (*EvalScope, error) {
|
||||
locations, err := thread.Stacktrace(0)
|
||||
func ThreadScope(thread IThread) (*EvalScope, error) {
|
||||
locations, err := ThreadStacktrace(thread, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(locations) < 1 {
|
||||
return nil, errors.New("could not decode first frame")
|
||||
}
|
||||
return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, nil, thread.dbp.BinInfo()}, nil
|
||||
return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, nil, thread.BinInfo()}, nil
|
||||
}
|
||||
|
||||
// GoroutineScope returns an EvalScope for the goroutine running on this thread.
|
||||
func (thread *Thread) GoroutineScope() (*EvalScope, error) {
|
||||
locations, err := thread.Stacktrace(0)
|
||||
func GoroutineScope(thread IThread) (*EvalScope, error) {
|
||||
locations, err := ThreadStacktrace(thread, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(locations) < 1 {
|
||||
return nil, errors.New("could not decode first frame")
|
||||
}
|
||||
gvar, err := thread.getGVariable()
|
||||
gvar, err := getGVariable(thread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, gvar, thread.dbp.BinInfo()}, nil
|
||||
return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, gvar, thread.BinInfo()}, nil
|
||||
}
|
||||
|
||||
// SetCurrentBreakpoint sets the current breakpoint that this
|
||||
@ -475,7 +496,7 @@ func (thread *Thread) SetCurrentBreakpoint() error {
|
||||
}
|
||||
thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.checkCondition(thread)
|
||||
if thread.onTriggeredBreakpoint() {
|
||||
if g, err := thread.GetG(); err == nil {
|
||||
if g, err := GetG(thread); err == nil {
|
||||
thread.CurrentBreakpoint.HitCount[g.ID]++
|
||||
}
|
||||
thread.CurrentBreakpoint.TotalHitCount++
|
||||
@ -527,3 +548,11 @@ func (thread *Thread) onNextGoroutine() (bool, error) {
|
||||
}
|
||||
return bp.checkCondition(thread)
|
||||
}
|
||||
|
||||
func (th *Thread) Breakpoint() (*Breakpoint, bool, error) {
|
||||
return th.CurrentBreakpoint, (th.CurrentBreakpoint != nil && th.BreakpointConditionMet), th.BreakpointConditionError
|
||||
}
|
||||
|
||||
func (th *Thread) ThreadID() int {
|
||||
return th.ID
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ type G struct {
|
||||
CurrentLoc Location
|
||||
|
||||
// Thread that this goroutine is currently allocated to
|
||||
thread *Thread
|
||||
thread IThread
|
||||
|
||||
variable *Variable
|
||||
}
|
||||
@ -157,8 +157,8 @@ func (scope *EvalScope) newVariable(name string, addr uintptr, dwarfType dwarf.T
|
||||
return newVariable(name, addr, dwarfType, scope.bi, scope.Mem)
|
||||
}
|
||||
|
||||
func (t *Thread) newVariable(name string, addr uintptr, dwarfType dwarf.Type) *Variable {
|
||||
return newVariable(name, addr, dwarfType, t.dbp.BinInfo(), t)
|
||||
func newVariableFromThread(t IThread, name string, addr uintptr, dwarfType dwarf.Type) *Variable {
|
||||
return newVariable(name, addr, dwarfType, t.BinInfo(), t)
|
||||
}
|
||||
|
||||
func (v *Variable) newVariable(name string, addr uintptr, dwarfType dwarf.Type) *Variable {
|
||||
|
@ -13,7 +13,6 @@ type Interface interface {
|
||||
Info
|
||||
ProcessManipulation
|
||||
BreakpointManipulation
|
||||
VariableEval
|
||||
}
|
||||
|
||||
// Info is an interface that provides general information on the target.
|
||||
@ -26,10 +25,6 @@ type Info interface {
|
||||
ThreadInfo
|
||||
GoroutineInfo
|
||||
|
||||
// Disassemble disassembles target memory between startPC and endPC, marking
|
||||
// the current instruction being executed in goroutine g.
|
||||
Disassemble(g *proc.G, startPC, endPC uint64) ([]proc.AsmInstruction, error)
|
||||
|
||||
// FindFileLocation returns the address of the first instruction belonging
|
||||
// to line lineNumber in file fileName.
|
||||
FindFileLocation(fileName string, lineNumber int) (uint64, error)
|
||||
@ -58,15 +53,15 @@ type Info interface {
|
||||
// ThreadInfo is an interface for getting information on active threads
|
||||
// in the process.
|
||||
type ThreadInfo interface {
|
||||
Threads() map[int]*proc.Thread
|
||||
CurrentThread() *proc.Thread
|
||||
FindThread(threadID int) (proc.IThread, bool)
|
||||
ThreadList() []proc.IThread
|
||||
CurrentThread() proc.IThread
|
||||
}
|
||||
|
||||
// GoroutineInfo is an interface for getting information on running goroutines.
|
||||
type GoroutineInfo interface {
|
||||
GoroutinesInfo() ([]*proc.G, error)
|
||||
SelectedGoroutine() *proc.G
|
||||
FindGoroutine(int) (*proc.G, error)
|
||||
}
|
||||
|
||||
// ProcessManipulation is an interface for changing the execution state of a process.
|
||||
|
@ -47,7 +47,7 @@ func ConvertBreakpoint(bp *proc.Breakpoint) *Breakpoint {
|
||||
|
||||
// ConvertThread converts a proc.Thread into an
|
||||
// api thread.
|
||||
func ConvertThread(th *proc.Thread) *Thread {
|
||||
func ConvertThread(th proc.IThread) *Thread {
|
||||
var (
|
||||
function *Function
|
||||
file string
|
||||
@ -66,16 +66,16 @@ func ConvertThread(th *proc.Thread) *Thread {
|
||||
|
||||
var bp *Breakpoint
|
||||
|
||||
if th.CurrentBreakpoint != nil && th.BreakpointConditionMet {
|
||||
bp = ConvertBreakpoint(th.CurrentBreakpoint)
|
||||
if b, active, _ := th.Breakpoint(); active {
|
||||
bp = ConvertBreakpoint(b)
|
||||
}
|
||||
|
||||
if g, _ := th.GetG(); g != nil {
|
||||
if g, _ := proc.GetG(th); g != nil {
|
||||
gid = g.ID
|
||||
}
|
||||
|
||||
return &Thread{
|
||||
ID: th.ID,
|
||||
ID: th.ThreadID(),
|
||||
PC: pc,
|
||||
File: file,
|
||||
Line: line,
|
||||
@ -204,7 +204,7 @@ func ConvertGoroutine(g *proc.G) *Goroutine {
|
||||
th := g.Thread()
|
||||
tid := 0
|
||||
if th != nil {
|
||||
tid = th.ID
|
||||
tid = th.ThreadID()
|
||||
}
|
||||
return &Goroutine{
|
||||
ID: g.ID,
|
||||
|
@ -179,10 +179,10 @@ func (d *Debugger) state() (*api.DebuggerState, error) {
|
||||
Exited: d.target.Exited(),
|
||||
}
|
||||
|
||||
for i := range d.target.Threads() {
|
||||
th := api.ConvertThread(d.target.Threads()[i])
|
||||
for _, thread := range d.target.ThreadList() {
|
||||
th := api.ConvertThread(thread)
|
||||
state.Threads = append(state.Threads, th)
|
||||
if i == d.target.CurrentThread().ID {
|
||||
if thread.ThreadID() == d.target.CurrentThread().ThreadID() {
|
||||
state.CurrentThread = th
|
||||
}
|
||||
}
|
||||
@ -372,7 +372,7 @@ func (d *Debugger) Threads() ([]*api.Thread, error) {
|
||||
return nil, &proc.ProcessExitedError{}
|
||||
}
|
||||
threads := []*api.Thread{}
|
||||
for _, th := range d.target.Threads() {
|
||||
for _, th := range d.target.ThreadList() {
|
||||
threads = append(threads, api.ConvertThread(th))
|
||||
}
|
||||
return threads, nil
|
||||
@ -387,8 +387,8 @@ func (d *Debugger) FindThread(id int) (*api.Thread, error) {
|
||||
return nil, &proc.ProcessExitedError{}
|
||||
}
|
||||
|
||||
for _, th := range d.target.Threads() {
|
||||
if th.ID == id {
|
||||
for _, th := range d.target.ThreadList() {
|
||||
if th.ThreadID() == id {
|
||||
return api.ConvertThread(th), nil
|
||||
}
|
||||
}
|
||||
@ -472,7 +472,7 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
||||
state.Threads[i].BreakpointInfo = bpi
|
||||
|
||||
if bp.Goroutine {
|
||||
g, err := d.target.CurrentThread().GetG()
|
||||
g, err := proc.GetG(d.target.CurrentThread())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -480,7 +480,7 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
||||
}
|
||||
|
||||
if bp.Stacktrace > 0 {
|
||||
rawlocs, err := d.target.CurrentThread().Stacktrace(bp.Stacktrace)
|
||||
rawlocs, err := proc.ThreadStacktrace(d.target.CurrentThread(), bp.Stacktrace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -490,7 +490,11 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
||||
}
|
||||
}
|
||||
|
||||
s, err := d.target.Threads()[state.Threads[i].ID].GoroutineScope()
|
||||
thread, found := d.target.FindThread(state.Threads[i].ID)
|
||||
if !found {
|
||||
return fmt.Errorf("could not find thread %d", state.Threads[i].ID)
|
||||
}
|
||||
s, err := proc.GoroutineScope(thread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -598,11 +602,11 @@ func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadCo
|
||||
}
|
||||
|
||||
vars := []api.Variable{}
|
||||
thread, found := d.target.Threads()[threadID]
|
||||
thread, found := d.target.FindThread(threadID)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
||||
}
|
||||
scope, err := thread.ThreadScope()
|
||||
scope, err := proc.ThreadScope(thread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -623,7 +627,7 @@ func (d *Debugger) Registers(threadID int, floatingPoint bool) (api.Registers, e
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
thread, found := d.target.Threads()[threadID]
|
||||
thread, found := d.target.FindThread(threadID)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
||||
}
|
||||
@ -647,7 +651,7 @@ func (d *Debugger) LocalVariables(scope api.EvalScope, cfg proc.LoadConfig) ([]a
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
s, err := proc.ConvertEvalScope(d.target, scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -663,7 +667,7 @@ func (d *Debugger) FunctionArguments(scope api.EvalScope, cfg proc.LoadConfig) (
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
s, err := proc.ConvertEvalScope(d.target, scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -680,7 +684,7 @@ func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string, cfg p
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
s, err := proc.ConvertEvalScope(d.target, scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -697,7 +701,7 @@ func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol, value string)
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
s, err := proc.ConvertEvalScope(d.target, scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -729,13 +733,13 @@ func (d *Debugger) Stacktrace(goroutineID, depth int, cfg *proc.LoadConfig) ([]a
|
||||
|
||||
var rawlocs []proc.Stackframe
|
||||
|
||||
g, err := d.target.FindGoroutine(goroutineID)
|
||||
g, err := proc.FindGoroutine(d.target, goroutineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g == nil {
|
||||
rawlocs, err = d.target.CurrentThread().Stacktrace(depth)
|
||||
rawlocs, err = proc.ThreadStacktrace(d.target.CurrentThread(), depth)
|
||||
} else {
|
||||
rawlocs, err = g.Stacktrace(depth)
|
||||
}
|
||||
@ -752,7 +756,7 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo
|
||||
frame := api.Stackframe{Location: api.ConvertLocation(rawlocs[i].Call)}
|
||||
if cfg != nil && rawlocs[i].Current.Fn != nil {
|
||||
var err error
|
||||
scope := d.target.FrameToScope(rawlocs[i])
|
||||
scope := proc.FrameToScope(d.target, rawlocs[i])
|
||||
locals, err := scope.LocalVariables(*cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -781,7 +785,7 @@ func (d *Debugger) FindLocation(scope api.EvalScope, locStr string) ([]api.Locat
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s, _ := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
s, _ := proc.ConvertEvalScope(d.target, scope.GoroutineID, scope.Frame)
|
||||
|
||||
locs, err := loc.Find(d, s, locStr)
|
||||
for i := range locs {
|
||||
@ -808,12 +812,12 @@ func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavo
|
||||
endPC = fn.End
|
||||
}
|
||||
|
||||
g, err := d.target.FindGoroutine(scope.GoroutineID)
|
||||
g, err := proc.FindGoroutine(d.target, scope.GoroutineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
insts, err := d.target.Disassemble(g, startPC, endPC)
|
||||
insts, err := proc.Disassemble(d.target, g, startPC, endPC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ func assertVariable(t *testing.T, variable *proc.Variable, expected varTest) {
|
||||
}
|
||||
|
||||
func evalVariable(p *proc.Process, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
|
||||
scope, err := p.CurrentThread().GoroutineScope()
|
||||
scope, err := proc.GoroutineScope(p.CurrentThread())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -68,7 +68,7 @@ func (tc *varTest) alternateVarTest() varTest {
|
||||
}
|
||||
|
||||
func setVariable(p *proc.Process, symbol, value string) error {
|
||||
scope, err := p.CurrentThread().GoroutineScope()
|
||||
scope, err := proc.GoroutineScope(p.CurrentThread())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -348,7 +348,7 @@ func TestLocalVariables(t *testing.T) {
|
||||
assertNoError(err, t, "Continue() returned an error")
|
||||
|
||||
for _, tc := range testcases {
|
||||
scope, err := p.CurrentThread().GoroutineScope()
|
||||
scope, err := proc.GoroutineScope(p.CurrentThread())
|
||||
assertNoError(err, t, "AsScope()")
|
||||
vars, err := tc.fn(scope, pnormalLoadConfig)
|
||||
assertNoError(err, t, "LocalVariables() returned an error")
|
||||
|
Loading…
Reference in New Issue
Block a user