Improve handling of Go runtime scheduler
This commit is contained in:
parent
13a3112b6b
commit
16392ce609
@ -249,13 +249,29 @@ func (dbp *DebuggedProcess) LoadInformation() error {
|
||||
|
||||
// Steps through process.
|
||||
func (dbp *DebuggedProcess) Step() (err error) {
|
||||
for _, thread := range dbp.Threads {
|
||||
err := thread.Step()
|
||||
if err != nil {
|
||||
if _, ok := err.(ProcessExitedError); !ok {
|
||||
var (
|
||||
th *ThreadContext
|
||||
ok bool
|
||||
)
|
||||
|
||||
allm, err := dbp.CurrentThread.AllM()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, m := range allm {
|
||||
th, ok = dbp.Threads[m.procid]
|
||||
if !ok {
|
||||
th = dbp.Threads[dbp.Pid]
|
||||
}
|
||||
|
||||
if m.blocked == 0 {
|
||||
err := th.Step()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -263,18 +279,39 @@ func (dbp *DebuggedProcess) Step() (err error) {
|
||||
|
||||
// Step over function calls.
|
||||
func (dbp *DebuggedProcess) Next() error {
|
||||
for _, thread := range dbp.Threads {
|
||||
err := thread.Next()
|
||||
if err != nil {
|
||||
// TODO(dp): There are some coordination issues
|
||||
// here that need to be resolved.
|
||||
if _, ok := err.(TimeoutError); !ok && err != syscall.ESRCH {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var (
|
||||
th *ThreadContext
|
||||
ok bool
|
||||
)
|
||||
|
||||
allm, err := dbp.CurrentThread.AllM()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
for _, m := range allm {
|
||||
th, ok = dbp.Threads[m.procid]
|
||||
if !ok {
|
||||
th = dbp.Threads[dbp.Pid]
|
||||
}
|
||||
|
||||
if m.blocked == 1 {
|
||||
// Continue any blocked M so that the
|
||||
// scheduler can continue to do its'
|
||||
// job correctly.
|
||||
err := th.Continue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err := th.Next()
|
||||
if err != nil && err != syscall.ESRCH {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return stopTheWorld(dbp)
|
||||
}
|
||||
|
||||
// Resume process.
|
||||
|
@ -44,10 +44,17 @@ func currentLineNumber(p *proctl.DebuggedProcess, t *testing.T) (string, int) {
|
||||
|
||||
func TestStep(t *testing.T) {
|
||||
helper.WithTestProcess("../_fixtures/testprog", t, func(p *proctl.DebuggedProcess) {
|
||||
helloworldfunc := p.GoSymTable.LookupFunc("main.helloworld")
|
||||
helloworldaddr := helloworldfunc.Entry
|
||||
|
||||
_, err := p.Break(uintptr(helloworldaddr))
|
||||
assertNoError(err, t, "Break()")
|
||||
assertNoError(p.Continue(), t, "Continue()")
|
||||
|
||||
regs := helper.GetRegisters(p, t)
|
||||
rip := regs.PC()
|
||||
|
||||
err := p.Step()
|
||||
err = p.Step()
|
||||
assertNoError(err, t, "Step()")
|
||||
|
||||
regs = helper.GetRegisters(p, t)
|
||||
|
@ -19,6 +19,168 @@ type Variable struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
type M struct {
|
||||
procid int
|
||||
spinning uint8
|
||||
blocked uint8
|
||||
curg uintptr
|
||||
}
|
||||
|
||||
// Parses and returns select info on the internal M
|
||||
// data structures used by the Go scheduler.
|
||||
func (thread *ThreadContext) AllM() ([]*M, error) {
|
||||
data, err := thread.Process.Executable.DWARF()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader := data.Reader()
|
||||
|
||||
allmaddr, err := parseAllMPtr(thread.Process, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mptr, err := thread.readMemory(uintptr(allmaddr), 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := binary.LittleEndian.Uint64(mptr)
|
||||
if m == 0 {
|
||||
return nil, fmt.Errorf("allm contains no M pointers")
|
||||
}
|
||||
|
||||
// parse addresses
|
||||
procidInstructions, err := instructionsForMember("procid", thread.Process, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spinningInstructions, err := instructionsForMember("spinning", thread.Process, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
alllinkInstructions, err := instructionsForMember("alllink", thread.Process, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockedInstructions, err := instructionsForMember("blocked", thread.Process, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
curgInstructions, err := instructionsForMember("curg", thread.Process, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var allm []*M
|
||||
for {
|
||||
// curg
|
||||
curgAddr, err := executeMemberStackProgram(mptr, curgInstructions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
curgBytes, err := thread.readMemory(uintptr(curgAddr), 8)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read curg %#v %s", curgAddr, err)
|
||||
}
|
||||
curg := binary.LittleEndian.Uint64(curgBytes)
|
||||
|
||||
// procid
|
||||
procidAddr, err := executeMemberStackProgram(mptr, procidInstructions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
procidBytes, err := thread.readMemory(uintptr(procidAddr), 8)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read procid %#v %s", procidAddr, err)
|
||||
}
|
||||
procid := binary.LittleEndian.Uint64(procidBytes)
|
||||
|
||||
// spinning
|
||||
spinningAddr, err := executeMemberStackProgram(mptr, spinningInstructions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
spinBytes, err := thread.readMemory(uintptr(spinningAddr), 1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read spinning %#v %d", spinningAddr, err)
|
||||
}
|
||||
|
||||
// blocked
|
||||
blockedAddr, err := executeMemberStackProgram(mptr, blockedInstructions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockBytes, err := thread.readMemory(uintptr(blockedAddr), 1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read blocked %#v %s", blockedAddr, err)
|
||||
}
|
||||
|
||||
allm = append(allm, &M{
|
||||
procid: int(procid),
|
||||
blocked: blockBytes[0],
|
||||
spinning: spinBytes[0],
|
||||
curg: uintptr(curg),
|
||||
})
|
||||
|
||||
// Follow the linked list
|
||||
alllinkAddr, err := executeMemberStackProgram(mptr, alllinkInstructions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mptr, err = thread.readMemory(uintptr(alllinkAddr), 8)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read alllink %#v %s", alllinkAddr, err)
|
||||
}
|
||||
m = binary.LittleEndian.Uint64(mptr)
|
||||
|
||||
if m == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return allm, nil
|
||||
}
|
||||
|
||||
func instructionsForMember(member string, dbp *DebuggedProcess, reader *dwarf.Reader) ([]byte, error) {
|
||||
reader.Seek(0)
|
||||
entry, err := findDwarfEntry(member, reader, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
return instructions, nil
|
||||
}
|
||||
|
||||
func executeMemberStackProgram(base, instructions []byte) (uint64, error) {
|
||||
parentInstructions := append([]byte{op.DW_OP_addr}, base...)
|
||||
addr, err := op.ExecuteStackProgram(0, append(parentInstructions, instructions...))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint64(addr), nil
|
||||
}
|
||||
|
||||
func parseAllMPtr(dbp *DebuggedProcess, reader *dwarf.Reader) (uint64, error) {
|
||||
entry, err := findDwarfEntry("runtime.allm", reader, false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
addr, err := op.ExecuteStackProgram(0, instructions)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uint64(addr), nil
|
||||
}
|
||||
|
||||
func (dbp *DebuggedProcess) PrintGoroutinesInfo() error {
|
||||
data, err := dbp.Executable.DWARF()
|
||||
if err != nil {
|
||||
@ -225,7 +387,7 @@ func findDwarfEntry(name string, reader *dwarf.Reader, member bool) (*dwarf.Entr
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if entry.Tag != dwarf.TagVariable && entry.Tag != dwarf.TagFormalParameter {
|
||||
if entry.Tag != dwarf.TagVariable && entry.Tag != dwarf.TagFormalParameter && entry.Tag != dwarf.TagStructType {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user