}
}
+func TestCgoCrashTracebackGo(t *testing.T) {
+ t.Parallel()
+ switch platform := runtime.GOOS + "/" + runtime.GOARCH; platform {
+ case "darwin/amd64":
+ case "linux/amd64":
+ case "linux/ppc64le":
+ default:
+ t.Skipf("not yet supported on %s", platform)
+ }
+ got := runTestProg(t, "testprogcgo", "CrashTracebackGo")
+ for i := 1; i <= 3; i++ {
+ want := fmt.Sprintf("main.h%d", i)
+ if !strings.Contains(got, want) {
+ t.Errorf("missing %s", want)
+ }
+ }
+}
+
func TestCgoTracebackContext(t *testing.T) {
t.Parallel()
got := runTestProg(t, "testprogcgo", "TracebackContext")
"sync/atomic"
"testing"
"time"
+ _ "unsafe" // for go:linkname
)
// TestStackMem measures per-thread stack segment cache behavior.
// Pass a value to escapeMe to force it to escape.
var escapeMe = func(x interface{}) {}
+
+// Test that when F -> G is inlined and F is excluded from stack
+// traces, G still appears.
+func TestTracebackInlineExcluded(t *testing.T) {
+ defer func() {
+ recover()
+ buf := make([]byte, 4<<10)
+ stk := string(buf[:Stack(buf, false)])
+
+ t.Log(stk)
+
+ if not := "tracebackExcluded"; strings.Contains(stk, not) {
+ t.Errorf("found but did not expect %q", not)
+ }
+ if want := "tracebackNotExcluded"; !strings.Contains(stk, want) {
+ t.Errorf("expected %q in stack", want)
+ }
+ }()
+ tracebackExcluded()
+}
+
+// tracebackExcluded should be excluded from tracebacks. There are
+// various ways this could come up. Linking it to a "runtime." name is
+// rather synthetic, but it's easy and reliable. See issue #42754 for
+// one way this happened in real code.
+//
+//go:linkname tracebackExcluded runtime.tracebackExcluded
+//go:noinline
+func tracebackExcluded() {
+ // Call an inlined function that should not itself be excluded
+ // from tracebacks.
+ tracebackNotExcluded()
+}
+
+// tracebackNotExcluded should be inlined into tracebackExcluded, but
+// should not itself be excluded from the traceback.
+func tracebackNotExcluded() {
+ var x *int
+ *x = 0
+}
#cgo CFLAGS: -g -O0
// Defined in traceback_c.c.
+extern int crashInGo;
int tracebackF1(void);
void cgoTraceback(void* parg);
void cgoSymbolizer(void* parg);
func init() {
register("CrashTraceback", CrashTraceback)
+ register("CrashTracebackGo", CrashTracebackGo)
}
func CrashTraceback() {
runtime.SetCgoTraceback(0, unsafe.Pointer(C.cgoTraceback), nil, unsafe.Pointer(C.cgoSymbolizer))
C.tracebackF1()
}
+
+func CrashTracebackGo() {
+ C.crashInGo = 1
+ CrashTraceback()
+}
+
+//export h1
+func h1() {
+ h2()
+}
+
+func h2() {
+ h3()
+}
+
+func h3() {
+ var x *int
+ *x = 0
+}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// The C definitions for traceback.go.
+// The C definitions for traceback.go. That file uses //export so
+// it can't put function definitions in the "C" import comment.
#include <stdint.h>
char *p;
+int crashInGo;
+extern void h1(void);
+
int tracebackF3(void) {
- *p = 0;
+ if (crashInGo)
+ h1();
+ else
+ *p = 0;
return 0;
}
// If there is inlining info, print the inner frames.
if inldata := funcdata(f, _FUNCDATA_InlTree); inldata != nil {
inltree := (*[1 << 20]inlinedCall)(inldata)
+ var inlFunc _func
+ inlFuncInfo := funcInfo{&inlFunc, f.datap}
for {
ix := pcdatavalue(f, _PCDATA_InlTreeIndex, tracepc, nil)
if ix < 0 {
break
}
- if (flags&_TraceRuntimeFrames) != 0 || showframe(f, gp, nprint == 0, inltree[ix].funcID, lastFuncID) {
- name := funcnameFromNameoff(f, inltree[ix].func_)
+
+ // Create a fake _func for the
+ // inlined function.
+ inlFunc.nameoff = inltree[ix].func_
+ inlFunc.funcID = inltree[ix].funcID
+
+ if (flags&_TraceRuntimeFrames) != 0 || showframe(inlFuncInfo, gp, nprint == 0, inlFuncInfo.funcID, lastFuncID) {
+ name := funcname(inlFuncInfo)
file, line := funcline(f, tracepc)
print(name, "(...)\n")
print("\t", file, ":", line, "\n")
// showfuncinfo reports whether a function with the given characteristics should
// be printed during a traceback.
func showfuncinfo(f funcInfo, firstFrame bool, funcID, childID funcID) bool {
+ // Note that f may be a synthesized funcInfo for an inlined
+ // function, in which case only nameoff and funcID are set.
+
level, _, _ := gotraceback()
if level > 1 {
// Show all frames.