Skip to content

Commit 18510ae

Browse files
committed
runtime, cmd/link/internal/ld: disable memory profiling when data unreachable
If runtime.MemProfile is unreachable, default to not collecting any memory profiling samples, to save memory on the hash table. Fixes #42347 Change-Id: I9a4894a5fc77035fe59b1842e1ec77a1182e70c1 Reviewed-on: https://go-review.googlesource.com/c/go/+/299671 Reviewed-by: Cherry Zhang <cherryyz@google.com> Trust: Keith Randall <khr@golang.org>
1 parent e4f3cfa commit 18510ae

File tree

3 files changed

+128
-1
lines changed

3 files changed

+128
-1
lines changed

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

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,103 @@ func testWindowsBuildmodeCSharedASLR(t *testing.T, useASLR bool) {
224224
t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag should not be set")
225225
}
226226
}
227+
228+
// TestMemProfileCheck tests that cmd/link sets
229+
// runtime.disableMemoryProfiling if the runtime.MemProfile
230+
// symbol is unreachable after deadcode (and not dynlinking).
231+
// The runtime then uses that to set the default value of
232+
// runtime.MemProfileRate, which this test checks.
233+
func TestMemProfileCheck(t *testing.T) {
234+
testenv.MustHaveGoBuild(t)
235+
t.Parallel()
236+
237+
tests := []struct {
238+
name string
239+
prog string
240+
wantOut string
241+
}{
242+
{
243+
"no_memprofile",
244+
`
245+
package main
246+
import "runtime"
247+
func main() {
248+
println(runtime.MemProfileRate)
249+
}
250+
`,
251+
"0",
252+
},
253+
{
254+
"with_memprofile",
255+
`
256+
package main
257+
import "runtime"
258+
func main() {
259+
runtime.MemProfile(nil, false)
260+
println(runtime.MemProfileRate)
261+
}
262+
`,
263+
"524288",
264+
},
265+
{
266+
"with_memprofile_indirect",
267+
`
268+
package main
269+
import "runtime"
270+
var f = runtime.MemProfile
271+
func main() {
272+
if f == nil {
273+
panic("no f")
274+
}
275+
println(runtime.MemProfileRate)
276+
}
277+
`,
278+
"524288",
279+
},
280+
{
281+
"with_memprofile_runtime_pprof",
282+
`
283+
package main
284+
import "runtime"
285+
import "runtime/pprof"
286+
func main() {
287+
_ = pprof.Profiles()
288+
println(runtime.MemProfileRate)
289+
}
290+
`,
291+
"524288",
292+
},
293+
{
294+
"with_memprofile_http_pprof",
295+
`
296+
package main
297+
import "runtime"
298+
import _ "net/http/pprof"
299+
func main() {
300+
println(runtime.MemProfileRate)
301+
}
302+
`,
303+
"524288",
304+
},
305+
}
306+
for _, tt := range tests {
307+
tt := tt
308+
t.Run(tt.name, func(t *testing.T) {
309+
t.Parallel()
310+
tempDir := t.TempDir()
311+
src := filepath.Join(tempDir, "x.go")
312+
if err := ioutil.WriteFile(src, []byte(tt.prog), 0644); err != nil {
313+
t.Fatal(err)
314+
}
315+
cmd := exec.Command(testenv.GoToolPath(t), "run", src)
316+
out, err := cmd.CombinedOutput()
317+
if err != nil {
318+
t.Fatal(err)
319+
}
320+
got := strings.TrimSpace(string(out))
321+
if got != tt.wantOut {
322+
t.Errorf("got %q; want %q", got, tt.wantOut)
323+
}
324+
})
325+
}
326+
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,18 @@ func (ctxt *Link) linksetup() {
790790
sb.SetSize(0)
791791
sb.AddUint8(uint8(objabi.GOARM))
792792
}
793+
794+
// Set runtime.disableMemoryProfiling bool if
795+
// runtime.MemProfile is not retained in the binary after
796+
// deadcode (and we're not dynamically linking).
797+
memProfile := ctxt.loader.Lookup("runtime.MemProfile", sym.SymVerABIInternal)
798+
if memProfile != 0 && !ctxt.loader.AttrReachable(memProfile) && !ctxt.DynlinkingGo() {
799+
memProfSym := ctxt.loader.LookupOrCreateSym("runtime.disableMemoryProfiling", 0)
800+
sb := ctxt.loader.MakeSymbolUpdater(memProfSym)
801+
sb.SetType(sym.SDATA)
802+
sb.SetSize(0)
803+
sb.AddUint8(1) // true bool
804+
}
793805
} else {
794806
// If OTOH the module does not contain the runtime package,
795807
// create a local symbol for the moduledata.

src/runtime/mprof.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,22 @@ func (r *StackRecord) Stack() []uintptr {
490490
// memory profiling rate should do so just once, as early as
491491
// possible in the execution of the program (for example,
492492
// at the beginning of main).
493-
var MemProfileRate int = 512 * 1024
493+
var MemProfileRate int = defaultMemProfileRate(512 * 1024)
494+
495+
// defaultMemProfileRate returns 0 if disableMemoryProfiling is set.
496+
// It exists primarily for the godoc rendering of MemProfileRate
497+
// above.
498+
func defaultMemProfileRate(v int) int {
499+
if disableMemoryProfiling {
500+
return 0
501+
}
502+
return v
503+
}
504+
505+
// disableMemoryProfiling is set by the linker if runtime.MemProfile
506+
// is not used and the link type guarantees nobody else could use it
507+
// elsewhere.
508+
var disableMemoryProfiling bool
494509

495510
// A MemProfileRecord describes the live objects allocated
496511
// by a particular call sequence (stack trace).

0 commit comments

Comments
 (0)