Change `--experimental_sandbox_memory_limit` to take a number in mebibytes, renaming it to `--experimental_sandbox_memory_limit_mb`.

Also allows using HOST_RAM.

An int value can only handle up to 2GB, which is not enough.

PiperOrigin-RevId: 509171311
Change-Id: I8e45c0309b72b498e87b707fd1a4143c45a91a93
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/BUILD b/src/main/java/com/google/devtools/build/lib/sandbox/BUILD
index 92efa00..b28de40 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/BUILD
@@ -35,6 +35,7 @@
     deps = [
         "//src/main/java/com/google/devtools/build/lib/actions:localhost_capacity",
         "//src/main/java/com/google/devtools/build/lib/util",
+        "//src/main/java/com/google/devtools/build/lib/util:ram_resource_converter",
         "//src/main/java/com/google/devtools/build/lib/util:resource_converter",
         "//src/main/java/com/google/devtools/build/lib/vfs",
         "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/CgroupsInfo.java b/src/main/java/com/google/devtools/build/lib/sandbox/CgroupsInfo.java
index f07937b..cf61d49 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/CgroupsInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/CgroupsInfo.java
@@ -291,7 +291,7 @@
   /**
    * Creates a cgroups directory with the given memory limit.
    *
-   * @param memoryLimit Memory limit in bytes.
+   * @param memoryLimit Memory limit in megabytes (MiB).
    * @param dirName Base name of the directory created. In cgroups v2, <code>.scope</code> gets
    *     appended.
    */
@@ -304,13 +304,13 @@
       // In cgroups v2, we need to propagate the controllers into new subdirs.
       Files.asCharSink(new File(cgroupsDir, "memory.oom.group"), UTF_8).write("1\n");
       Files.asCharSink(new File(cgroupsDir, "memory.max"), UTF_8)
-          .write(Integer.toString(memoryLimit));
+          .write(Long.toString(memoryLimit * 1024L * 1024L));
     } else {
       cgroupsDir = new File(getBlazeDir(), dirName);
       cgroupsDir.mkdirs();
       cgroupsDir.deleteOnExit();
       Files.asCharSink(new File(cgroupsDir, "memory.limit_in_bytes"), UTF_8)
-          .write(Integer.toString(memoryLimit));
+          .write(Long.toString(memoryLimit * 1024L * 1024L));
     }
     return cgroupsDir.toString();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java
index 16fe254..9a52601 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java
@@ -331,13 +331,13 @@
             .setUseDebugMode(sandboxOptions.sandboxDebug)
             .setKillDelay(timeoutKillDelay);
 
-    if (sandboxOptions.memoryLimit > 0) {
+    if (sandboxOptions.memoryLimitMb > 0) {
       CgroupsInfo cgroupsInfo = CgroupsInfo.getInstance();
       // We put the sandbox inside a unique subdirectory using the context's ID. This ID is
       // unique per spawn run by this spawn runner.
       cgroupsDir =
           cgroupsInfo.createMemoryLimitCgroupDir(
-              "sandbox_" + context.getId(), sandboxOptions.memoryLimit);
+              "sandbox_" + context.getId(), sandboxOptions.memoryLimitMb);
       commandLineBuilder.setCgroupsDir(cgroupsDir);
     }
 
@@ -410,7 +410,7 @@
       throws IOException {
     ImmutableSet.Builder<Path> writableDirs = ImmutableSet.builder();
     writableDirs.addAll(super.getWritableDirs(sandboxExecRoot, withinSandboxExecRoot, env));
-    if (getSandboxOptions().memoryLimit > 0) {
+    if (getSandboxOptions().memoryLimitMb > 0) {
       CgroupsInfo cgroupsInfo = CgroupsInfo.getInstance();
       writableDirs.add(fileSystem.getPath(cgroupsInfo.getMountPoint().getAbsolutePath()));
     }
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java
index 84c08fe..87e133b 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java
@@ -20,6 +20,7 @@
 import com.google.common.collect.Maps;
 import com.google.devtools.build.lib.actions.LocalHostCapacity;
 import com.google.devtools.build.lib.util.OptionsUtils;
+import com.google.devtools.build.lib.util.RamResourceConverter;
 import com.google.devtools.build.lib.util.ResourceConverter;
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.Path;
@@ -396,14 +397,15 @@
   public boolean sandboxHermeticTmp;
 
   @Option(
-      name = "experimental_sandbox_memory_limit",
+      name = "experimental_sandbox_memory_limit_mb",
       defaultValue = "0",
       documentationCategory = OptionDocumentationCategory.EXECUTION_STRATEGY,
       effectTags = {OptionEffectTag.EXECUTION},
+      converter = RamResourceConverter.class,
       help =
-          "If set to true, each Linux sandbox will be limited to the given amount of memory."
-              + " Requires cgroups v2 and permissions for the users to the cgroups dir.")
-  public int memoryLimit;
+          "If > 0, each Linux sandbox will be limited to the given amount of memory (in MB)."
+              + " Requires cgroups v1 or v2 and permissions for the users to the cgroups dir.")
+  public int memoryLimitMb;
 
   /** Converter for the number of threads used for asynchronous tree deletion. */
   public static final class AsyncTreeDeletesConverter extends ResourceConverter {
diff --git a/src/main/java/com/google/devtools/build/lib/util/RamResourceConverter.java b/src/main/java/com/google/devtools/build/lib/util/RamResourceConverter.java
index e9b1591..647fcf6 100644
--- a/src/main/java/com/google/devtools/build/lib/util/RamResourceConverter.java
+++ b/src/main/java/com/google/devtools/build/lib/util/RamResourceConverter.java
@@ -32,6 +32,6 @@
 
   @Override
   public String getTypeDescription() {
-    return "an integer, or \"HOST_RAM\", optionally followed by [-|*]<float>.";
+    return "an integer number of MBs, or \"HOST_RAM\", optionally followed by [-|*]<float>.";
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerModule.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerModule.java
index bc977f3..b0450f4 100644
--- a/src/main/java/com/google/devtools/build/lib/worker/WorkerModule.java
+++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerModule.java
@@ -100,7 +100,7 @@
               sandboxOptions.sandboxDebug,
               ImmutableList.copyOf(sandboxOptions.sandboxTmpfsPath),
               ImmutableList.copyOf(sandboxOptions.sandboxWritablePath),
-              sandboxOptions.memoryLimit,
+              sandboxOptions.memoryLimitMb,
               sandboxOptions.getInaccessiblePaths(env.getRuntime().getFileSystem()));
     } else {
       workerSandboxOptions = null;
diff --git a/src/test/shell/integration/sandboxing_test.sh b/src/test/shell/integration/sandboxing_test.sh
index 4f1604d..2586e3c 100755
--- a/src/test/shell/integration/sandboxing_test.sh
+++ b/src/test/shell/integration/sandboxing_test.sh
@@ -269,16 +269,20 @@
 
   mkdir pkg
   cat >pkg/BUILD <<EOF
-genrule(name = "pkg", outs = ["pkg.out"], cmd = "pwd; echo >\$@")
+genrule(
+  name = "pkg",
+  outs = ["pkg.out"],
+  cmd = "pwd; hexdump -C -n 250000 < /dev/random | sort > /dev/null 2>\$@"
+)
 EOF
   local genfiles_base="$(bazel info ${PRODUCT_NAME}-genfiles)"
 
   bazel build --genrule_strategy=linux-sandbox \
-    --experimental_sandbox_memory_limit=1000000 //pkg \
+    --experimental_sandbox_memory_limit_mb=1000 //pkg \
     >"${TEST_log}" 2>&1 || fail "Expected build to succeed"
   rm -f ${genfiles_base}/pkg/pkg.out
   bazel build --genrule_strategy=linux-sandbox \
-    --experimental_sandbox_memory_limit=100 //pkg \
+    --experimental_sandbox_memory_limit_mb=1 //pkg \
     >"${TEST_log}" 2>&1 && fail "Expected build to fail" || true
 }
 
@@ -298,7 +302,11 @@
 
   mkdir pkg
   cat >pkg/BUILD <<EOF
-genrule(name = "pkg", outs = ["pkg.out"], cmd = "pwd; echo >\$@")
+genrule(
+  name = "pkg",
+  outs = ["pkg.out"],
+  cmd = "pwd; hexdump -C -n 250000 < /dev/random | sort > /dev/null 2>\$@"
+)
 EOF
   local genfiles_base="$(bazel info ${PRODUCT_NAME}-genfiles)"
   # Need to make sure the bazel server runs under systemd, too.
@@ -306,14 +314,14 @@
 
   XDG_RUNTIME_DIR=/run/user/$( id -u ) systemd-run --user --scope \
   bazel build --genrule_strategy=linux-sandbox \
-    --experimental_sandbox_memory_limit=1000000 //pkg \
+    --experimental_sandbox_memory_limit_mb=1000 //pkg \
     >"${TEST_log}" 2>&1 || fail "Expected build to succeed"
   rm -f ${genfiles_base}/pkg/pkg.out
 
   bazel shutdown
   XDG_RUNTIME_DIR=/run/user/$( id -u ) systemd-run --user --scope \
   bazel build --genrule_strategy=linux-sandbox \
-    --experimental_sandbox_memory_limit=100 //pkg \
+    --experimental_sandbox_memory_limit_mb=1 //pkg \
     >"${TEST_log}" 2>&1 && fail "Expected build to fail" || true
 }