diff options
author | 2017-12-12 20:57:29 -0800 | |
---|---|---|
committer | 2017-12-12 20:59:05 -0800 | |
commit | b1ca7fac0b934cf8fc7ffa3284431bd93e14cc23 (patch) | |
tree | c99575107787502f467681539207a9f3600edce8 /src/test/java/com/google | |
parent | b28285a51ab39c0983478731f03ac1a213f20ada (diff) |
Enable local action execution statistics collection when the LocalSpawnRunner uses the process-wrapper to run commands.
In particular, record metrics for user and system CPU execution time, block I/O and involuntary context switches.
This feature is guarded behind a new option, --experimental_collect_local_action_metrics.
RELNOTES: None.
PiperOrigin-RevId: 178856077
Diffstat (limited to 'src/test/java/com/google')
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/BUILD | 3 | ||||
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java | 331 |
2 files changed, 284 insertions, 50 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD index 2b3280ab53..e947617222 100644 --- a/src/test/java/com/google/devtools/build/lib/BUILD +++ b/src/test/java/com/google/devtools/build/lib/BUILD @@ -1204,6 +1204,7 @@ java_test( ":analysis_testutil", "//src/main/java/com/google/devtools/build/lib:build-base", "//src/main/java/com/google/devtools/build/lib:io", + "//src/main/java/com/google/devtools/build/lib:unix", "//src/main/java/com/google/devtools/build/lib:util", "//src/main/java/com/google/devtools/build/lib/actions", "//src/main/java/com/google/devtools/build/lib/exec/local", @@ -1212,7 +1213,9 @@ java_test( "//src/main/java/com/google/devtools/build/lib/vfs", "//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs", "//src/main/java/com/google/devtools/common/options", + "//src/main/tools:process-wrapper", "//src/test/java/com/google/devtools/build/lib:testutil", + "//src/test/shell/integration:spend_cpu_time", "//third_party:guava", "//third_party:junit4", "//third_party:mockito", diff --git a/src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java index 554b66c3f9..c1d867e7c2 100644 --- a/src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java +++ b/src/test/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunnerTest.java @@ -18,6 +18,7 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; @@ -27,10 +28,12 @@ import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; import com.google.common.io.ByteStreams; +import com.google.common.io.Files; import com.google.devtools.build.lib.actions.ActionInput; import com.google.devtools.build.lib.actions.ActionInputFileCache; import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander; import com.google.devtools.build.lib.actions.ExecutionRequirements; +import com.google.devtools.build.lib.actions.LocalHostCapacity; import com.google.devtools.build.lib.actions.ResourceManager; import com.google.devtools.build.lib.actions.ResourceSet; import com.google.devtools.build.lib.actions.Spawn; @@ -42,6 +45,10 @@ import com.google.devtools.build.lib.shell.JavaSubprocessFactory; import com.google.devtools.build.lib.shell.Subprocess; import com.google.devtools.build.lib.shell.SubprocessBuilder; import com.google.devtools.build.lib.shell.SubprocessFactory; +import com.google.devtools.build.lib.testutil.BlazeTestUtils; +import com.google.devtools.build.lib.testutil.TestConstants; +import com.google.devtools.build.lib.testutil.TestUtils; +import com.google.devtools.build.lib.unix.UnixFileSystem; import com.google.devtools.build.lib.util.NetUtil; import com.google.devtools.build.lib.util.OS; import com.google.devtools.build.lib.util.io.FileOutErr; @@ -52,6 +59,7 @@ import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import com.google.devtools.common.options.Options; import java.io.ByteArrayInputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -66,7 +74,6 @@ import java.util.logging.Filter; import java.util.logging.LogRecord; import java.util.logging.Logger; import java.util.regex.Pattern; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -74,9 +81,7 @@ import org.junit.runners.JUnit4; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; -/** - * Unit tests for {@link LocalSpawnRunner}. - */ +/** Unit tests for {@link LocalSpawnRunner}. */ @RunWith(JUnit4.class) public class LocalSpawnRunnerTest { private static final boolean USE_WRAPPER = true; @@ -152,6 +157,11 @@ public class LocalSpawnRunnerTest { private long timeoutMillis; private boolean prefetchCalled; private boolean lockOutputFilesCalled; + private FileOutErr fileOutErr; + + public SpawnExecutionPolicyForTesting(FileOutErr fileOutErr) { + this.fileOutErr = fileOutErr; + } @Override public int getId() { @@ -190,7 +200,7 @@ public class LocalSpawnRunnerTest { @Override public FileOutErr getFileOutErr() { - return outErr; + return fileOutErr; } @Override @@ -204,14 +214,11 @@ public class LocalSpawnRunnerTest { } } - private FileSystem fs; private final ActionInputFileCache mockFileCache = mock(ActionInputFileCache.class); private final ResourceManager resourceManager = ResourceManager.instanceForTestingOnly(); private Logger logger; - private FileOutErr outErr; - private final SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(); @Before public final void suppressLogging() { @@ -224,28 +231,34 @@ public class LocalSpawnRunnerTest { }); } - @Before - public final void setup() throws Exception { - fs = new InMemoryFileSystem(); + private FileSystem setupEnvironmentForFakeExecution() { // Prevent any subprocess execution at all. SubprocessBuilder.setSubprocessFactory(new SubprocessInterceptor()); resourceManager.setAvailableResources( ResourceSet.create(/*memoryMb=*/1, /*cpuUsage=*/1, /*ioUsage=*/1, /*localTestCount=*/1)); + return new InMemoryFileSystem(); } - @After - public final void tearDown() { + /** + * Enables real execution by default. + * + * <p>Tests should call setupEnvironmentForFakeExecution() if they do not want real execution. + */ + @Before + public final void setupEnvironmentForRealExecution() { SubprocessBuilder.setSubprocessFactory(JavaSubprocessFactory.INSTANCE); + resourceManager.setAvailableResources(LocalHostCapacity.getLocalHostCapacity()); } @Test public void vanillaZeroExit() throws Exception { - if (OS.getCurrent() == OS.WINDOWS) { - // TODO(#3536): Make this test work on Windows. - // The Command API implicitly absolutizes the path, and we get weird paths on Windows: - // T:\execroot\execroot\_bin\process-wrapper - return; - } + // TODO(#3536): Make this test work on Windows. + // The Command API implicitly absolutizes the path, and we get weird paths on Windows: + // T:\execroot\execroot\_bin\process-wrapper + assumeTrue(OS.getCurrent() != OS.WINDOWS); + + FileSystem fs = setupEnvironmentForFakeExecution(); + SubprocessFactory factory = mock(SubprocessFactory.class); ArgumentCaptor<SubprocessBuilder> captor = ArgumentCaptor.forClass(SubprocessBuilder.class); when(factory.create(captor.capture())).thenReturn(new FinishedSubprocess(0)); @@ -257,8 +270,9 @@ public class LocalSpawnRunnerTest { fs.getPath("/execroot"), options, resourceManager, USE_WRAPPER, OS.LINUX, "product-name", LocalEnvProvider.UNMODIFIED); + FileOutErr fileOutErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); + SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(fileOutErr); policy.timeoutMillis = 123 * 1000L; - outErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); assertThat(fs.getPath("/execroot").createDirectory()).isTrue(); SpawnResult result = runner.exec(SIMPLE_SPAWN, policy); verify(factory).create(any(SubprocessBuilder.class)); @@ -287,12 +301,13 @@ public class LocalSpawnRunnerTest { @Test public void noProcessWrapper() throws Exception { - if (OS.getCurrent() == OS.WINDOWS) { - // TODO(#3536): Make this test work on Windows. - // The Command API implicitly absolutizes the path, and we get weird paths on Windows: - // T:\execroot\bin\echo - return; - } + // TODO(#3536): Make this test work on Windows. + // The Command API implicitly absolutizes the path, and we get weird paths on Windows: + // T:\execroot\bin\echo + assumeTrue(OS.getCurrent() != OS.WINDOWS); + + FileSystem fs = setupEnvironmentForFakeExecution(); + SubprocessFactory factory = mock(SubprocessFactory.class); ArgumentCaptor<SubprocessBuilder> captor = ArgumentCaptor.forClass(SubprocessBuilder.class); when(factory.create(captor.capture())).thenReturn(new FinishedSubprocess(0)); @@ -304,8 +319,9 @@ public class LocalSpawnRunnerTest { fs.getPath("/execroot"), options, resourceManager, NO_WRAPPER, OS.LINUX, "product-name", LocalEnvProvider.UNMODIFIED); + FileOutErr fileOutErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); + SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(fileOutErr); policy.timeoutMillis = 123 * 1000L; - outErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); assertThat(fs.getPath("/execroot").createDirectory()).isTrue(); SpawnResult result = runner.exec(SIMPLE_SPAWN, policy); verify(factory).create(any()); @@ -325,12 +341,13 @@ public class LocalSpawnRunnerTest { @Test public void nonZeroExit() throws Exception { - if (OS.getCurrent() == OS.WINDOWS) { - // TODO(#3536): Make this test work on Windows. - // The Command API implicitly absolutizes the path, and we get weird paths on Windows: - // T:\execroot\execroot\_bin\process-wrapper - return; - } + // TODO(#3536): Make this test work on Windows. + // The Command API implicitly absolutizes the path, and we get weird paths on Windows: + // T:\execroot\execroot\_bin\process-wrapper + assumeTrue(OS.getCurrent() != OS.WINDOWS); + + FileSystem fs = setupEnvironmentForFakeExecution(); + SubprocessFactory factory = mock(SubprocessFactory.class); ArgumentCaptor<SubprocessBuilder> captor = ArgumentCaptor.forClass(SubprocessBuilder.class); when(factory.create(captor.capture())).thenReturn(new FinishedSubprocess(3)); @@ -341,8 +358,9 @@ public class LocalSpawnRunnerTest { fs.getPath("/execroot"), options, resourceManager, USE_WRAPPER, OS.LINUX, "product-name", LocalEnvProvider.UNMODIFIED); - outErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); assertThat(fs.getPath("/execroot").createDirectory()).isTrue(); + FileOutErr fileOutErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); + SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(fileOutErr); SpawnResult result = runner.exec(SIMPLE_SPAWN, policy); verify(factory).create(any(SubprocessBuilder.class)); assertThat(result.status()).isEqualTo(SpawnResult.Status.NON_ZERO_EXIT); @@ -368,6 +386,8 @@ public class LocalSpawnRunnerTest { @Test public void processStartupThrows() throws Exception { + FileSystem fs = setupEnvironmentForFakeExecution(); + SubprocessFactory factory = mock(SubprocessFactory.class); ArgumentCaptor<SubprocessBuilder> captor = ArgumentCaptor.forClass(SubprocessBuilder.class); when(factory.create(captor.capture())).thenThrow(new IOException("I'm sorry, Dave")); @@ -379,8 +399,9 @@ public class LocalSpawnRunnerTest { "product-name", LocalEnvProvider.UNMODIFIED); assertThat(fs.getPath("/out").createDirectory()).isTrue(); - outErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); assertThat(fs.getPath("/execroot").createDirectory()).isTrue(); + FileOutErr fileOutErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); + SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(fileOutErr); SpawnResult result = runner.exec(SIMPLE_SPAWN, policy); verify(factory).create(any(SubprocessBuilder.class)); assertThat(result.status()).isEqualTo(SpawnResult.Status.EXECUTION_FAILED); @@ -399,14 +420,17 @@ public class LocalSpawnRunnerTest { @Test public void disallowLocalExecution() throws Exception { + FileSystem fs = setupEnvironmentForFakeExecution(); + LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); options.allowedLocalAction = Pattern.compile("none"); LocalSpawnRunner runner = new LocalSpawnRunner( fs.getPath("/execroot"), options, resourceManager, USE_WRAPPER, OS.LINUX, "product-name", LocalEnvProvider.UNMODIFIED); - outErr = new FileOutErr(); assertThat(fs.getPath("/execroot").createDirectory()).isTrue(); + FileOutErr fileOutErr = new FileOutErr(); + SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(fileOutErr); SpawnResult reply = runner.exec(SIMPLE_SPAWN, policy); assertThat(reply.status()).isEqualTo(SpawnResult.Status.EXECUTION_DENIED); assertThat(reply.exitCode()).isEqualTo(-1); @@ -422,6 +446,8 @@ public class LocalSpawnRunnerTest { @Test public void interruptedException() throws Exception { + FileSystem fs = setupEnvironmentForFakeExecution(); + SubprocessFactory factory = mock(SubprocessFactory.class); ArgumentCaptor<SubprocessBuilder> captor = ArgumentCaptor.forClass(SubprocessBuilder.class); when(factory.create(captor.capture())).thenReturn(new FinishedSubprocess(3) { @@ -447,7 +473,8 @@ public class LocalSpawnRunnerTest { fs.getPath("/execroot"), options, resourceManager, USE_WRAPPER, OS.LINUX, "product-name", LocalEnvProvider.UNMODIFIED); - outErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); + FileOutErr fileOutErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); + SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(fileOutErr); assertThat(fs.getPath("/execroot").createDirectory()).isTrue(); try { runner.exec(SIMPLE_SPAWN, policy); @@ -461,6 +488,8 @@ public class LocalSpawnRunnerTest { @Test public void checkPrefetchCalled() throws Exception { + FileSystem fs = setupEnvironmentForFakeExecution(); + SubprocessFactory factory = mock(SubprocessFactory.class); when(factory.create(any())).thenReturn(new FinishedSubprocess(0)); SubprocessBuilder.setSubprocessFactory(factory); @@ -470,8 +499,9 @@ public class LocalSpawnRunnerTest { fs.getPath("/execroot"), options, resourceManager, USE_WRAPPER, OS.LINUX, "product-name", LocalEnvProvider.UNMODIFIED); + FileOutErr fileOutErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); + SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(fileOutErr); policy.timeoutMillis = 123 * 1000L; - outErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); assertThat(fs.getPath("/execroot").createDirectory()).isTrue(); runner.exec(SIMPLE_SPAWN, policy); assertThat(policy.prefetchCalled).isTrue(); @@ -479,6 +509,8 @@ public class LocalSpawnRunnerTest { @Test public void checkNoPrefetchCalled() throws Exception { + FileSystem fs = setupEnvironmentForFakeExecution(); + SubprocessFactory factory = mock(SubprocessFactory.class); when(factory.create(any())).thenReturn(new FinishedSubprocess(0)); SubprocessBuilder.setSubprocessFactory(factory); @@ -488,8 +520,9 @@ public class LocalSpawnRunnerTest { fs.getPath("/execroot"), options, resourceManager, USE_WRAPPER, OS.LINUX, "product-name", LocalEnvProvider.UNMODIFIED); + FileOutErr fileOutErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); + SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(fileOutErr); policy.timeoutMillis = 123 * 1000L; - outErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); Spawn spawn = new SpawnBuilder("/bin/echo", "Hi!") .withExecutionInfo(ExecutionRequirements.DISABLE_LOCAL_PREFETCH, "").build(); @@ -500,6 +533,8 @@ public class LocalSpawnRunnerTest { @Test public void checkLocalEnvProviderCalled() throws Exception { + FileSystem fs = setupEnvironmentForFakeExecution(); + SubprocessFactory factory = mock(SubprocessFactory.class); when(factory.create(any())).thenReturn(new FinishedSubprocess(0)); SubprocessBuilder.setSubprocessFactory(factory); @@ -510,8 +545,9 @@ public class LocalSpawnRunnerTest { fs.getPath("/execroot"), options, resourceManager, USE_WRAPPER, OS.LINUX, "product-name", localEnvProvider); + FileOutErr fileOutErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); + SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(fileOutErr); policy.timeoutMillis = 123 * 1000L; - outErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); assertThat(fs.getPath("/execroot").createDirectory()).isTrue(); runner.exec(SIMPLE_SPAWN, policy); @@ -528,21 +564,21 @@ public class LocalSpawnRunnerTest { } return ((Path) arg) .getPathString() - .matches("^/execroot/tmp[0-9a-fA-F]+_[0-9a-fA-F]+$"); + .matches("^/execroot/tmp[0-9a-fA-F]+_[0-9a-fA-F]+/work$"); } - } - ), + }), eq("product-name")); } @Test public void useCorrectExtensionOnWindows() throws Exception { - if (OS.getCurrent() == OS.WINDOWS) { - // TODO(#3536): Make this test work on Windows. - // The Command API implicitly absolutizes the path, and we get weird paths on Windows: - // T:\execroot\execroot\_bin\process-wrapper.exe - return; - } + // TODO(#3536): Make this test work on Windows. + // The Command API implicitly absolutizes the path, and we get weird paths on Windows: + // T:\execroot\execroot\_bin\process-wrapper.exe + assumeTrue(OS.getCurrent() != OS.WINDOWS); + + FileSystem fs = setupEnvironmentForFakeExecution(); + SubprocessFactory factory = mock(SubprocessFactory.class); ArgumentCaptor<SubprocessBuilder> captor = ArgumentCaptor.forClass(SubprocessBuilder.class); when(factory.create(captor.capture())).thenReturn(new FinishedSubprocess(0)); @@ -554,8 +590,9 @@ public class LocalSpawnRunnerTest { fs.getPath("/execroot"), options, resourceManager, USE_WRAPPER, OS.WINDOWS, "product-name", LocalEnvProvider.UNMODIFIED); + FileOutErr fileOutErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); + SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(fileOutErr); policy.timeoutMillis = 321 * 1000L; - outErr = new FileOutErr(fs.getPath("/out/stdout"), fs.getPath("/out/stderr")); assertThat(fs.getPath("/execroot").createDirectory()).isTrue(); SpawnResult result = runner.exec(SIMPLE_SPAWN, policy); verify(factory).create(any(SubprocessBuilder.class)); @@ -576,6 +613,8 @@ public class LocalSpawnRunnerTest { @Test public void testCreateActionTemp_exceptionIfUnableToCreateDir() throws IOException { + FileSystem fs = setupEnvironmentForFakeExecution(); + Path execRoot = fs.getPath("/execroot"); assertThat(execRoot.createDirectory()).isTrue(); assertThat(execRoot.exists()).isTrue(); @@ -586,6 +625,8 @@ public class LocalSpawnRunnerTest { @Test public void testCreateActionTemp_retriesIfNameClashes() throws IOException { + FileSystem fs = setupEnvironmentForFakeExecution(); + Path execRoot = fs.getPath("/execroot"); assertThat(execRoot.createDirectory()).isTrue(); assertThat(execRoot.exists()).isTrue(); @@ -610,4 +651,194 @@ public class LocalSpawnRunnerTest { return this.currentElement++; } } + + /** + * Copies the {@code process-wrapper} tool into the path under the temporary execRoot where the + * {@link LocalSpawnRunner} expects to find it. + */ + private Path copyProcessWrapperIntoExecRoot(FileSystem fs, Path execRoot) throws IOException { + File realProcessWrapperFile = + new File( + PathFragment.create(BlazeTestUtils.runfilesDir()) + .getRelative(TestConstants.PROCESS_WRAPPER_PATH) + .getPathString()); + assertThat(realProcessWrapperFile.exists()).isTrue(); + + Path binDirectoryPath = execRoot.getRelative("_bin"); + binDirectoryPath.createDirectory(fs); + + Path execRootProcessWrapperPath = binDirectoryPath.getRelative("process-wrapper"); + File execRootCpuTimeSpenderFile = execRootProcessWrapperPath.getPathFile(); + + assertThat(execRootProcessWrapperPath.exists(fs)).isFalse(); + Files.copy(realProcessWrapperFile, execRootCpuTimeSpenderFile); + assertThat(execRootProcessWrapperPath.exists(fs)).isTrue(); + + execRootProcessWrapperPath.setExecutable(fs, true); + + return execRootProcessWrapperPath; + } + + /** + * Copies the {@code spend_cpu_time} test util into the temporary execRoot so that the {@link + * LocalSpawnRunner} can execute it. + */ + private Path copyCpuTimeSpenderIntoExecRoot(FileSystem fs, Path execRoot) throws IOException { + File realCpuTimeSpenderFile = + new File( + PathFragment.create(BlazeTestUtils.runfilesDir()) + .getRelative(TestConstants.CPU_TIME_SPENDER_PATH) + .getPathString()); + assertThat(realCpuTimeSpenderFile.exists()).isTrue(); + + Path execRootCpuTimeSpenderPath = execRoot.getRelative("spend-cpu-time"); + File execRootCpuTimeSpenderFile = execRootCpuTimeSpenderPath.getPathFile(); + + assertThat(execRootCpuTimeSpenderPath.exists(fs)).isFalse(); + Files.copy(realCpuTimeSpenderFile, execRootCpuTimeSpenderFile); + assertThat(execRootCpuTimeSpenderPath.exists(fs)).isTrue(); + + execRootCpuTimeSpenderPath.setExecutable(fs, true); + + return execRootCpuTimeSpenderPath; + } + + /** + * Returns an execRoot {@link Path} inside a new temporary directory. + * + * <p>The temporary directory will be automatically deleted on exit. + */ + private Path getTemporaryExecRoot(FileSystem fs) throws IOException { + File tempDirFile = TestUtils.makeTempDir(); + tempDirFile.deleteOnExit(); + + Path tempDirPath = fs.getPath(tempDirFile.getPath()); + assertThat(tempDirPath.exists(fs)).isTrue(); + + Path execRoot = tempDirPath.getRelative("execroot"); + assertThat(execRoot.createDirectory(fs)).isTrue(); + assertThat(execRoot.exists(fs)).isTrue(); + + return execRoot; + } + + @Test + public void hasExecutionStatistics_whenOptionIsEnabled() throws Exception { + // TODO(b/62588075) Currently no process-wrapper or execution statistics support in Windows. + assumeTrue(OS.getCurrent() != OS.WINDOWS); + + FileSystem fs = new UnixFileSystem(); + + LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); + options.collectLocalExecutionStatistics = true; + + Duration minimumWallTimeToSpend = Duration.ofSeconds(10); + Duration maximumWallTimeToSpend = minimumWallTimeToSpend.plus(minimumWallTimeToSpend); // double + Duration minimumUserTimeToSpend = minimumWallTimeToSpend; + Duration maximumUserTimeToSpend = minimumUserTimeToSpend.plus(Duration.ofSeconds(2)); + Duration minimumSystemTimeToSpend = Duration.ZERO; + Duration maximumSystemTimeToSpend = minimumSystemTimeToSpend.plus(Duration.ofSeconds(2)); + + Path execRoot = getTemporaryExecRoot(fs); + copyProcessWrapperIntoExecRoot(fs, execRoot); + Path cpuTimeSpenderPath = copyCpuTimeSpenderIntoExecRoot(fs, execRoot); + + LocalSpawnRunner runner = + new LocalSpawnRunner( + execRoot, + options, + resourceManager, + USE_WRAPPER, + OS.LINUX, + "product-name", + LocalEnvProvider.UNMODIFIED); + + Spawn spawn = + new SpawnBuilder( + cpuTimeSpenderPath.getPathString(), + String.valueOf(minimumUserTimeToSpend.getSeconds()), + String.valueOf(minimumSystemTimeToSpend.getSeconds())) + .build(); + + FileOutErr fileOutErr = new FileOutErr(); + SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(fileOutErr); + + SpawnResult spawnResult = runner.exec(spawn, policy); + + assertThat(spawnResult.status()).isEqualTo(SpawnResult.Status.SUCCESS); + assertThat(spawnResult.exitCode()).isEqualTo(0); + assertThat(spawnResult.setupSuccess()).isTrue(); + assertThat(spawnResult.getExecutorHostName()).isEqualTo(NetUtil.getCachedShortHostName()); + + assertThat(spawnResult.getWallTime()).isPresent(); + assertThat(spawnResult.getWallTime().get()).isAtLeast(minimumWallTimeToSpend); + assertThat(spawnResult.getWallTime().get()).isAtMost(maximumWallTimeToSpend); + assertThat(spawnResult.getUserTime()).isPresent(); + assertThat(spawnResult.getUserTime().get()).isAtLeast(minimumUserTimeToSpend); + assertThat(spawnResult.getUserTime().get()).isAtMost(maximumUserTimeToSpend); + assertThat(spawnResult.getSystemTime()).isPresent(); + assertThat(spawnResult.getSystemTime().get()).isAtLeast(minimumSystemTimeToSpend); + assertThat(spawnResult.getSystemTime().get()).isAtMost(maximumSystemTimeToSpend); + assertThat(spawnResult.getNumBlockOutputOperations().get()).isAtLeast(0L); + assertThat(spawnResult.getNumBlockInputOperations().get()).isAtLeast(0L); + assertThat(spawnResult.getNumInvoluntaryContextSwitches().get()).isAtLeast(0L); + } + + @Test + public void hasNoExecutionStatistics_whenOptionIsDisabled() throws Exception { + // TODO(b/62588075) Currently no process-wrapper or execution statistics support in Windows. + assumeTrue(OS.getCurrent() != OS.WINDOWS); + + FileSystem fs = new UnixFileSystem(); + + LocalExecutionOptions options = Options.getDefaults(LocalExecutionOptions.class); + options.collectLocalExecutionStatistics = false; + + Duration minimumWallTimeToSpend = Duration.ofSeconds(10); + // Because of e.g. interference, wall time taken may be much larger than CPU time used. + Duration maximumWallTimeToSpend = Duration.ofSeconds(40); + + Duration minimumUserTimeToSpend = minimumWallTimeToSpend; + Duration minimumSystemTimeToSpend = Duration.ZERO; + + Path execRoot = getTemporaryExecRoot(fs); + copyProcessWrapperIntoExecRoot(fs, execRoot); + Path cpuTimeSpenderPath = copyCpuTimeSpenderIntoExecRoot(fs, execRoot); + + LocalSpawnRunner runner = + new LocalSpawnRunner( + execRoot, + options, + resourceManager, + USE_WRAPPER, + OS.LINUX, + "product-name", + LocalEnvProvider.UNMODIFIED); + + Spawn spawn = + new SpawnBuilder( + cpuTimeSpenderPath.getPathString(), + String.valueOf(minimumUserTimeToSpend.getSeconds()), + String.valueOf(minimumSystemTimeToSpend.getSeconds())) + .build(); + + FileOutErr fileOutErr = new FileOutErr(); + SpawnExecutionPolicyForTesting policy = new SpawnExecutionPolicyForTesting(fileOutErr); + + SpawnResult spawnResult = runner.exec(spawn, policy); + + assertThat(spawnResult.status()).isEqualTo(SpawnResult.Status.SUCCESS); + assertThat(spawnResult.exitCode()).isEqualTo(0); + assertThat(spawnResult.setupSuccess()).isTrue(); + assertThat(spawnResult.getExecutorHostName()).isEqualTo(NetUtil.getCachedShortHostName()); + + assertThat(spawnResult.getWallTime()).isPresent(); + assertThat(spawnResult.getWallTime().get()).isAtLeast(minimumWallTimeToSpend); + assertThat(spawnResult.getWallTime().get()).isAtMost(maximumWallTimeToSpend); + assertThat(spawnResult.getUserTime()).isEmpty(); + assertThat(spawnResult.getSystemTime()).isEmpty(); + assertThat(spawnResult.getNumBlockOutputOperations()).isEmpty(); + assertThat(spawnResult.getNumBlockInputOperations()).isEmpty(); + assertThat(spawnResult.getNumInvoluntaryContextSwitches()).isEmpty(); + } } |