Skip to content

Commit 82c1434

Browse files
committed
cmd/link: don't disable memory profiling when pprof.WriteHeapProfile is used
We have an optimization that if the memory profile is not consumed anywhere, we set the memory profiling rate to 0 to disable the "background" low-rate profiling. We detect whether the memory profile is used by checking whether the runtime.MemProfile function is reachable at link time. Previously, all APIs that access the memory profile go through runtime.MemProfile. But the code was refactored in CL 572396, and now the legacy entry point WriteHeapProfile uses pprof_memProfileInternal without going through runtime.MemProfile. In fact, even with the recommended runtime/pprof.Profile API (pprof.Lookup or pprof.Profiles), runtime.MemProfile is only (happen to be) reachable through countHeap. Change the linker to check runtime.memProfileInternal instead, which is on all code paths that retrieve the memory profile. Add a test case for WriteHeapProfile, so we cover all entry points. Fixes #68136. Change-Id: I075c8d45c95c81825a1822f032e23107aea4303c Reviewed-on: https://go-review.googlesource.com/c/go/+/596538 Reviewed-by: Than McIntosh <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 7d19d50 commit 82c1434

File tree

4 files changed

+41
-5
lines changed

4 files changed

+41
-5
lines changed

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

+28-1
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,34 @@ package main
304304
import "runtime"
305305
import "runtime/pprof"
306306
func main() {
307-
_ = pprof.Profiles()
307+
_ = pprof.Profiles()
308+
println(runtime.MemProfileRate)
309+
}
310+
`,
311+
"524288",
312+
},
313+
{
314+
"with_memprofile_runtime_pprof_writeheap",
315+
`
316+
package main
317+
import "io"
318+
import "runtime"
319+
import "runtime/pprof"
320+
func main() {
321+
_ = pprof.WriteHeapProfile(io.Discard)
322+
println(runtime.MemProfileRate)
323+
}
324+
`,
325+
"524288",
326+
},
327+
{
328+
"with_memprofile_runtime_pprof_lookupheap",
329+
`
330+
package main
331+
import "runtime"
332+
import "runtime/pprof"
333+
func main() {
334+
_ = pprof.Lookup("heap")
308335
println(runtime.MemProfileRate)
309336
}
310337
`,

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -892,9 +892,9 @@ func (ctxt *Link) linksetup() {
892892
}
893893

894894
// Set runtime.disableMemoryProfiling bool if
895-
// runtime.MemProfile is not retained in the binary after
895+
// runtime.memProfileInternal is not retained in the binary after
896896
// deadcode (and we're not dynamically linking).
897-
memProfile := ctxt.loader.Lookup("runtime.MemProfile", abiInternalVer)
897+
memProfile := ctxt.loader.Lookup("runtime.memProfileInternal", abiInternalVer)
898898
if memProfile != 0 && !ctxt.loader.AttrReachable(memProfile) && !ctxt.DynlinkingGo() {
899899
memProfSym := ctxt.loader.LookupOrCreateSym("runtime.disableMemoryProfiling", 0)
900900
sb := ctxt.loader.MakeSymbolUpdater(memProfSym)

src/runtime/mprof.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -892,9 +892,10 @@ func (r *StackRecord) Stack() []uintptr {
892892
// at the beginning of main).
893893
var MemProfileRate int = 512 * 1024
894894

895-
// disableMemoryProfiling is set by the linker if runtime.MemProfile
895+
// disableMemoryProfiling is set by the linker if memory profiling
896896
// is not used and the link type guarantees nobody else could use it
897897
// elsewhere.
898+
// We check if the runtime.memProfileInternal symbol is present.
898899
var disableMemoryProfiling bool
899900

900901
// A MemProfileRecord describes the live objects allocated
@@ -955,6 +956,13 @@ func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool) {
955956
// memProfileInternal returns the number of records n in the profile. If there
956957
// are less than size records, copyFn is invoked for each record, and ok returns
957958
// true.
959+
//
960+
// The linker set disableMemoryProfiling to true to disable memory profiling
961+
// if this function is not reachable. Mark it noinline to ensure the symbol exists.
962+
// (This function is big and normally not inlined anyway.)
963+
// See also disableMemoryProfiling above and cmd/link/internal/ld/lib.go:linksetup.
964+
//
965+
//go:noinline
958966
func memProfileInternal(size int, inuseZero bool, copyFn func(profilerecord.MemProfileRecord)) (n int, ok bool) {
959967
cycle := mProfCycle.read()
960968
// If we're between mProf_NextCycle and mProf_Flush, take care

src/runtime/pprof/pprof.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,8 @@ func writeHeapInternal(w io.Writer, debug int, defaultSampleType string) error {
586586
runtime.ReadMemStats(memStats)
587587
}
588588

589-
// Find out how many records there are (MemProfile(nil, true)),
589+
// Find out how many records there are (the call
590+
// pprof_memProfileInternal(nil, true) below),
590591
// allocate that many records, and get the data.
591592
// There's a race—more records might be added between
592593
// the two calls—so allocate a few extra records for safety

0 commit comments

Comments
 (0)