From 43756cd864315b1be3fabdb9f028b1d8da886fcb Mon Sep 17 00:00:00 2001 From: aarzilli Date: Fri, 18 Mar 2016 09:32:17 +0100 Subject: [PATCH] proc: bugfix: Truncate stacktrace when FDE of a frame can not be found Instead of returning an error when FDE of a frame can not be found, just truncate the stack trace. Fixes #462 --- dwarf/frame/entries.go | 10 +++++++++- proc/proc_test.go | 30 ++++++++++++++++++++++++++++++ proc/stack.go | 7 +++++++ proc/threads.go | 4 ++++ proc/variables.go | 8 +++++--- 5 files changed, 55 insertions(+), 4 deletions(-) diff --git a/dwarf/frame/entries.go b/dwarf/frame/entries.go index 6e52169b..caca2ce2 100644 --- a/dwarf/frame/entries.go +++ b/dwarf/frame/entries.go @@ -65,6 +65,14 @@ func NewFrameIndex() FrameDescriptionEntries { return make(FrameDescriptionEntries, 0, 1000) } +type NoFDEForPCError struct { + PC uint64 +} + +func (err *NoFDEForPCError) Error() string { + return fmt.Sprintf("could not find FDE for PC %#v", err.PC) +} + // Returns the Frame Description Entry for the given PC. func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry, error) { idx := sort.Search(len(fdes), func(i int) bool { @@ -77,7 +85,7 @@ func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry, return true }) if idx == len(fdes) { - return nil, fmt.Errorf("could not find FDE for PC %#v", pc) + return nil, &NoFDEForPCError{pc} } return fdes[idx], nil } diff --git a/proc/proc_test.go b/proc/proc_test.go index a3b046f7..f7e02b6e 100644 --- a/proc/proc_test.go +++ b/proc/proc_test.go @@ -1655,3 +1655,33 @@ func TestPanicBreakpoint(t *testing.T) { } }) } + +func TestIssue462(t *testing.T) { + // Stacktrace of Goroutine 0 fails with an error + if runtime.GOOS == "windows" { + return + } + withTestProcess("testnextnethttp", t, func(p *Process, fixture protest.Fixture) { + go func() { + for !p.Running() { + time.Sleep(50 * time.Millisecond) + } + + // Wait for program to start listening. + for { + conn, err := net.Dial("tcp", "localhost:9191") + if err == nil { + conn.Close() + break + } + time.Sleep(50 * time.Millisecond) + } + + p.RequestManualStop() + }() + + assertNoError(p.Continue(), t, "Continue()") + _, err := p.CurrentThread.Stacktrace(40) + assertNoError(err, t, "Stacktrace()") + }) +} diff --git a/proc/stack.go b/proc/stack.go index f0a0226a..6e3411f6 100644 --- a/proc/stack.go +++ b/proc/stack.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "errors" "fmt" + "github.com/derekparker/delve/dwarf/frame" ) // NoReturnAddr is returned when return address @@ -101,6 +102,12 @@ func (it *StackIterator) Next() bool { } it.frame, it.err = it.dbp.frameInfo(it.pc, it.sp, it.top) if it.err != nil { + if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top { + it.frame = Stackframe{ Current: Location{ PC: it.pc, File: "?", Line: -1 }, Call: Location{ PC: it.pc, File: "?", Line: -1 }, CFA: 0, Ret: 0 } + it.atend = true + it.err = nil + return true + } return false } diff --git a/proc/threads.go b/proc/threads.go index 47d872a3..0f640e5e 100644 --- a/proc/threads.go +++ b/proc/threads.go @@ -3,6 +3,7 @@ package proc import ( "debug/gosym" "encoding/binary" + "errors" "fmt" "path/filepath" "reflect" @@ -353,6 +354,9 @@ func (thread *Thread) Scope() (*EvalScope, error) { if err != nil { return nil, err } + if len(locations) < 1 { + return nil, errors.New("could not decode first frame") + } return locations[0].Scope(thread), nil } diff --git a/proc/variables.go b/proc/variables.go index a6fcb111..3c36faa4 100644 --- a/proc/variables.go +++ b/proc/variables.go @@ -422,9 +422,11 @@ func (g *G) UserCurrent() Location { it := newStackIterator(g.dbp, pc, sp) for it.Next() { frame := it.Frame() - name := frame.Call.Fn.Name - if (strings.Index(name, ".") >= 0) && (!strings.HasPrefix(name, "runtime.") || isExportedRuntime(name)) { - return frame.Call + if frame.Call.Fn != nil { + name := frame.Call.Fn.Name + if (strings.Index(name, ".") >= 0) && (!strings.HasPrefix(name, "runtime.") || isExportedRuntime(name)) { + return frame.Call + } } } return g.CurrentLoc