Improve handling of Go runtime scheduler

This commit is contained in:
Derek Parker 2014-11-26 20:35:53 -06:00
parent 13a3112b6b
commit 16392ce609
3 changed files with 222 additions and 16 deletions

@ -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
}
}