cmd/cgo, runtime, runtime/cgo: use cgo context function
Add support for the context function set by runtime.SetCgoTraceback.
The context function was added in CL 17761, without support.
This CL is the support.
This CL has not been tested for real C code, as a working context
function for C code requires unwind support that does not seem to exist.
I wanted to get the CL out before the freeze.
I apologize for the length of this CL. It's mostly plumbing, but
unfortunately the plumbing is processor-specific.
Change-Id: I8ce11a0de9b3dafcc29efd2649d776e93bff0e90
Reviewed-on: https://go-review.googlesource.com/22508
Reviewed-by: Austin Clements <austin@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 529aa1e..7771426 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -172,6 +172,7 @@
frame.lr = lr0
}
waspanic := false
+ cgoCtxt := gp.cgoCtxt
printing := pcbuf == nil && callback == nil
_defer := gp._defer
@@ -252,6 +253,7 @@
sp = gp.m.curg.sched.sp
stkbarG = gp.m.curg
stkbar = stkbarG.stkbar[stkbarG.stkbarPos:]
+ cgoCtxt = gp.m.curg.cgoCtxt
}
frame.fp = sp + uintptr(funcspdelta(f, frame.pc, &cache))
if !usesLR {
@@ -413,6 +415,18 @@
n++
skipped:
+ if f.entry == cgocallback_gofuncPC && len(cgoCtxt) > 0 {
+ ctxt := cgoCtxt[len(cgoCtxt)-1]
+ cgoCtxt = cgoCtxt[:len(cgoCtxt)-1]
+
+ // skip only applies to Go frames.
+ // callback != nil only used when we only care
+ // about Go frames.
+ if skip == 0 && callback == nil {
+ n = tracebackCgoContext(pcbuf, printing, ctxt, n, max)
+ }
+ }
+
waspanic = f.entry == sigpanicPC
// Do not unwind past the bottom of the stack.
@@ -546,6 +560,39 @@
return
}
+// tracebackCgoContext handles tracing back a cgo context value, from
+// the context argument to setCgoTraceback, for the gentraceback
+// function. It returns the new value of n.
+func tracebackCgoContext(pcbuf *uintptr, printing bool, ctxt uintptr, n, max int) int {
+ var cgoPCs [32]uintptr
+ cgoContextPCs(ctxt, cgoPCs[:])
+ var arg cgoSymbolizerArg
+ anySymbolized := false
+ for _, pc := range cgoPCs {
+ if pc == 0 || n >= max {
+ break
+ }
+ if pcbuf != nil {
+ (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = pc
+ }
+ if printing {
+ if cgoSymbolizer == nil {
+ print("non-Go function at pc=", hex(pc), "\n")
+ } else {
+ c := printOneCgoTraceback(pc, max-n, &arg)
+ n += c - 1 // +1 a few lines down
+ anySymbolized = true
+ }
+ }
+ n++
+ }
+ if anySymbolized {
+ arg.pc = 0
+ callCgoSymbolizer(&arg)
+ }
+ return n
+}
+
func printcreatedby(gp *g) {
// Show what created goroutine, except main goroutine (goid 1).
pc := gp.gopc
@@ -782,10 +829,11 @@
// };
//
// If the Context field is 0, the context function is being called to
-// record the current traceback context. It should record whatever
-// information is needed about the current point of execution to later
-// produce a stack trace, probably the stack pointer and PC. In this
-// case the context function will be called from C code.
+// record the current traceback context. It should record in the
+// Context field whatever information is needed about the current
+// point of execution to later produce a stack trace, probably the
+// stack pointer and PC. In this case the context function will be
+// called from C code.
//
// If the Context field is not 0, then it is a value returned by a
// previous call to the context function. This case is called when the
@@ -903,16 +951,18 @@
if version != 0 {
panic("unsupported version")
}
- if context != nil {
- panic("SetCgoTraceback: context function not yet implemented")
- }
+
cgoTraceback = traceback
- cgoContext = context
cgoSymbolizer = symbolizer
+
+ // The context function is called when a C function calls a Go
+ // function. As such it is only called by C code in runtime/cgo.
+ if _cgo_set_context_function != nil {
+ cgocall(_cgo_set_context_function, context)
+ }
}
var cgoTraceback unsafe.Pointer
-var cgoContext unsafe.Pointer
var cgoSymbolizer unsafe.Pointer
// cgoTracebackArg is the type passed to cgoTraceback.
@@ -922,7 +972,7 @@
max uintptr
}
-// cgoContextArg is the type passed to cgoContext.
+// cgoContextArg is the type passed to the context function.
type cgoContextArg struct {
context uintptr
}
@@ -950,39 +1000,75 @@
return
}
- call := cgocall
- if panicking > 0 {
- // We do not want to call into the scheduler when panicking.
- call = asmcgocall
- }
-
var arg cgoSymbolizerArg
for _, c := range callers {
if c == 0 {
break
}
- arg.pc = c
- for {
- call(cgoSymbolizer, noescape(unsafe.Pointer(&arg)))
- if arg.funcName != nil {
- // Note that we don't print any argument
- // information here, not even parentheses.
- // The symbolizer must add that if
- // appropriate.
- println(gostringnocopy(arg.funcName))
- } else {
- println("non-Go function")
- }
- print("\t")
- if arg.file != nil {
- print(gostringnocopy(arg.file), ":", arg.lineno, " ")
- }
- print("pc=", hex(c), "\n")
- if arg.more == 0 {
- break
- }
- }
+ printOneCgoTraceback(c, 0x7fffffff, &arg)
}
arg.pc = 0
- call(cgoSymbolizer, noescape(unsafe.Pointer(&arg)))
+ callCgoSymbolizer(&arg)
+}
+
+// printOneCgoTraceback prints the traceback of a single cgo caller.
+// This can print more than one line because of inlining.
+// Returns the number of frames printed.
+func printOneCgoTraceback(pc uintptr, max int, arg *cgoSymbolizerArg) int {
+ c := 0
+ arg.pc = pc
+ for {
+ if c > max {
+ break
+ }
+ callCgoSymbolizer(arg)
+ if arg.funcName != nil {
+ // Note that we don't print any argument
+ // information here, not even parentheses.
+ // The symbolizer must add that if appropriate.
+ println(gostringnocopy(arg.funcName))
+ } else {
+ println("non-Go function")
+ }
+ print("\t")
+ if arg.file != nil {
+ print(gostringnocopy(arg.file), ":", arg.lineno, " ")
+ }
+ print("pc=", hex(c), "\n")
+ c++
+ if arg.more == 0 {
+ break
+ }
+ }
+ return c
+}
+
+// callCgoSymbolizer calls the cgoSymbolizer function.
+func callCgoSymbolizer(arg *cgoSymbolizerArg) {
+ call := cgocall
+ if panicking > 0 || getg().m.curg != getg() {
+ // We do not want to call into the scheduler when panicking
+ // or when on the system stack.
+ call = asmcgocall
+ }
+ call(cgoSymbolizer, noescape(unsafe.Pointer(arg)))
+}
+
+// cgoContextPCs gets the PC values from a cgo traceback.
+func cgoContextPCs(ctxt uintptr, buf []uintptr) {
+ if cgoTraceback == nil {
+ return
+ }
+ call := cgocall
+ if panicking > 0 || getg().m.curg != getg() {
+ // We do not want to call into the scheduler when panicking
+ // or when on the system stack.
+ call = asmcgocall
+ }
+ arg := cgoTracebackArg{
+ context: ctxt,
+ buf: (*uintptr)(noescape(unsafe.Pointer(&buf[0]))),
+ max: uintptr(len(buf)),
+ }
+ call(cgoTraceback, noescape(unsafe.Pointer(&arg)))
}