]> Cypherpunks repositories - gostls13.git/commitdiff
runtime, cmd/link/internal/ld: disable memory profiling when data unreachable
authorBrad Fitzpatrick <bradfitz@golang.org>
Mon, 8 Mar 2021 04:52:48 +0000 (20:52 -0800)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 9 Mar 2021 20:09:52 +0000 (20:09 +0000)
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>

src/cmd/link/internal/ld/ld_test.go
src/cmd/link/internal/ld/lib.go
src/runtime/mprof.go

index 836d9bff3d82cfb804a60a1031377dd4f88a7ce8..f3725cbc6a6577f7c44bad919d80dd8744872a8e 100644 (file)
@@ -224,3 +224,103 @@ func testWindowsBuildmodeCSharedASLR(t *testing.T, useASLR bool) {
                t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag should not be set")
        }
 }
+
+// TestMemProfileCheck tests that cmd/link sets
+// runtime.disableMemoryProfiling if the runtime.MemProfile
+// symbol is unreachable after deadcode (and not dynlinking).
+// The runtime then uses that to set the default value of
+// runtime.MemProfileRate, which this test checks.
+func TestMemProfileCheck(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+       t.Parallel()
+
+       tests := []struct {
+               name    string
+               prog    string
+               wantOut string
+       }{
+               {
+                       "no_memprofile",
+                       `
+package main
+import "runtime"
+func main() {
+       println(runtime.MemProfileRate)
+}
+`,
+                       "0",
+               },
+               {
+                       "with_memprofile",
+                       `
+package main
+import "runtime"
+func main() {
+       runtime.MemProfile(nil, false)
+       println(runtime.MemProfileRate)
+}
+`,
+                       "524288",
+               },
+               {
+                       "with_memprofile_indirect",
+                       `
+package main
+import "runtime"
+var f = runtime.MemProfile
+func main() {
+       if f == nil {
+               panic("no f")
+       }
+       println(runtime.MemProfileRate)
+}
+`,
+                       "524288",
+               },
+               {
+                       "with_memprofile_runtime_pprof",
+                       `
+package main
+import "runtime"
+import "runtime/pprof"
+func main() {
+        _ = pprof.Profiles()
+       println(runtime.MemProfileRate)
+}
+`,
+                       "524288",
+               },
+               {
+                       "with_memprofile_http_pprof",
+                       `
+package main
+import "runtime"
+import _ "net/http/pprof"
+func main() {
+       println(runtime.MemProfileRate)
+}
+`,
+                       "524288",
+               },
+       }
+       for _, tt := range tests {
+               tt := tt
+               t.Run(tt.name, func(t *testing.T) {
+                       t.Parallel()
+                       tempDir := t.TempDir()
+                       src := filepath.Join(tempDir, "x.go")
+                       if err := ioutil.WriteFile(src, []byte(tt.prog), 0644); err != nil {
+                               t.Fatal(err)
+                       }
+                       cmd := exec.Command(testenv.GoToolPath(t), "run", src)
+                       out, err := cmd.CombinedOutput()
+                       if err != nil {
+                               t.Fatal(err)
+                       }
+                       got := strings.TrimSpace(string(out))
+                       if got != tt.wantOut {
+                               t.Errorf("got %q; want %q", got, tt.wantOut)
+                       }
+               })
+       }
+}
index 517b0f69300088539cfdf0297b88a11d2ebc05b8..4c69d24354fc4c09567ebfeec4860b62085a822e 100644 (file)
@@ -790,6 +790,18 @@ func (ctxt *Link) linksetup() {
                        sb.SetSize(0)
                        sb.AddUint8(uint8(objabi.GOARM))
                }
+
+               // Set runtime.disableMemoryProfiling bool if
+               // runtime.MemProfile is not retained in the binary after
+               // deadcode (and we're not dynamically linking).
+               memProfile := ctxt.loader.Lookup("runtime.MemProfile", sym.SymVerABIInternal)
+               if memProfile != 0 && !ctxt.loader.AttrReachable(memProfile) && !ctxt.DynlinkingGo() {
+                       memProfSym := ctxt.loader.LookupOrCreateSym("runtime.disableMemoryProfiling", 0)
+                       sb := ctxt.loader.MakeSymbolUpdater(memProfSym)
+                       sb.SetType(sym.SDATA)
+                       sb.SetSize(0)
+                       sb.AddUint8(1) // true bool
+               }
        } else {
                // If OTOH the module does not contain the runtime package,
                // create a local symbol for the moduledata.
index c94b8f7cae60910e0797923254fd10ab3b055a8b..1156329615287735347544f6fc1544b2a6ab5216 100644 (file)
@@ -490,7 +490,22 @@ func (r *StackRecord) Stack() []uintptr {
 // memory profiling rate should do so just once, as early as
 // possible in the execution of the program (for example,
 // at the beginning of main).
-var MemProfileRate int = 512 * 1024
+var MemProfileRate int = defaultMemProfileRate(512 * 1024)
+
+// defaultMemProfileRate returns 0 if disableMemoryProfiling is set.
+// It exists primarily for the godoc rendering of MemProfileRate
+// above.
+func defaultMemProfileRate(v int) int {
+       if disableMemoryProfiling {
+               return 0
+       }
+       return v
+}
+
+// disableMemoryProfiling is set by the linker if runtime.MemProfile
+// is not used and the link type guarantees nobody else could use it
+// elsewhere.
+var disableMemoryProfiling bool
 
 // A MemProfileRecord describes the live objects allocated
 // by a particular call sequence (stack trace).