Skip to content

Commit bcee121

Browse files
committed
cmd/compile, runtime: use unwrapped PC for goroutine creation tracing
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 <[email protected]> Reviewed-by: Than McIntosh <[email protected]> Run-TryBot: Cherry Mui <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 0bde2cf commit bcee121

File tree

12 files changed

+70
-3
lines changed

12 files changed

+70
-3
lines changed

src/cmd/compile/internal/escape/call.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,15 @@ func (e *escape) goDeferStmt(n *ir.GoDeferStmt) {
238238
fn.SetWrapper(true)
239239
fn.Nname.SetType(types.NewSignature(types.LocalPkg, nil, nil, nil, nil))
240240
fn.Body = []ir.Node{call}
241+
if call, ok := call.(*ir.CallExpr); ok && call.Op() == ir.OCALLFUNC {
242+
// If the callee is a named function, link to the original callee.
243+
x := call.X
244+
if x.Op() == ir.ONAME && x.(*ir.Name).Class == ir.PFUNC {
245+
fn.WrappedFunc = call.X.(*ir.Name).Func
246+
} else if x.Op() == ir.OMETHEXPR && ir.MethodExprFunc(x).Nname != nil {
247+
fn.WrappedFunc = ir.MethodExprName(x).Func
248+
}
249+
}
241250

242251
clo := fn.OClosure
243252
if n.Op() == ir.OGO {

src/cmd/compile/internal/gc/obj.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,10 @@ func addGCLocals() {
263263
objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
264264
x.Set(obj.AttrStatic, true)
265265
}
266+
if x := fn.WrapInfo; x != nil && !x.OnList() {
267+
objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
268+
x.Set(obj.AttrStatic, true)
269+
}
266270
}
267271
}
268272

src/cmd/compile/internal/ir/func.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ type Func struct {
133133
// function for go:nowritebarrierrec analysis. Only filled in
134134
// if nowritebarrierrecCheck != nil.
135135
NWBRCalls *[]SymAndPos
136+
137+
// For wrapper functions, WrappedFunc point to the original Func.
138+
// Currently only used for go/defer wrappers.
139+
WrappedFunc *Func
136140
}
137141

138142
func NewFunc(pos src.XPos) *Func {

src/cmd/compile/internal/ir/sizeof_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) {
2020
_32bit uintptr // size on 32bit platforms
2121
_64bit uintptr // size on 64bit platforms
2222
}{
23-
{Func{}, 192, 328},
23+
{Func{}, 196, 336},
2424
{Name{}, 112, 200},
2525
}
2626

src/cmd/compile/internal/ssagen/ssa.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6768,6 +6768,34 @@ func EmitArgInfo(f *ir.Func, abiInfo *abi.ABIParamResultInfo) *obj.LSym {
67686768
return x
67696769
}
67706770

6771+
// for wrapper, emit info of wrapped function.
6772+
func emitWrappedFuncInfo(e *ssafn, pp *objw.Progs) {
6773+
if base.Ctxt.Flag_linkshared {
6774+
// Relative reference (SymPtrOff) to another shared object doesn't work.
6775+
// Unfortunate.
6776+
return
6777+
}
6778+
6779+
wfn := e.curfn.WrappedFunc
6780+
if wfn == nil {
6781+
return
6782+
}
6783+
6784+
wsym := wfn.Linksym()
6785+
x := base.Ctxt.LookupInit(fmt.Sprintf("%s.wrapinfo", wsym.Name), func(x *obj.LSym) {
6786+
objw.SymPtrOff(x, 0, wsym)
6787+
x.Set(obj.AttrContentAddressable, true)
6788+
})
6789+
e.curfn.LSym.Func().WrapInfo = x
6790+
6791+
// Emit a funcdata pointing at the wrap info data.
6792+
p := pp.Prog(obj.AFUNCDATA)
6793+
p.From.SetConst(objabi.FUNCDATA_WrapInfo)
6794+
p.To.Type = obj.TYPE_MEM
6795+
p.To.Name = obj.NAME_EXTERN
6796+
p.To.Sym = x
6797+
}
6798+
67716799
// genssa appends entries to pp for each instruction in f.
67726800
func genssa(f *ssa.Func, pp *objw.Progs) {
67736801
var s State
@@ -6790,6 +6818,8 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
67906818
p.To.Sym = openDeferInfo
67916819
}
67926820

6821+
emitWrappedFuncInfo(e, pp)
6822+
67936823
// Remember where each block starts.
67946824
s.bstart = make([]*obj.Prog, f.NumBlocks())
67956825
s.pp = pp

src/cmd/internal/obj/link.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ type FuncInfo struct {
487487
OpenCodedDeferInfo *LSym
488488
ArgInfo *LSym // argument info for traceback
489489
ArgLiveInfo *LSym // argument liveness info for traceback
490+
WrapInfo *LSym // for wrapper, info of wrapped function
490491

491492
FuncInfoSym *LSym
492493
}

src/cmd/internal/obj/objfile.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ func contentHashSection(s *LSym) byte {
417417
strings.HasSuffix(name, ".arginfo0") ||
418418
strings.HasSuffix(name, ".arginfo1") ||
419419
strings.HasSuffix(name, ".argliveinfo") ||
420+
strings.HasSuffix(name, ".wrapinfo") ||
420421
strings.HasSuffix(name, ".args_stackmap") ||
421422
strings.HasSuffix(name, ".stkobj") {
422423
return 'F' // go.func.* or go.funcrel.*

src/cmd/internal/objabi/funcdata.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const (
2323
FUNCDATA_OpenCodedDeferInfo = 4
2424
FUNCDATA_ArgInfo = 5
2525
FUNCDATA_ArgLiveInfo = 6
26+
FUNCDATA_WrapInfo = 7
2627

2728
// ArgsSizeUnknown is set in Func.argsize to mark all functions
2829
// whose argument size is unknown (C vararg functions, and

src/cmd/link/internal/ld/symtab.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind {
567567
strings.HasSuffix(name, ".arginfo0"),
568568
strings.HasSuffix(name, ".arginfo1"),
569569
strings.HasSuffix(name, ".argliveinfo"),
570+
strings.HasSuffix(name, ".wrapinfo"),
570571
strings.HasSuffix(name, ".args_stackmap"),
571572
strings.HasSuffix(name, ".stkobj"):
572573
ldr.SetAttrNotInSymbolTable(s, true)

src/runtime/funcdata.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#define FUNCDATA_OpenCodedDeferInfo 4 /* info for func with open-coded defers */
2121
#define FUNCDATA_ArgInfo 5
2222
#define FUNCDATA_ArgLiveInfo 6
23+
#define FUNCDATA_WrapInfo 7
2324

2425
// Pseudo-assembly statements.
2526

src/runtime/symtab.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ const (
310310
_FUNCDATA_OpenCodedDeferInfo = 4
311311
_FUNCDATA_ArgInfo = 5
312312
_FUNCDATA_ArgLiveInfo = 6
313+
_FUNCDATA_WrapInfo = 7
313314

314315
_ArgsSizeUnknown = -0x80000000
315316
)

src/runtime/trace.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ func StartTrace() error {
229229
gp.traceseq = 0
230230
gp.tracelastp = getg().m.p
231231
// +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum.
232-
id := trace.stackTab.put([]uintptr{gp.startpc + sys.PCQuantum})
232+
id := trace.stackTab.put([]uintptr{startPCforTrace(gp.startpc) + sys.PCQuantum})
233233
traceEvent(traceEvGoCreate, -1, uint64(gp.goid), uint64(id), stackID)
234234
}
235235
if status == _Gwaiting {
@@ -1071,7 +1071,7 @@ func traceGoCreate(newg *g, pc uintptr) {
10711071
newg.traceseq = 0
10721072
newg.tracelastp = getg().m.p
10731073
// +PCQuantum because traceFrameForPC expects return PCs and subtracts PCQuantum.
1074-
id := trace.stackTab.put([]uintptr{pc + sys.PCQuantum})
1074+
id := trace.stackTab.put([]uintptr{startPCforTrace(pc) + sys.PCQuantum})
10751075
traceEvent(traceEvGoCreate, 2, uint64(newg.goid), uint64(id))
10761076
}
10771077

@@ -1244,3 +1244,17 @@ func trace_userLog(id uint64, category, message string) {
12441244

12451245
traceReleaseBuffer(pid)
12461246
}
1247+
1248+
// the start PC of a goroutine for tracing purposes. If pc is a wrapper,
1249+
// it returns the PC of the wrapped function. Otherwise it returns pc.
1250+
func startPCforTrace(pc uintptr) uintptr {
1251+
f := findfunc(pc)
1252+
if !f.valid() {
1253+
return pc // should not happen, but don't care
1254+
}
1255+
w := funcdata(f, _FUNCDATA_WrapInfo)
1256+
if w == nil {
1257+
return pc // not a wrapper
1258+
}
1259+
return f.datap.textAddr(*(*uint32)(w))
1260+
}

0 commit comments

Comments
 (0)