mirror of
https://github.com/golang/go.git
synced 2025-11-01 09:10:57 +00:00
runtime: initialise debug settings much earlier in startup process
This is necessary specifically to set the value of `debug.decoratemappings` sufficiently early in the startup sequence that all memory ranges allocated can be named appropriately using the new Linux-specific naming API introduced in #71546. Example output (on ARM64): https://gist.github.com/9muir/3667654b9c3f52e8be92756219371672 Fixes: #75324 Change-Id: Ic0b16233f54a45adef1660c4d0df59af2f5af86a Reviewed-on: https://go-review.googlesource.com/c/go/+/703476 Auto-Submit: Michael Knyszek <mknyszek@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
parent
a846bb0aa5
commit
300d9d2714
6 changed files with 109 additions and 14 deletions
72
src/runtime/decoratemappings_test.go
Normal file
72
src/runtime/decoratemappings_test.go
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright 2025 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package runtime_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateMapLabels(t *testing.T, labels []string) {
|
||||||
|
// These are the specific region labels that need get added during the
|
||||||
|
// runtime phase. Hence they are the ones that need to be confirmed as
|
||||||
|
// present at the time the test reads its own region labels, which
|
||||||
|
// is sufficient to validate that the default `decoratemappings` value
|
||||||
|
// (enabled) was set early enough in the init process.
|
||||||
|
regions := map[string]bool{
|
||||||
|
"allspans array": false,
|
||||||
|
"gc bits": false,
|
||||||
|
"heap": false,
|
||||||
|
"heap index": false,
|
||||||
|
"heap reservation": false,
|
||||||
|
"immortal metadata": false,
|
||||||
|
"page alloc": false,
|
||||||
|
"page alloc index": false,
|
||||||
|
"page summary": false,
|
||||||
|
"scavenge index": false,
|
||||||
|
}
|
||||||
|
for _, label := range labels {
|
||||||
|
if _, ok := regions[label]; !ok {
|
||||||
|
t.Logf("unexpected region label found: \"%s\"", label)
|
||||||
|
}
|
||||||
|
regions[label] = true
|
||||||
|
}
|
||||||
|
for label, found := range regions {
|
||||||
|
if !found {
|
||||||
|
t.Logf("region label missing: \"%s\"", label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecorateMappings(t *testing.T) {
|
||||||
|
if runtime.GOOS != "linux" {
|
||||||
|
t.Skip("decoratemappings is only supported on Linux")
|
||||||
|
// /proc/self/maps is also Linux-specific
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
if rawMaps, err := os.ReadFile("/proc/self/maps"); err != nil {
|
||||||
|
t.Fatalf("failed to read /proc/self/maps: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Logf("maps:%s\n", string(rawMaps))
|
||||||
|
matches := regexp.MustCompile("[^[]+ \\[anon: Go: (.+)\\]\n").FindAllSubmatch(rawMaps, -1)
|
||||||
|
for _, match_pair := range matches {
|
||||||
|
// match_pair consists of the matching substring and the parenthesized group
|
||||||
|
labels = append(labels, string(match_pair[1]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Logf("DebugDecorateMappings: %v", *runtime.DebugDecorateMappings)
|
||||||
|
if *runtime.DebugDecorateMappings != 0 && runtime.SetVMANameSupported() {
|
||||||
|
validateMapLabels(t, labels)
|
||||||
|
} else {
|
||||||
|
if len(labels) > 0 {
|
||||||
|
t.Errorf("unexpected mapping labels present: %v", labels)
|
||||||
|
} else {
|
||||||
|
t.Skip("mapping labels absent as expected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1936,3 +1936,7 @@ func (t *TraceStackTable) Reset() {
|
||||||
func TraceStack(gp *G, tab *TraceStackTable) {
|
func TraceStack(gp *G, tab *TraceStackTable) {
|
||||||
traceStack(0, gp, (*traceStackTable)(tab))
|
traceStack(0, gp, (*traceStackTable)(tab))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var DebugDecorateMappings = &debug.decoratemappings
|
||||||
|
|
||||||
|
func SetVMANameSupported() bool { return setVMANameSupported() }
|
||||||
|
|
|
||||||
|
|
@ -789,7 +789,9 @@ func cpuinit(env string) {
|
||||||
// getGodebugEarly extracts the environment variable GODEBUG from the environment on
|
// getGodebugEarly extracts the environment variable GODEBUG from the environment on
|
||||||
// Unix-like operating systems and returns it. This function exists to extract GODEBUG
|
// Unix-like operating systems and returns it. This function exists to extract GODEBUG
|
||||||
// early before much of the runtime is initialized.
|
// early before much of the runtime is initialized.
|
||||||
func getGodebugEarly() string {
|
//
|
||||||
|
// Returns nil, false if OS doesn't provide env vars early in the init sequence.
|
||||||
|
func getGodebugEarly() (string, bool) {
|
||||||
const prefix = "GODEBUG="
|
const prefix = "GODEBUG="
|
||||||
var env string
|
var env string
|
||||||
switch GOOS {
|
switch GOOS {
|
||||||
|
|
@ -807,12 +809,16 @@ func getGodebugEarly() string {
|
||||||
s := unsafe.String(p, findnull(p))
|
s := unsafe.String(p, findnull(p))
|
||||||
|
|
||||||
if stringslite.HasPrefix(s, prefix) {
|
if stringslite.HasPrefix(s, prefix) {
|
||||||
env = gostring(p)[len(prefix):]
|
env = gostringnocopy(p)[len(prefix):]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "", false
|
||||||
}
|
}
|
||||||
return env
|
return env, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// The bootstrap sequence is:
|
// The bootstrap sequence is:
|
||||||
|
|
@ -859,12 +865,15 @@ func schedinit() {
|
||||||
// The world starts stopped.
|
// The world starts stopped.
|
||||||
worldStopped()
|
worldStopped()
|
||||||
|
|
||||||
|
godebug, parsedGodebug := getGodebugEarly()
|
||||||
|
if parsedGodebug {
|
||||||
|
parseRuntimeDebugVars(godebug)
|
||||||
|
}
|
||||||
ticks.init() // run as early as possible
|
ticks.init() // run as early as possible
|
||||||
moduledataverify()
|
moduledataverify()
|
||||||
stackinit()
|
stackinit()
|
||||||
randinit() // must run before mallocinit, alginit, mcommoninit
|
randinit() // must run before mallocinit, alginit, mcommoninit
|
||||||
mallocinit()
|
mallocinit()
|
||||||
godebug := getGodebugEarly()
|
|
||||||
cpuinit(godebug) // must run before alginit
|
cpuinit(godebug) // must run before alginit
|
||||||
alginit() // maps, hash, rand must not be used before this call
|
alginit() // maps, hash, rand must not be used before this call
|
||||||
mcommoninit(gp.m, -1)
|
mcommoninit(gp.m, -1)
|
||||||
|
|
@ -880,7 +889,12 @@ func schedinit() {
|
||||||
goenvs()
|
goenvs()
|
||||||
secure()
|
secure()
|
||||||
checkfds()
|
checkfds()
|
||||||
parsedebugvars()
|
if !parsedGodebug {
|
||||||
|
// Some platforms, e.g., Windows, didn't make env vars available "early",
|
||||||
|
// so try again now.
|
||||||
|
parseRuntimeDebugVars(gogetenv("GODEBUG"))
|
||||||
|
}
|
||||||
|
finishDebugVarsSetup()
|
||||||
gcinit()
|
gcinit()
|
||||||
|
|
||||||
// Allocate stack space that can be used when crashing due to bad stack
|
// Allocate stack space that can be used when crashing due to bad stack
|
||||||
|
|
|
||||||
|
|
@ -402,7 +402,7 @@ var dbgvars = []*dbgVar{
|
||||||
{name: "updatemaxprocs", value: &debug.updatemaxprocs, def: 1},
|
{name: "updatemaxprocs", value: &debug.updatemaxprocs, def: 1},
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsedebugvars() {
|
func parseRuntimeDebugVars(godebug string) {
|
||||||
// defaults
|
// defaults
|
||||||
debug.cgocheck = 1
|
debug.cgocheck = 1
|
||||||
debug.invalidptr = 1
|
debug.invalidptr = 1
|
||||||
|
|
@ -420,12 +420,6 @@ func parsedebugvars() {
|
||||||
}
|
}
|
||||||
debug.traceadvanceperiod = defaultTraceAdvancePeriod
|
debug.traceadvanceperiod = defaultTraceAdvancePeriod
|
||||||
|
|
||||||
godebug := gogetenv("GODEBUG")
|
|
||||||
|
|
||||||
p := new(string)
|
|
||||||
*p = godebug
|
|
||||||
godebugEnv.Store(p)
|
|
||||||
|
|
||||||
// apply runtime defaults, if any
|
// apply runtime defaults, if any
|
||||||
for _, v := range dbgvars {
|
for _, v := range dbgvars {
|
||||||
if v.def != 0 {
|
if v.def != 0 {
|
||||||
|
|
@ -437,7 +431,6 @@ func parsedebugvars() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply compile-time GODEBUG settings
|
// apply compile-time GODEBUG settings
|
||||||
parsegodebug(godebugDefault, nil)
|
parsegodebug(godebugDefault, nil)
|
||||||
|
|
||||||
|
|
@ -463,6 +456,12 @@ func parsedebugvars() {
|
||||||
if debug.gccheckmark > 0 {
|
if debug.gccheckmark > 0 {
|
||||||
debug.asyncpreemptoff = 1
|
debug.asyncpreemptoff = 1
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func finishDebugVarsSetup() {
|
||||||
|
p := new(string)
|
||||||
|
*p = gogetenv("GODEBUG")
|
||||||
|
godebugEnv.Store(p)
|
||||||
|
|
||||||
setTraceback(gogetenv("GOTRACEBACK"))
|
setTraceback(gogetenv("GOTRACEBACK"))
|
||||||
traceback_env = traceback_cache
|
traceback_env = traceback_cache
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,13 @@ import (
|
||||||
|
|
||||||
var prSetVMAUnsupported atomic.Bool
|
var prSetVMAUnsupported atomic.Bool
|
||||||
|
|
||||||
|
func setVMANameSupported() bool {
|
||||||
|
return !prSetVMAUnsupported.Load()
|
||||||
|
}
|
||||||
|
|
||||||
// setVMAName calls prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, start, len, name)
|
// setVMAName calls prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, start, len, name)
|
||||||
func setVMAName(start unsafe.Pointer, length uintptr, name string) {
|
func setVMAName(start unsafe.Pointer, length uintptr, name string) {
|
||||||
if debug.decoratemappings == 0 || prSetVMAUnsupported.Load() {
|
if debug.decoratemappings == 0 || !setVMANameSupported() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,3 +10,5 @@ import "unsafe"
|
||||||
|
|
||||||
// setVMAName isn’t implemented
|
// setVMAName isn’t implemented
|
||||||
func setVMAName(start unsafe.Pointer, len uintptr, name string) {}
|
func setVMAName(start unsafe.Pointer, len uintptr, name string) {}
|
||||||
|
|
||||||
|
func setVMANameSupported() bool { return false }
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue