提交 510b7db2 编写于 作者: A aarzilli 提交者: Derek Parker

proc: introduce IThread interface to abstract threads

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