var Syms symsStruct
type symsStruct struct {
- AssertE2I *obj.LSym
- AssertE2I2 *obj.LSym
- Asanread *obj.LSym
- Asanwrite *obj.LSym
- CgoCheckMemmove *obj.LSym
- CgoCheckPtrWrite *obj.LSym
- CheckPtrAlignment *obj.LSym
- Deferproc *obj.LSym
- Deferprocat *obj.LSym
- DeferprocStack *obj.LSym
- Deferreturn *obj.LSym
- Duffcopy *obj.LSym
- Duffzero *obj.LSym
- GCWriteBarrier [8]*obj.LSym
- Goschedguarded *obj.LSym
- Growslice *obj.LSym
- InterfaceSwitch *obj.LSym
- MallocGC *obj.LSym
- Memmove *obj.LSym
- Msanread *obj.LSym
- Msanwrite *obj.LSym
- Msanmove *obj.LSym
- Newobject *obj.LSym
- Newproc *obj.LSym
- PanicBounds *obj.LSym
- PanicExtend *obj.LSym
- Panicdivide *obj.LSym
- Panicshift *obj.LSym
- PanicdottypeE *obj.LSym
- PanicdottypeI *obj.LSym
- Panicnildottype *obj.LSym
- Panicoverflow *obj.LSym
- Racefuncenter *obj.LSym
- Racefuncexit *obj.LSym
- Raceread *obj.LSym
- Racereadrange *obj.LSym
- Racewrite *obj.LSym
- Racewriterange *obj.LSym
- TypeAssert *obj.LSym
- WBZero *obj.LSym
- WBMove *obj.LSym
+ AssertE2I *obj.LSym
+ AssertE2I2 *obj.LSym
+ Asanread *obj.LSym
+ Asanwrite *obj.LSym
+ CgoCheckMemmove *obj.LSym
+ CgoCheckPtrWrite *obj.LSym
+ CheckPtrAlignment *obj.LSym
+ Deferproc *obj.LSym
+ Deferprocat *obj.LSym
+ DeferprocStack *obj.LSym
+ Deferreturn *obj.LSym
+ Duffcopy *obj.LSym
+ Duffzero *obj.LSym
+ GCWriteBarrier [8]*obj.LSym
+ Goschedguarded *obj.LSym
+ Growslice *obj.LSym
+ InterfaceSwitch *obj.LSym
+ MallocGC *obj.LSym
+ MallocGCSmallNoScan [27]*obj.LSym
+ MallocGCSmallScanNoHeader [27]*obj.LSym
+ MallocGCTiny [16]*obj.LSym
+ Memmove *obj.LSym
+ Msanread *obj.LSym
+ Msanwrite *obj.LSym
+ Msanmove *obj.LSym
+ Newobject *obj.LSym
+ Newproc *obj.LSym
+ PanicBounds *obj.LSym
+ PanicExtend *obj.LSym
+ Panicdivide *obj.LSym
+ Panicshift *obj.LSym
+ PanicdottypeE *obj.LSym
+ PanicdottypeI *obj.LSym
+ Panicnildottype *obj.LSym
+ Panicoverflow *obj.LSym
+ Racefuncenter *obj.LSym
+ Racefuncexit *obj.LSym
+ Raceread *obj.LSym
+ Racereadrange *obj.LSym
+ Racewrite *obj.LSym
+ Racewriterange *obj.LSym
+ TypeAssert *obj.LSym
+ WBZero *obj.LSym
+ WBMove *obj.LSym
// Wasm
SigPanic *obj.LSym
Staticuint64s *obj.LSym
// for rewriting results of some late-expanded rewrites (below)
(SelectN [n] m:(MakeResult ___)) => m.Args[n]
+// TODO(matloob): Try out having non-zeroing mallocs for prointerless
+// memory, and leaving the zeroing here. Then the compiler can remove
+// the zeroing if the user has explicit writes to the whole object.
+
// for late-expanded calls, recognize newobject and remove zeroing and nilchecks
-(Zero (SelectN [0] call:(StaticLECall _ _)) mem:(SelectN [1] call))
- && isSameCall(call.Aux, "runtime.newobject")
+(Zero (SelectN [0] call:(StaticLECall ___)) mem:(SelectN [1] call))
+ && isMalloc(call.Aux)
=> mem
-(Store (SelectN [0] call:(StaticLECall _ _)) x mem:(SelectN [1] call))
+(Store (SelectN [0] call:(StaticLECall ___)) x mem:(SelectN [1] call))
&& isConstZero(x)
- && isSameCall(call.Aux, "runtime.newobject")
+ && isMalloc(call.Aux)
=> mem
-(Store (OffPtr (SelectN [0] call:(StaticLECall _ _))) x mem:(SelectN [1] call))
+(Store (OffPtr (SelectN [0] call:(StaticLECall ___))) x mem:(SelectN [1] call))
&& isConstZero(x)
- && isSameCall(call.Aux, "runtime.newobject")
+ && isMalloc(call.Aux)
=> mem
-(NilCheck ptr:(SelectN [0] call:(StaticLECall _ _)) _)
- && isSameCall(call.Aux, "runtime.newobject")
+(NilCheck ptr:(SelectN [0] call:(StaticLECall ___)) _)
+ && isMalloc(call.Aux)
&& warnRule(fe.Debug_checknil(), v, "removed nil check")
=> ptr
-(NilCheck ptr:(OffPtr (SelectN [0] call:(StaticLECall _ _))) _)
- && isSameCall(call.Aux, "runtime.newobject")
+(NilCheck ptr:(OffPtr (SelectN [0] call:(StaticLECall ___))) _)
+ && isMalloc(call.Aux)
&& warnRule(fe.Debug_checknil(), v, "removed nil check")
=> ptr
return fn != nil && fn.String() == name
}
+func isMalloc(aux Aux) bool {
+ return isNewObject(aux) || isSpecializedMalloc(aux)
+}
+
+func isNewObject(aux Aux) bool {
+ fn := aux.(*AuxCall).Fn
+ return fn != nil && fn.String() == "runtime.newobject"
+}
+
+func isSpecializedMalloc(aux Aux) bool {
+ fn := aux.(*AuxCall).Fn
+ if fn == nil {
+ return false
+ }
+ name := fn.String()
+ return strings.HasPrefix(name, "runtime.mallocgcSmallNoScanSC") ||
+ strings.HasPrefix(name, "runtime.mallocgcSmallScanNoHeaderSC") ||
+ strings.HasPrefix(name, "runtime.mallocTiny")
+}
+
// canLoadUnaligned reports if the architecture supports unaligned load operations.
func canLoadUnaligned(c *Config) bool {
return c.ctxt.Arch.Alignment == 1
v.copyOf(ptr)
return true
}
- // match: (NilCheck ptr:(SelectN [0] call:(StaticLECall _ _)) _)
- // cond: isSameCall(call.Aux, "runtime.newobject") && warnRule(fe.Debug_checknil(), v, "removed nil check")
+ // match: (NilCheck ptr:(SelectN [0] call:(StaticLECall ___)) _)
+ // cond: isMalloc(call.Aux) && warnRule(fe.Debug_checknil(), v, "removed nil check")
// result: ptr
for {
ptr := v_0
break
}
call := ptr.Args[0]
- if call.Op != OpStaticLECall || len(call.Args) != 2 || !(isSameCall(call.Aux, "runtime.newobject") && warnRule(fe.Debug_checknil(), v, "removed nil check")) {
+ if call.Op != OpStaticLECall {
+ break
+ }
+ if !(isMalloc(call.Aux) && warnRule(fe.Debug_checknil(), v, "removed nil check")) {
break
}
v.copyOf(ptr)
return true
}
- // match: (NilCheck ptr:(OffPtr (SelectN [0] call:(StaticLECall _ _))) _)
- // cond: isSameCall(call.Aux, "runtime.newobject") && warnRule(fe.Debug_checknil(), v, "removed nil check")
+ // match: (NilCheck ptr:(OffPtr (SelectN [0] call:(StaticLECall ___))) _)
+ // cond: isMalloc(call.Aux) && warnRule(fe.Debug_checknil(), v, "removed nil check")
// result: ptr
for {
ptr := v_0
break
}
call := ptr_0.Args[0]
- if call.Op != OpStaticLECall || len(call.Args) != 2 || !(isSameCall(call.Aux, "runtime.newobject") && warnRule(fe.Debug_checknil(), v, "removed nil check")) {
+ if call.Op != OpStaticLECall {
+ break
+ }
+ if !(isMalloc(call.Aux) && warnRule(fe.Debug_checknil(), v, "removed nil check")) {
break
}
v.copyOf(ptr)
v.AddArg3(dst, e, mem)
return true
}
- // match: (Store (SelectN [0] call:(StaticLECall _ _)) x mem:(SelectN [1] call))
- // cond: isConstZero(x) && isSameCall(call.Aux, "runtime.newobject")
+ // match: (Store (SelectN [0] call:(StaticLECall ___)) x mem:(SelectN [1] call))
+ // cond: isConstZero(x) && isMalloc(call.Aux)
// result: mem
for {
if v_0.Op != OpSelectN || auxIntToInt64(v_0.AuxInt) != 0 {
break
}
call := v_0.Args[0]
- if call.Op != OpStaticLECall || len(call.Args) != 2 {
+ if call.Op != OpStaticLECall {
break
}
x := v_1
mem := v_2
- if mem.Op != OpSelectN || auxIntToInt64(mem.AuxInt) != 1 || call != mem.Args[0] || !(isConstZero(x) && isSameCall(call.Aux, "runtime.newobject")) {
+ if mem.Op != OpSelectN || auxIntToInt64(mem.AuxInt) != 1 || call != mem.Args[0] || !(isConstZero(x) && isMalloc(call.Aux)) {
break
}
v.copyOf(mem)
return true
}
- // match: (Store (OffPtr (SelectN [0] call:(StaticLECall _ _))) x mem:(SelectN [1] call))
- // cond: isConstZero(x) && isSameCall(call.Aux, "runtime.newobject")
+ // match: (Store (OffPtr (SelectN [0] call:(StaticLECall ___))) x mem:(SelectN [1] call))
+ // cond: isConstZero(x) && isMalloc(call.Aux)
// result: mem
for {
if v_0.Op != OpOffPtr {
break
}
call := v_0_0.Args[0]
- if call.Op != OpStaticLECall || len(call.Args) != 2 {
+ if call.Op != OpStaticLECall {
break
}
x := v_1
mem := v_2
- if mem.Op != OpSelectN || auxIntToInt64(mem.AuxInt) != 1 || call != mem.Args[0] || !(isConstZero(x) && isSameCall(call.Aux, "runtime.newobject")) {
+ if mem.Op != OpSelectN || auxIntToInt64(mem.AuxInt) != 1 || call != mem.Args[0] || !(isConstZero(x) && isMalloc(call.Aux)) {
break
}
v.copyOf(mem)
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
- // match: (Zero (SelectN [0] call:(StaticLECall _ _)) mem:(SelectN [1] call))
- // cond: isSameCall(call.Aux, "runtime.newobject")
+ // match: (Zero (SelectN [0] call:(StaticLECall ___)) mem:(SelectN [1] call))
+ // cond: isMalloc(call.Aux)
// result: mem
for {
if v_0.Op != OpSelectN || auxIntToInt64(v_0.AuxInt) != 0 {
break
}
call := v_0.Args[0]
- if call.Op != OpStaticLECall || len(call.Args) != 2 {
+ if call.Op != OpStaticLECall {
break
}
mem := v_1
- if mem.Op != OpSelectN || auxIntToInt64(mem.AuxInt) != 1 || call != mem.Args[0] || !(isSameCall(call.Aux, "runtime.newobject")) {
+ if mem.Op != OpSelectN || auxIntToInt64(mem.AuxInt) != 1 || call != mem.Args[0] || !(isMalloc(call.Aux)) {
break
}
v.copyOf(mem)
if call.Op != OpStaticCall {
return nil, false
}
- if !isSameCall(call.Aux, "runtime.newobject") {
+ // Check for new object, or for new object calls that have been transformed into size-specialized malloc calls.
+ // Calls that have return type unsafe pointer may have originally been produced by flushPendingHeapAllocations
+ // in the ssa generator, so may have not originally been newObject calls.
+ var numParameters int64
+ switch {
+ case isNewObject(call.Aux):
+ numParameters = 1
+ case isSpecializedMalloc(call.Aux) && !v.Type.IsUnsafePtr():
+ numParameters = 3
+ default:
return nil, false
}
if f.ABIDefault == f.ABI1 && len(c.intParamRegs) >= 1 {
if v.Args[0].Args[0].Op != OpSP {
return nil, false
}
- if v.Args[0].AuxInt != c.ctxt.Arch.FixedFrameSize+c.RegSize { // offset of return value
+ if v.Args[0].AuxInt != c.ctxt.Arch.FixedFrameSize+numParameters*c.RegSize { // offset of return value
return nil, false
}
return mem, true
"go/constant"
"html"
"internal/buildcfg"
+ "internal/runtime/gc"
"os"
"path/filepath"
"slices"
ir.Syms.Goschedguarded = typecheck.LookupRuntimeFunc("goschedguarded")
ir.Syms.Growslice = typecheck.LookupRuntimeFunc("growslice")
ir.Syms.InterfaceSwitch = typecheck.LookupRuntimeFunc("interfaceSwitch")
+ for i := 1; i < len(ir.Syms.MallocGCSmallNoScan); i++ {
+ ir.Syms.MallocGCSmallNoScan[i] = typecheck.LookupRuntimeFunc(fmt.Sprintf("mallocgcSmallNoScanSC%d", i))
+ }
+ for i := 1; i < len(ir.Syms.MallocGCSmallScanNoHeader); i++ {
+ ir.Syms.MallocGCSmallScanNoHeader[i] = typecheck.LookupRuntimeFunc(fmt.Sprintf("mallocgcSmallScanNoHeaderSC%d", i))
+ }
+ for i := 1; i < len(ir.Syms.MallocGCTiny); i++ {
+ ir.Syms.MallocGCTiny[i] = typecheck.LookupRuntimeFunc(fmt.Sprintf("mallocTiny%d", i))
+ }
ir.Syms.MallocGC = typecheck.LookupRuntimeFunc("mallocgc")
ir.Syms.Memmove = typecheck.LookupRuntimeFunc("memmove")
ir.Syms.Msanread = typecheck.LookupRuntimeFunc("msanread")
func (s *state) newHeapaddr(n *ir.Name) {
size := allocSize(n.Type())
if n.Type().HasPointers() || size >= maxAggregatedHeapAllocation || size == 0 {
- s.setHeapaddr(n.Pos(), n, s.newObject(n.Type(), nil))
+ s.setHeapaddr(n.Pos(), n, s.newObject(n.Type()))
return
}
// Make an allocation, but the type being allocated is just
// the first pending object. We will come back and update it
// later if needed.
- allocCall = s.newObject(n.Type(), nil)
+ allocCall = s.newObjectNonSpecialized(n.Type(), nil)
} else {
allocCall = s.pendingHeapAllocations[0].Args[0]
}
s.constBool(true), // needZero TODO: false is ok?
call.Args[1], // memory
}
- call.Aux = ssa.StaticAuxCall(ir.Syms.MallocGC, s.f.ABIDefault.ABIAnalyzeTypes(
+ mallocSym := ir.Syms.MallocGC
+ if specialMallocSym := s.specializedMallocSym(size, false); specialMallocSym != nil {
+ mallocSym = specialMallocSym
+ }
+ call.Aux = ssa.StaticAuxCall(mallocSym, s.f.ABIDefault.ABIAnalyzeTypes(
[]*types.Type{args[0].Type, args[1].Type, args[2].Type},
[]*types.Type{types.Types[types.TUNSAFEPTR]},
))
ptr.Type = types.Types[types.TUNSAFEPTR]
}
+func (s *state) specializedMallocSym(size int64, hasPointers bool) *obj.LSym {
+ if !s.sizeSpecializedMallocEnabled() {
+ return nil
+ }
+ ptrSize := s.config.PtrSize
+ ptrBits := ptrSize * 8
+ minSizeForMallocHeader := ptrSize * ptrBits
+ heapBitsInSpan := size <= minSizeForMallocHeader
+ if !heapBitsInSpan {
+ return nil
+ }
+ divRoundUp := func(n, a uintptr) uintptr { return (n + a - 1) / a }
+ sizeClass := gc.SizeToSizeClass8[divRoundUp(uintptr(size), gc.SmallSizeDiv)]
+ if hasPointers {
+ return ir.Syms.MallocGCSmallScanNoHeader[sizeClass]
+ }
+ if size < gc.TinySize {
+ return ir.Syms.MallocGCTiny[size]
+ }
+ return ir.Syms.MallocGCSmallNoScan[sizeClass]
+}
+
+func (s *state) sizeSpecializedMallocEnabled() bool {
+ if base.Flag.CompilingRuntime {
+ // The compiler forces the values of the asan, msan, and race flags to false if
+ // we're compiling the runtime, so we lose the information about whether we're
+ // building in asan, msan, or race mode. Because the specialized functions don't
+ // work in that mode, just turn if off in that case.
+ // TODO(matloob): Save the information about whether the flags were passed in
+ // originally so we can turn off size specialized malloc in that case instead
+ // using Instrumenting below. Then we can remove this condition.
+ return false
+ }
+
+ return buildcfg.Experiment.SizeSpecializedMalloc && !base.Flag.Cfg.Instrumenting
+}
+
// setHeapaddr allocates a new PAUTO variable to store ptr (which must be non-nil)
// and then sets it as n's heap address.
func (s *state) setHeapaddr(pos src.XPos, n *ir.Name, ptr *ssa.Value) {
}
// newObject returns an SSA value denoting new(typ).
-func (s *state) newObject(typ *types.Type, rtype *ssa.Value) *ssa.Value {
+func (s *state) newObject(typ *types.Type) *ssa.Value {
+ if typ.Size() == 0 {
+ return s.newValue1A(ssa.OpAddr, types.NewPtr(typ), ir.Syms.Zerobase, s.sb)
+ }
+ rtype := s.reflectType(typ)
+ if specialMallocSym := s.specializedMallocSym(typ.Size(), typ.HasPointers()); specialMallocSym != nil {
+ return s.rtcall(specialMallocSym, true, []*types.Type{types.NewPtr(typ)},
+ s.constInt(types.Types[types.TUINTPTR], typ.Size()),
+ rtype,
+ s.constBool(true),
+ )[0]
+ }
+ return s.rtcall(ir.Syms.Newobject, true, []*types.Type{types.NewPtr(typ)}, rtype)[0]
+}
+
+// newObjectNonSpecialized returns an SSA value denoting new(typ). It does
+// not produce size-specialized malloc functions.
+func (s *state) newObjectNonSpecialized(typ *types.Type, rtype *ssa.Value) *ssa.Value {
if typ.Size() == 0 {
return s.newValue1A(ssa.OpAddr, types.NewPtr(typ), ir.Syms.Zerobase, s.sb)
}
case ir.ONEW:
n := n.(*ir.UnaryExpr)
- var rtype *ssa.Value
if x, ok := n.X.(*ir.DynamicType); ok && x.Op() == ir.ODYNAMICTYPE {
- rtype = s.expr(x.RType)
+ return s.newObjectNonSpecialized(n.Type().Elem(), s.expr(x.RType))
}
- return s.newObject(n.Type().Elem(), rtype)
+ return s.newObject(n.Type().Elem())
case ir.OUNSAFEADD:
n := n.(*ir.BinaryExpr)
"internal/platform",
"internal/profile",
"internal/race",
+ "internal/runtime/gc",
"internal/saferio",
"internal/syscall/unix",
"internal/types/errors",
// desired maximum number of frames after expansion.
// This should be at least as large as the largest skip value
// used for profiling; otherwise stacks may be truncated inconsistently
- maxSkip = 6
+ maxSkip = 8
// maxProfStackDepth is the highest valid value for debug.profstackdepth.
// It's used for the bucket.stk func.
}
// Only use the part of mp.profStack we need and ignore the extra space
// reserved for delayed inline expansion with frame pointer unwinding.
- nstk := callers(5, mp.profStack[:debug.profstackdepth])
+ nstk := callers(3, mp.profStack[:debug.profstackdepth+2])
index := (mProfCycle.read() + 2) % uint32(len(memRecord{}.future))
b := stkbucket(memProfile, size, mp.profStack[:nstk], true)
legacy string
}{{
stk: []string{"runtime/pprof.allocatePersistent1K", "runtime/pprof.TestMemoryProfiler"},
- legacy: fmt.Sprintf(`%v: %v \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+ legacy: fmt.Sprintf(`%v: %v \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+( 0x[0-9,a-f]+ 0x[0-9,a-f]+)?
# 0x[0-9,a-f]+ runtime/pprof\.allocatePersistent1K\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test\.go:48
# 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test\.go:87
`, 32*memoryProfilerRun, 1024*memoryProfilerRun, 32*memoryProfilerRun, 1024*memoryProfilerRun),
}, {
stk: []string{"runtime/pprof.allocateTransient1M", "runtime/pprof.TestMemoryProfiler"},
- legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+ legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
# 0x[0-9,a-f]+ runtime/pprof\.allocateTransient1M\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:25
# 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:84
`, (1<<10)*memoryProfilerRun, (1<<20)*memoryProfilerRun),
}, {
stk: []string{"runtime/pprof.allocateTransient2M", "runtime/pprof.TestMemoryProfiler"},
- legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+ legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
# 0x[0-9,a-f]+ runtime/pprof\.allocateTransient2M\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:31
# 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:85
`, memoryProfilerRun, (2<<20)*memoryProfilerRun),
}, {
stk: []string{"runtime/pprof.allocateTransient2MInline", "runtime/pprof.TestMemoryProfiler"},
- legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
+ legacy: fmt.Sprintf(`0: 0 \[%v: %v\] @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+
# 0x[0-9,a-f]+ runtime/pprof\.allocateTransient2MInline\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:35
# 0x[0-9,a-f]+ runtime/pprof\.TestMemoryProfiler\+0x[0-9,a-f]+ .*runtime/pprof/mprof_test.go:86
`, memoryProfilerRun, (2<<20)*memoryProfilerRun),
t.Errorf("want stack depth = %d, got %d", depth, len(stk))
}
- if rootFn, wantFn := stk[depth-1], "runtime/pprof.produceProfileEvents"; rootFn != wantFn {
+ if rootFn, wantFn := stk[depth-1], "runtime/pprof.allocDeep"; rootFn != wantFn {
t.Errorf("want stack stack root %s, got %v", wantFn, rootFn)
}
}
// guaranteed to have exactly the desired depth with produceProfileEvents as
// their root frame which is expected by TestProfilerStackDepth.
func produceProfileEvents(t *testing.T, depth int) {
- allocDeep(depth - 1) // -1 for produceProfileEvents, **
+ allocDeep(depth + 1) // +1 for produceProfileEvents, **
blockChanDeep(t, depth-2) // -2 for produceProfileEvents, **, chanrecv1
blockMutexDeep(t, depth-2) // -2 for produceProfileEvents, **, Unlock
memSink = nil
func ToByteSlice() []byte { // Issue #24698
// amd64:`LEAQ\ttype:\[3\]uint8`
- // amd64:`CALL\truntime\.newobject`
+ // amd64:`CALL\truntime\.mallocTiny3`
// amd64:-`.*runtime.stringtoslicebyte`
return []byte("foo")
}
var b bool
-func f1(q *Q, xx []byte) interface{} { // ERROR "live at call to newobject: xx$" "live at entry to f1: xx$"
+func f1(q *Q, xx []byte) interface{} { // ERROR "live at call to mallocgcSmallScanNoHeaderSC[0-9]+: xx$" "live at entry to f1: xx$"
// xx was copied from the stack to the heap on the previous line:
// xx was live for the first two prints but then it switched to &xx
// being live. We should not see plain xx again.
//go:noinline
func f2(d []byte, n int) (odata, res []byte, e interface{}) { // ERROR "live at entry to f2: d$"
if n > len(d) {
- return d, nil, &T{M: "hello"} // ERROR "live at call to newobject: d"
+ return d, nil, &T{M: "hello"} // ERROR "live at call to mallocgcSmallScanNoHeaderSC[0-9]+: d"
}
res = d[:n]
odata = d[n:]
const iters = 50000
// Sizes of the allocations performed by each experiment.
frames := []string{"main.allocInterleaved1", "main.allocInterleaved2", "main.allocInterleaved3"}
+ leafFrame := "main.allocInterleaved"
// Pass if at least one of three experiments has no errors. Use a separate
// function for each experiment to identify each experiment in the profile.
allocInterleaved1(iters)
- if checkAllocations(getMemProfileRecords(), frames[0:1], iters, allocInterleavedSizes) == nil {
+ if checkAllocations(getMemProfileRecords(), leafFrame, frames[0:1], iters, allocInterleavedSizes) == nil {
// Passed on first try, report no error.
return nil
}
allocInterleaved2(iters)
- if checkAllocations(getMemProfileRecords(), frames[0:2], iters, allocInterleavedSizes) == nil {
+ if checkAllocations(getMemProfileRecords(), leafFrame, frames[0:2], iters, allocInterleavedSizes) == nil {
// Passed on second try, report no error.
return nil
}
allocInterleaved3(iters)
// If it fails a third time, we may be onto something.
- return checkAllocations(getMemProfileRecords(), frames[0:3], iters, allocInterleavedSizes)
+ return checkAllocations(getMemProfileRecords(), leafFrame, frames[0:3], iters, allocInterleavedSizes)
}
var allocInterleavedSizes = []int64{17 * 1024, 1024, 18 * 1024, 512, 16 * 1024, 256}
// Sizes of the allocations performed by each experiment.
sizes := []int64{1024, 512, 256}
frames := []string{"main.allocSmall1", "main.allocSmall2", "main.allocSmall3"}
+ leafFrame := "main.allocSmall"
// Pass if at least one of three experiments has no errors. Use a separate
// function for each experiment to identify each experiment in the profile.
allocSmall1(iters)
- if checkAllocations(getMemProfileRecords(), frames[0:1], iters, sizes) == nil {
+ if checkAllocations(getMemProfileRecords(), leafFrame, frames[0:1], iters, sizes) == nil {
// Passed on first try, report no error.
return nil
}
allocSmall2(iters)
- if checkAllocations(getMemProfileRecords(), frames[0:2], iters, sizes) == nil {
+ if checkAllocations(getMemProfileRecords(), leafFrame, frames[0:2], iters, sizes) == nil {
// Passed on second try, report no error.
return nil
}
allocSmall3(iters)
// If it fails a third time, we may be onto something.
- return checkAllocations(getMemProfileRecords(), frames[0:3], iters, sizes)
+ return checkAllocations(getMemProfileRecords(), leafFrame, frames[0:3], iters, sizes)
}
// allocSmall performs only small allocations for sanity testing.
// Look only at samples that include the named frames, and group the
// allocations by their line number. All these allocations are done from
// the same leaf function, so their line numbers are the same.
-func checkAllocations(records []runtime.MemProfileRecord, frames []string, count int64, size []int64) error {
+func checkAllocations(records []runtime.MemProfileRecord, leafFrame string, frames []string, count int64, size []int64) error {
objectsPerLine := map[int][]int64{}
bytesPerLine := map[int][]int64{}
totalCount := []int64{}
// Compute the line number of the first allocation. All the
// allocations are from the same leaf, so pick the first one.
var firstLine int
- for ln := range allocObjects(records, frames[0]) {
+ for ln := range allocObjects(records, leafFrame, frames[0]) {
if firstLine == 0 || firstLine > ln {
firstLine = ln
}
}
for _, frame := range frames {
var objectCount int64
- a := allocObjects(records, frame)
+ a := allocObjects(records, leafFrame, frame)
for s := range size {
// Allocations of size size[s] should be on line firstLine + s.
ln := firstLine + s
// allocObjects examines the profile records for samples including the
// named function and returns the allocation stats aggregated by
// source line number of the allocation (at the leaf frame).
-func allocObjects(records []runtime.MemProfileRecord, function string) map[int]allocStat {
+func allocObjects(records []runtime.MemProfileRecord, leafFrame, function string) map[int]allocStat {
a := make(map[int]allocStat)
for _, r := range records {
var pcs []uintptr
for {
frame, more := frames.Next()
name := frame.Function
- if line == 0 {
+ if name == leafFrame && line == 0 {
line = frame.Line
}
if name == function {
func f27go(b bool) {
x := 0
if b {
- go call27(func() { x++ }) // ERROR "live at call to newobject: &x$" "live at call to newobject: &x .autotmp_[0-9]+$" "live at call to newproc: &x$" // allocate two closures, the func literal, and the wrapper for go
+ go call27(func() { x++ }) // ERROR "live at call to mallocgcSmallScanNoHeaderSC[0-9]+: &x$" "live at call to mallocgcSmallScanNoHeaderSC[0-9]+: &x .autotmp_[0-9]+$" "live at call to newproc: &x$" // allocate two closures, the func literal, and the wrapper for go
}
- go call27(func() { x++ }) // ERROR "live at call to newobject: &x$" "live at call to newobject: .autotmp_[0-9]+$" // allocate two closures, the func literal, and the wrapper for go
+ go call27(func() { x++ }) // ERROR "live at call to mallocgcSmallScanNoHeaderSC[0-9]+: &x$" "live at call to mallocgcSmallScanNoHeaderSC[0-9]+: .autotmp_[0-9]+$" // allocate two closures, the func literal, and the wrapper for go
printnl()
}
g31(g18()) // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
}
if b2 {
- h31(g18()) // ERROR "live at call to convT: .autotmp_[0-9]+$" "live at call to newobject: .autotmp_[0-9]+$"
+ h31(g18()) // ERROR "live at call to convT: .autotmp_[0-9]+$" "live at call to mallocgcSmallScanNoHeaderSC[0-9]+: .autotmp_[0-9]+$"
}
if b3 {
panic(g18())
func f39b() (x [10]*int) {
x = [10]*int{}
- x[0] = new(int) // ERROR "live at call to newobject: x$"
+ x[0] = new(int) // ERROR "live at call to mallocTiny[48]: x$"
printnl() // ERROR "live at call to printnl: x$"
return x
}
func f39c() (x [10]*int) {
x = [10]*int{}
- x[0] = new(int) // ERROR "live at call to newobject: x$"
+ x[0] = new(int) // ERROR "live at call to mallocTiny[48]: x$"
printnl() // ERROR "live at call to printnl: x$"
return
}
func f27go(b bool) {
x := 0
if b {
- go call27(func() { x++ }) // ERROR "live at call to newobject: &x$" "live at call to newobject: &x .autotmp_[0-9]+$" "live at call to newproc: &x$" // allocate two closures, the func literal, and the wrapper for go
+ go call27(func() { x++ }) // ERROR "live at call to mallocgcSmallScanNoHeaderSC[0-9]+: &x$" "live at call to mallocgcSmallScanNoHeaderSC[0-9]+: &x .autotmp_[0-9]+$" "live at call to newproc: &x$" // allocate two closures, the func literal, and the wrapper for go
}
- go call27(func() { x++ }) // ERROR "live at call to newobject: &x$" "live at call to newobject: .autotmp_[0-9]+$" // allocate two closures, the func literal, and the wrapper for go
+ go call27(func() { x++ }) // ERROR "live at call to mallocgcSmallScanNoHeaderSC[0-9]+: &x$" "live at call to mallocgcSmallScanNoHeaderSC[0-9]+: .autotmp_[0-9]+$" // allocate two closures, the func literal, and the wrapper for go
printnl()
}
g31(g18()) // ERROR "stack object .autotmp_[0-9]+ \[2\]string$"
}
if b2 {
- h31(g18()) // ERROR "live at call to convT: .autotmp_[0-9]+$" "live at call to newobject: .autotmp_[0-9]+$"
+ h31(g18()) // ERROR "live at call to convT: .autotmp_[0-9]+$" "live at call to mallocgcSmallScanNoHeaderSC[0-9]+: .autotmp_[0-9]+$"
}
if b3 {
panic(g18())
func f39b() (x [10]*int) {
x = [10]*int{}
- x[0] = new(int) // ERROR "live at call to newobject: x$"
+ x[0] = new(int) // ERROR "live at call to mallocTiny[48]: x$"
printnl() // ERROR "live at call to printnl: x$"
return x
}
func f39c() (x [10]*int) {
x = [10]*int{}
- x[0] = new(int) // ERROR "live at call to newobject: x$"
+ x[0] = new(int) // ERROR "live at call to mallocTiny[48]: x$"
printnl() // ERROR "live at call to printnl: x$"
return
}
func (T) M2(a ...uintptr) {} // ERROR "escaping ...uintptr"
func TestF1() {
- var t int // ERROR "moved to heap"
- F1(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to F1: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
+ var t int // ERROR "moved to heap"
+ F1(uintptr(unsafe.Pointer(&t))) // ERROR "live at call to F1: .?autotmp" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
}
func TestF3() {
}
func TestF2() {
- var v int // ERROR "moved to heap"
- F2(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F2: .?autotmp" "escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
+ var v int // ERROR "moved to heap"
+ F2(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to mallocgcSmallNoScanSC[0-9]+: .?autotmp" "live at call to F2: .?autotmp" "escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
}
func TestF4() {
var v2 int // ERROR "moved to heap"
- F4(0, 1, uintptr(unsafe.Pointer(&v2)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to F4: .?autotmp" "escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
+ F4(0, 1, uintptr(unsafe.Pointer(&v2)), 2) // ERROR "live at call to mallocgcSmallNoScanSC[0-9]+: .?autotmp" "live at call to F4: .?autotmp" "escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
}
func TestM2() {
var t T
var v int // ERROR "moved to heap"
- t.M2(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to newobject: .?autotmp" "live at call to T.M2: .?autotmp" "escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
+ t.M2(0, 1, uintptr(unsafe.Pointer(&v)), 2) // ERROR "live at call to mallocgcSmallNoScanSC[0-9]+: .?autotmp" "live at call to T.M2: .?autotmp" "escapes to heap" "stack object .autotmp_[0-9]+ unsafe.Pointer$"
}