]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: add harddecommit GODEBUG flag
authorMichael Anthony Knyszek <mknyszek@google.com>
Mon, 18 Oct 2021 19:01:33 +0000 (19:01 +0000)
committerMichael Knyszek <mknyszek@google.com>
Fri, 5 Nov 2021 17:46:41 +0000 (17:46 +0000)
This change adds a new debug flag that makes the runtime map pages
PROT_NONE in sysUnused on Linux, in addition to the usual madvise calls.
This behavior mimics the behavior of decommit on Windows, and is helpful
in debugging the scavenger. sysUsed is also updated to re-map the pages
as PROT_READ|PROT_WRITE, mimicing Windows' explicit commit behavior.

Change-Id: Iaac5fcd0e6920bd1d0e753dd4e7f0c0b128fe842
Reviewed-on: https://go-review.googlesource.com/c/go/+/356612
Trust: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
src/runtime/extern.go
src/runtime/mem_linux.go
src/runtime/runtime1.go

index b2003ba54318ded5e5f835ebdfb80d56e2f8b307..f1f6ea51231fed11952c2c11be34c25e8ea9d327 100644 (file)
@@ -78,6 +78,11 @@ It is a comma-separated list of name=val pairs setting these named variables:
        If the line ends with "(forced)", this GC was forced by a
        runtime.GC() call.
 
+       harddecommit: setting harddecommit=1 causes memory that is returned to the OS to
+       also have protections removed on it. This is the only mode of operation on Windows,
+       but is helpful in debugging scavenger-related issues on other platforms. Currently,
+       only supported on Linux.
+
        inittrace: setting inittrace=1 causes the runtime to emit a single line to standard
        error for each package with init work, summarizing the execution time and memory
        allocation. No information is printed for inits executed as part of plugin loading
index 34368510911b4dea0a578ad7e43600adfc36bb8d..f8f9c531701f97ac9682315aa648f7a4b2871cb1 100644 (file)
@@ -114,9 +114,29 @@ func sysUnused(v unsafe.Pointer, n uintptr) {
                atomic.Store(&adviseUnused, _MADV_DONTNEED)
                madvise(v, n, _MADV_DONTNEED)
        }
+
+       if debug.harddecommit > 0 {
+               p, err := mmap(v, n, _PROT_NONE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0)
+               if p != v || err != 0 {
+                       throw("runtime: cannot disable permissions in address space")
+               }
+       }
 }
 
 func sysUsed(v unsafe.Pointer, n uintptr) {
+       if debug.harddecommit > 0 {
+               p, err := mmap(v, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE, -1, 0)
+               if err == _ENOMEM {
+                       throw("runtime: out of memory")
+               }
+               if p != v || err != 0 {
+                       throw("runtime: cannot remap pages in address space")
+               }
+               return
+
+               // Don't do the sysHugePage optimization in hard decommit mode.
+               // We're breaking up pages everywhere, there's no point.
+       }
        // Partially undo the NOHUGEPAGE marks from sysUnused
        // for whole huge pages between v and v+n. This may
        // leave huge pages off at the end points v and v+n
index b6c3cbfff4806d2600730a6ebe77153598ed3f58..65e1e0eebca1c1ded1e54c6fad41a5fb6b0cfa29 100644 (file)
@@ -315,6 +315,7 @@ var debug struct {
        schedtrace         int32
        tracebackancestors int32
        asyncpreemptoff    int32
+       harddecommit       int32
 
        // debug.malloc is used as a combined debug check
        // in the malloc function and should be set
@@ -344,6 +345,7 @@ var dbgvars = []dbgVar{
        {"tracebackancestors", &debug.tracebackancestors},
        {"asyncpreemptoff", &debug.asyncpreemptoff},
        {"inittrace", &debug.inittrace},
+       {"harddecommit", &debug.harddecommit},
 }
 
 func parsedebugvars() {