internal/gocore: don't panic if backtrace fails

Backtracing a thread in the core file may fail for various reasons: it
might have C functions, which we don't know how to deal with, or its
stack could be corrupted. Don't crash if that happens, just give up on
backtracing the current thread.

Fixes golang/go#27323

Change-Id: I271032cef1a9a54ef929b602a9846addfd145296
Reviewed-on: https://go-review.googlesource.com/132055
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rhys Hiltner <rhys@justin.tv>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/internal/gocore/module.go b/internal/gocore/module.go
index fe86162..85c3826 100644
--- a/internal/gocore/module.go
+++ b/internal/gocore/module.go
@@ -167,14 +167,14 @@
 	t.entries = []pcTabEntry{{bytes: 1<<63 - 1, val: -1}}
 }
 
-func (t *pcTab) find(off int64) int64 {
+func (t *pcTab) find(off int64) (int64, error) {
 	for _, e := range t.entries {
 		if off < e.bytes {
-			return e.val
+			return e.val, nil
 		}
 		off -= e.bytes
 	}
-	panic("can't find pctab entry")
+	return 0, fmt.Errorf("can't find pctab entry for offset %#x", off)
 }
 
 // readVarint reads a varint from the core file.
diff --git a/internal/gocore/process.go b/internal/gocore/process.go
index d4e368a..05c7c84 100644
--- a/internal/gocore/process.go
+++ b/internal/gocore/process.go
@@ -516,7 +516,11 @@
 		return g
 	}
 	for {
-		f := p.readFrame(sp, pc)
+		f, err := p.readFrame(sp, pc)
+		if err != nil {
+			fmt.Printf("warning: giving up on backtrace: %v\n", err)
+			break
+		}
 		if f.f.name == "runtime.goexit" {
 			break
 		}
@@ -553,13 +557,16 @@
 	return g
 }
 
-func (p *Process) readFrame(sp, pc core.Address) *Frame {
+func (p *Process) readFrame(sp, pc core.Address) (*Frame, error) {
 	f := p.funcTab.find(pc)
 	if f == nil {
-		panic(fmt.Errorf("  pc not found %x\n", pc))
+		return nil, fmt.Errorf("cannot find func for pc=%#x", pc)
 	}
 	off := pc.Sub(f.entry)
-	size := f.frameSize.find(off)
+	size, err := f.frameSize.find(off)
+	if err != nil {
+		return nil, fmt.Errorf("cannot read frame size at pc=%#x: %v", pc, err)
+	}
 	size += p.proc.PtrSize() // TODO: on amd64, the pushed return address
 
 	frame := &Frame{f: f, pc: pc, min: sp, max: sp.Add(size)}
@@ -570,7 +577,10 @@
 		locals := region{p: p, a: f.funcdata[x], typ: p.findType("runtime.stackmap")}
 		n := locals.Field("n").Int32()       // # of bitmaps
 		nbit := locals.Field("nbit").Int32() // # of bits per bitmap
-		idx := f.stackMap.find(off)
+		idx, err := f.stackMap.find(off)
+		if err != nil {
+			return nil, fmt.Errorf("cannot read stack map at pc=%#x: %v", pc, err)
+		}
 		if idx < 0 {
 			idx = 0
 		}
@@ -590,7 +600,10 @@
 		args := region{p: p, a: f.funcdata[x], typ: p.findType("runtime.stackmap")}
 		n := args.Field("n").Int32()       // # of bitmaps
 		nbit := args.Field("nbit").Int32() // # of bits per bitmap
-		idx := f.stackMap.find(off)
+		idx, err := f.stackMap.find(off)
+		if err != nil {
+			return nil, fmt.Errorf("cannot read stack map at pc=%#x: %v", pc, err)
+		}
 		if idx < 0 {
 			idx = 0
 		}
@@ -607,7 +620,7 @@
 	}
 	frame.Live = live
 
-	return frame
+	return frame, nil
 }
 
 // A Stats struct is the node of a tree representing the entire memory