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

proc: introduce IThread interface to abstract threads

上级 97cd3a0a
......@@ -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) 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
}
......@@ -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")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册