From 39945498a8201446695cc48ee9e22d19262f76d3 Mon Sep 17 00:00:00 2001 From: Derek Parker Date: Sat, 9 May 2015 12:44:38 -0500 Subject: [PATCH] Improve 'next': return into deferred func --- _fixtures/testnextdefer.go | 10 ++++++++++ dwarf/reader/reader.go | 3 +++ proctl/proctl.go | 7 +++++++ proctl/proctl_test.go | 18 +++++++++++++----- proctl/variables.go | 37 ++++++++++++++++++++++++++++++++++++- 5 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 _fixtures/testnextdefer.go diff --git a/_fixtures/testnextdefer.go b/_fixtures/testnextdefer.go new file mode 100644 index 00000000..0ec74707 --- /dev/null +++ b/_fixtures/testnextdefer.go @@ -0,0 +1,10 @@ +package main + +import "fmt" + +func main() { + defer func() { + fmt.Println("hi") + }() + fmt.Println("bye") +} diff --git a/dwarf/reader/reader.go b/dwarf/reader/reader.go index ee139157..0d9010bf 100755 --- a/dwarf/reader/reader.go +++ b/dwarf/reader/reader.go @@ -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 diff --git a/proctl/proctl.go b/proctl/proctl.go index 6e12e475..d3b6660e 100644 --- a/proctl/proctl.go +++ b/proctl/proctl.go @@ -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 { diff --git a/proctl/proctl_test.go b/proctl/proctl_test.go index 70688767..f7423841 100644 --- a/proctl/proctl_test.go +++ b/proctl/proctl_test.go @@ -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) { diff --git a/proctl/variables.go b/proctl/variables.go index 1de15aa4..a0892637 100644 --- a/proctl/variables.go +++ b/proctl/variables.go @@ -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 }