]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile, runtime: use unwrapped PC for goroutine creation tracing
authorCherry Mui <cherryyz@google.com>
Mon, 7 Feb 2022 17:00:44 +0000 (12:00 -0500)
committerCherry Mui <cherryyz@google.com>
Fri, 11 Feb 2022 20:01:24 +0000 (20:01 +0000)
With the switch to the register ABI, we now generate wrapper
functions for go statements in many cases. A new goroutine's start
PC now points to the wrapper function. This does not affect
execution, but the runtime tracer uses the start PC and the
function name as the name/label of that goroutine. If the start
function is a named function, using the name of the wrapper loses
that information. Furthur, the tracer's goroutine view groups
goroutines by start PC. For multiple go statements with the same
callee, they are grouped together. With the wrappers, which is
context-dependent as it is a closure, they are no longer grouped.

This CL fixes the problem by providing the underlying unwrapped
PC for tracing. The compiler emits metadata to link the unwrapped
PC to the wrapper function. And the runtime reads that metadata
and record that unwrapped PC for tracing.

(This doesn't work for shared buildmode. Unfortunate.)

TODO: is there a way to test?

Fixes #50622.

Change-Id: Iaa20e1b544111c0255eb0fc04427aab7a5e3b877
Reviewed-on: https://go-review.googlesource.com/c/go/+/384158
Trust: Cherry Mui <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Run-TryBot: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

12 files changed:
src/cmd/compile/internal/escape/call.go
src/cmd/compile/internal/gc/obj.go
src/cmd/compile/internal/ir/func.go
src/cmd/compile/internal/ir/sizeof_test.go
src/cmd/compile/internal/ssagen/ssa.go
src/cmd/internal/obj/link.go
src/cmd/internal/obj/objfile.go
src/cmd/internal/objabi/funcdata.go
src/cmd/link/internal/ld/symtab.go
src/runtime/funcdata.h
src/runtime/symtab.go
src/runtime/trace.go

index d1215afca8331e04a9519ce14a8e701df344c037..ee76adb0fac17ddfa4e68cb7912e0553d0fe2185 100644 (file)
@@ -238,6 +238,15 @@ func (e *escape) goDeferStmt(n *ir.GoDeferStmt) {
        fn.SetWrapper(true)
        fn.Nname.SetType(types.NewSignature(types.LocalPkg, nil, nil, nil, nil))
        fn.Body = []ir.Node{call}
+       if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC {
+               // If the callee is a named function, link to the original callee.
+               x := call.X
+               if x.Op() == ir.ONAME && x.(*ir.Name).Class == ir.PFUNC {
+                       fn.WrappedFunc = call.X.(*ir.Name).Func
+               } else if x.Op() == ir.OMETHEXPR && ir.MethodExprFunc(x).Nname != nil {
+                       fn.WrappedFunc = ir.MethodExprName(x).Func
+               }
+       }
 
        clo := fn.OClosure
        if n.Op() == ir.OGO {
index dcb54047f1fce75b577f8c32c40f0e19f88464e4..5353435ed1174c538a2a502b9b9d7e13c67df93a 100644 (file)
@@ -263,6 +263,10 @@ func addGCLocals() {
                        objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
                        x.Set(obj.AttrStatic, true)
                }
+               if x := fn.WrapInfo; x != nil && !x.OnList() {
+                       objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
+                       x.Set(obj.AttrStatic, true)
+               }
        }
 }
 
index 41c96079f75df3ceed4c695ed686f89f939a5415..23d56f7234e89649a5f5e3204cd075b6b7edd89f 100644 (file)
@@ -133,6 +133,10 @@ type Func struct {
        // function for go:nowritebarrierrec analysis. Only filled in
        // if nowritebarrierrecCheck != nil.
        NWBRCalls *[]SymAndPos
+
+       // For wrapper functions, WrappedFunc point to the original Func.
+       // Currently only used for go/defer wrappers.
+       WrappedFunc *Func
 }
 
 func NewFunc(pos src.XPos) *Func {
index a4421fcf531503dbac132e906cc7c55f9a429d85..72b6320261ea3ce09a5f25fecb8acfb38ad2154b 100644 (file)
@@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) {
                _32bit uintptr     // size on 32bit platforms
                _64bit uintptr     // size on 64bit platforms
        }{
-               {Func{}, 192, 328},
+               {Func{}, 196, 336},
                {Name{}, 112, 200},
        }
 
index 0b54925696eac47e8c8714199eb2e0f912d09260..364e0c8197ebf6859b51cc865667959986ccf71a 100644 (file)
@@ -6768,6 +6768,34 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
        return x
 }
 
+// for wrapper, emit info of wrapped function.
+func emitWrappedFuncInfo(e *ssafn, pp *objw.Progs) {
+       if base.Ctxt.Flag_linkshared {
+               // Relative reference (SymPtrOff) to another shared object doesn't work.
+               // Unfortunate.
+               return
+       }
+
+       wfn := e.curfn.WrappedFunc
+       if wfn == nil {
+               return
+       }
+
+       wsym := wfn.Linksym()
+       x := base.Ctxt.LookupInit(fmt.Sprintf("%s.wrapinfo", wsym.Name), func(x *obj.LSym) {
+               objw.SymPtrOff(x, 0, wsym)
+               x.Set(obj.AttrContentAddressable, true)
+       })
+       e.curfn.LSym.Func().WrapInfo = x
+
+       // Emit a funcdata pointing at the wrap info data.
+       p := pp.Prog(obj.AFUNCDATA)
+       p.From.SetConst(objabi.FUNCDATA_WrapInfo)
+       p.To.Type = obj.TYPE_MEM
+       p.To.Name = obj.NAME_EXTERN
+       p.To.Sym = x
+}
+
 // genssa appends entries to pp for each instruction in f.
 func genssa(f *ssa.Func, pp *objw.Progs) {
        var s State
@@ -6790,6 +6818,8 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
                p.To.Sym = openDeferInfo
        }
 
+       emitWrappedFuncInfo(e, pp)
+
        // Remember where each block starts.
        s.bstart = make([]*obj.Prog, f.NumBlocks())
        s.pp = pp
index 11af143f22c3c76ced5568c7fd75bbb6ab578f62..e0a3138c38cee2bda99fea8d80a6a70e00d8e18f 100644 (file)
@@ -487,6 +487,7 @@ type FuncInfo struct {
        OpenCodedDeferInfo *LSym
        ArgInfo            *LSym // argument info for traceback
        ArgLiveInfo        *LSym // argument liveness info for traceback
+       WrapInfo           *LSym // for wrapper, info of wrapped function
 
        FuncInfoSym *LSym
 }
index fa616691eb5c073eeef20af55d0d5751db0a2bad..560e8e24c458aa3a722aa4a13940fb7d59801737 100644 (file)
@@ -417,6 +417,7 @@ func contentHashSection(s *LSym) byte {
                strings.HasSuffix(name, ".arginfo0") ||
                strings.HasSuffix(name, ".arginfo1") ||
                strings.HasSuffix(name, ".argliveinfo") ||
+               strings.HasSuffix(name, ".wrapinfo") ||
                strings.HasSuffix(name, ".args_stackmap") ||
                strings.HasSuffix(name, ".stkobj") {
                return 'F' // go.func.* or go.funcrel.*
index 4d49a8d548dee836902669f3f35f5cd446e41ed1..05a1d49decbf68c5453004533b97a610e0072c80 100644 (file)
@@ -23,6 +23,7 @@ const (
        FUNCDATA_OpenCodedDeferInfo = 4
        FUNCDATA_ArgInfo            = 5
        FUNCDATA_ArgLiveInfo        = 6
+       FUNCDATA_WrapInfo           = 7
 
        // ArgsSizeUnknown is set in Func.argsize to mark all functions
        // whose argument size is unknown (C vararg functions, and
index 720c03afd2a290d31ebd46037846041ce513ff87..39066da286fba0d32b020217355001b20d1a46c9 100644 (file)
@@ -567,6 +567,7 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
                        strings.HasSuffix(name, ".arginfo0"),
                        strings.HasSuffix(name, ".arginfo1"),
                        strings.HasSuffix(name, ".argliveinfo"),
+                       strings.HasSuffix(name, ".wrapinfo"),
                        strings.HasSuffix(name, ".args_stackmap"),
                        strings.HasSuffix(name, ".stkobj"):
                        ldr.SetAttrNotInSymbolTable(s, true)
index a454dcaa6977b6b7a90a7d1c4a3047b2b7d6b488..2e2bb30446ccb523c8c8b4cec26d49fd45f1afaa 100644 (file)
@@ -20,6 +20,7 @@
 #define FUNCDATA_OpenCodedDeferInfo 4 /* info for func with open-coded defers */
 #define FUNCDATA_ArgInfo 5
 #define FUNCDATA_ArgLiveInfo 6
+#define FUNCDATA_WrapInfo 7
 
 // Pseudo-assembly statements.
 
index 017b0a0749f0e4419a9611e94a876251b9551095..ee4db47314bebd172c2d23d38abe863302fdf464 100644 (file)
@@ -310,6 +310,7 @@ const (
        _FUNCDATA_OpenCodedDeferInfo = 4
        _FUNCDATA_ArgInfo            = 5
        _FUNCDATA_ArgLiveInfo        = 6
+       _FUNCDATA_WrapInfo           = 7
 
        _ArgsSizeUnknown = -0x80000000
 )
index 71a29d4316ec8e14c1a83c9d357f17f08b7d49fc..8f60de2b05831ba7b0d6368c7bee3a20fbffda0f 100644 (file)
@@ -229,7 +229,7 @@ func StartTrace() error {
                        gp.traceseq = 0
                        gp.tracelastp = getg().m.p
                        // +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum.
-                       id := trace.stackTab.put([]uintptr{gp.startpc + sys.PCQuantum})
+                       id := trace.stackTab.put([]uintptr{startPCforTrace(gp.startpc) + sys.PCQuantum})
                        traceEvent(traceEvGoCreate, -1, uint64(gp.goid), uint64(id), stackID)
                }
                if status == _Gwaiting {
@@ -1071,7 +1071,7 @@ func traceGoCreate(newg *g, pc uintptr) {
        newg.traceseq = 0
        newg.tracelastp = getg().m.p
        // +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum.
-       id := trace.stackTab.put([]uintptr{pc + sys.PCQuantum})
+       id := trace.stackTab.put([]uintptr{startPCforTrace(pc) + sys.PCQuantum})
        traceEvent(traceEvGoCreate, 2, uint64(newg.goid), uint64(id))
 }
 
@@ -1244,3 +1244,17 @@ func trace_userLog(id uint64, category, message string) {
 
        traceReleaseBuffer(pid)
 }
+
+// the start PC of a goroutine for tracing purposes. If pc is a wrapper,
+// it returns the PC of the wrapped function. Otherwise it returns pc.
+func startPCforTrace(pc uintptr) uintptr {
+       f := findfunc(pc)
+       if !f.valid() {
+               return pc // should not happen, but don't care
+       }
+       w := funcdata(f, _FUNCDATA_WrapInfo)
+       if w == nil {
+               return pc // not a wrapper
+       }
+       return f.datap.textAddr(*(*uint32)(w))
+}