Skip to content

Commit

Permalink
cmd/compile, runtime: use unwrapped PC for goroutine creation tracing
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
cherrymui committed Feb 11, 2022
1 parent 0bde2cf commit bcee121
Show file tree
Hide file tree
Showing 12 changed files with 70 additions and 3 deletions.
9 changes: 9 additions & 0 deletions src/cmd/compile/internal/escape/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions src/cmd/compile/internal/gc/obj.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/cmd/compile/internal/ir/func.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/compile/internal/ir/sizeof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},
}

Expand Down
30 changes: 30 additions & 0 deletions src/cmd/compile/internal/ssagen/ssa.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/cmd/internal/obj/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
1 change: 1 addition & 0 deletions src/cmd/internal/obj/objfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand Down
1 change: 1 addition & 0 deletions src/cmd/internal/objabi/funcdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/cmd/link/internal/ld/symtab.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/runtime/funcdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
1 change: 1 addition & 0 deletions src/runtime/symtab.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ const (
_FUNCDATA_OpenCodedDeferInfo = 4
_FUNCDATA_ArgInfo = 5
_FUNCDATA_ArgLiveInfo = 6
_FUNCDATA_WrapInfo = 7

_ArgsSizeUnknown = -0x80000000
)
Expand Down
18 changes: 16 additions & 2 deletions src/runtime/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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))
}

Expand Down Expand Up @@ -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))
}

0 comments on commit bcee121

Please sign in to comment.