]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: move cgo traceback into unwinder
authorAustin Clements <austin@google.com>
Tue, 14 Feb 2023 16:50:30 +0000 (11:50 -0500)
committerAustin Clements <austin@google.com>
Fri, 10 Mar 2023 17:59:36 +0000 (17:59 +0000)
Currently, gentraceback's loop ends with a call to tracebackCgoContext
to process cgo frames. This requires spreading various parts of the
printing and pcbuf logic across these two functions.

Clean this up by moving cgo unwinding into unwinder and then lifting
the printing and pcbuf logic from tracebackCgoContext into
gentraceback along with the other printing and pcbuf logic.

Updates #54466.

Change-Id: Ic71afaa5ae110c0ea5be9409e267e4284e36a8c9
Reviewed-on: https://go-review.googlesource.com/c/go/+/468299
Reviewed-by: Michael Pratt <mpratt@google.com>
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/runtime/traceback.go

index 30f874bc7328de1a07938927ee647e36be8c7d38..968823316e4d8a4b240699eaf993275351edc4a5 100644 (file)
@@ -569,6 +569,28 @@ func (u *unwinder) symPC() uintptr {
        return u.frame.pc
 }
 
+// cgoCallers populates pcBuf with the cgo callers of the current frame using
+// the registered cgo unwinder. It returns the number of PCs written to pcBuf.
+// If the current frame is not a cgo frame or if there's no registered cgo
+// unwinder, it returns 0.
+func (u *unwinder) cgoCallers(pcBuf []uintptr) int {
+       if cgoTraceback == nil || u.frame.fn.funcID != funcID_cgocallback || u.cgoCtxt < 0 {
+               // We don't have a cgo unwinder (typical case), or we do but we're not
+               // in a cgo frame or we're out of cgo context.
+               return 0
+       }
+
+       ctxt := u.g.ptr().cgoCtxt[u.cgoCtxt]
+       u.cgoCtxt--
+       cgoContextPCs(ctxt, pcBuf)
+       for i, pc := range pcBuf {
+               if pc == 0 {
+                       return i
+               }
+       }
+       return len(pcBuf)
+}
+
 // Generic traceback. Handles runtime stack prints (pcbuf == nil),
 // and the runtime.Callers function (pcbuf != nil).
 // A little clunky to merge these, but avoids
@@ -605,10 +627,13 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
 
        nprint := 0
        n := 0
+       var cgoBuf [32]uintptr
        for ; n < max && u.valid(); u.next() {
                frame := &u.frame
                f := frame.fn
 
+               cgoN := u.cgoCallers(cgoBuf[:])
+
                if pcbuf != nil {
                        // TODO: Why does cache escape? (Same below)
                        for iu, uf := newInlineUnwinder(f, u.symPC(), noEscapePtr(&u.cache)); uf.valid(); uf = iu.next(uf) {
@@ -626,6 +651,13 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
                                }
                                u.calleeFuncID = sf.funcID
                        }
+                       // Add cgo frames
+                       if skip == 0 { // skip only applies to Go frames
+                               for i := 0; i < cgoN && n < max; i++ {
+                                       (*[1 << 20]uintptr)(unsafe.Pointer(pcbuf))[n] = cgoBuf[i]
+                                       n++
+                               }
+                       }
                        n-- // offset n++ below
                }
 
@@ -669,18 +701,31 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
                                        nprint++
                                }
                        }
-               }
-               n++
-
-               if f.funcID == funcID_cgocallback && u.cgoCtxt >= 0 {
-                       ctxt := gp.cgoCtxt[u.cgoCtxt]
-                       u.cgoCtxt--
-
-                       // skip only applies to Go frames.
-                       if skip == 0 {
-                               n = tracebackCgoContext(pcbuf, printing, ctxt, n, max)
+                       // Print cgo frames.
+                       if cgoN > 0 {
+                               var arg cgoSymbolizerArg
+                               anySymbolized := false
+                               for _, pc := range cgoBuf[:cgoN] {
+                                       if n >= max {
+                                               break
+                                       }
+                                       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
+                                       }
+                                       nprint++
+                               }
+                               if anySymbolized {
+                                       // Free symbolization state.
+                                       arg.pc = 0
+                                       callCgoSymbolizer(&arg)
+                               }
                        }
                }
+               n++
        }
 
        if printing {
@@ -791,39 +836,6 @@ printloop:
        }
 }
 
-// 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