Improve 'next': return into deferred func

This commit is contained in:
Derek Parker 2015-05-09 12:44:38 -05:00
parent 2cfbc2937f
commit 39945498a8
5 changed files with 69 additions and 6 deletions

@ -0,0 +1,10 @@
package main
import "fmt"
func main() {
defer func() {
fmt.Println("hi")
}()
fmt.Println("bye")
}

@ -87,6 +87,9 @@ func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (
if err != nil {
return 0, err
}
if entry == nil {
return 0, fmt.Errorf("nil entry for member named %s", member)
}
name, ok := entry.Val(dwarf.AttrName).(string)
if !ok || name != member {
continue

@ -249,6 +249,13 @@ func (dbp *DebuggedProcess) next() error {
return err
}
if curg.DeferPC != 0 {
_, err = dbp.TempBreak(curg.DeferPC)
if err != nil {
return err
}
}
var goroutineExiting bool
var waitCount int
for _, th := range dbp.Threads {

@ -226,8 +226,8 @@ type nextTest struct {
begin, end int
}
func testnext(testcases []nextTest, initialLocation string, t *testing.T) {
withTestProcess("testnextprog", t, func(p *DebuggedProcess, fixture protest.Fixture) {
func testnext(program string, testcases []nextTest, initialLocation string, t *testing.T) {
withTestProcess(program, t, func(p *DebuggedProcess, fixture protest.Fixture) {
bp, err := p.BreakByLocation(initialLocation)
assertNoError(err, t, "Break()")
assertNoError(p.Continue(), t, "Continue()")
@ -277,7 +277,7 @@ func TestNextGeneral(t *testing.T) {
{26, 27},
{27, 34},
}
testnext(testcases, "main.testnext", t)
testnext("testnextprog", testcases, "main.testnext", t)
}
func TestNextGoroutine(t *testing.T) {
@ -285,7 +285,7 @@ func TestNextGoroutine(t *testing.T) {
{46, 47},
{47, 42},
}
testnext(testcases, "main.testgoroutine", t)
testnext("testnextprog", testcases, "main.testgoroutine", t)
}
func TestNextFunctionReturn(t *testing.T) {
@ -293,7 +293,15 @@ func TestNextFunctionReturn(t *testing.T) {
{13, 14},
{14, 35},
}
testnext(testcases, "main.helloworld", t)
testnext("testnextprog", testcases, "main.helloworld", t)
}
func TestNextFunctionReturnDefer(t *testing.T) {
testcases := []nextTest{
{5, 9},
{9, 6},
}
testnext("testnextdefer", testcases, "main.main", t)
}
func TestRuntimeBreakpoint(t *testing.T) {

@ -50,6 +50,9 @@ type G struct {
File string
Line int
Func *gosym.Func
// PC of entry to top-most deferred function.
DeferPC uint64
}
// Returns whether the goroutine is blocked on
@ -89,7 +92,7 @@ func parseG(thread *ThreadContext, addr uint64) (*G, error) {
rdr := thread.Process.DwarfReader()
rdr.Seek(0)
_, err = rdr.SeekToTypeNamed("runtime.g")
entry, err := rdr.SeekToTypeNamed("runtime.g")
if err != nil {
return nil, err
}
@ -97,6 +100,37 @@ func parseG(thread *ThreadContext, addr uint64) (*G, error) {
// Let's parse all of the members we care about in order so that
// we don't have to spend any extra time seeking.
// Parse defer
deferAddr, err := rdr.AddrForMember("_defer", initialInstructions)
if err != nil {
return nil, err
}
var deferPC uint64
// Dereference *defer pointer
deferAddrBytes, err := thread.readMemory(uintptr(deferAddr), thread.Process.arch.PtrSize())
if err != nil {
return nil, fmt.Errorf("error derefing *G %s", err)
}
if binary.LittleEndian.Uint64(deferAddrBytes) != 0 {
initialDeferInstructions := append([]byte{op.DW_OP_addr}, deferAddrBytes...)
_, err = rdr.SeekToTypeNamed("runtime._defer")
if err != nil {
return nil, err
}
deferPCAddr, err := rdr.AddrForMember("fn", initialDeferInstructions)
deferPC, err = thread.readUintRaw(uintptr(deferPCAddr), 8)
if err != nil {
return nil, err
}
deferPC, err = thread.readUintRaw(uintptr(deferPC), 8)
if err != nil {
return nil, err
}
err = rdr.SeekToEntry(entry)
if err != nil {
return nil, err
}
}
// Parse sched
schedAddr, err := rdr.AddrForMember("sched", initialInstructions)
if err != nil {
@ -149,6 +183,7 @@ func parseG(thread *ThreadContext, addr uint64) (*G, error) {
Line: l,
Func: fn,
WaitReason: waitreason,
DeferPC: deferPC,
}
return g, nil
}