diff options
author | 2017-12-12 20:57:29 -0800 | |
---|---|---|
committer | 2017-12-12 20:59:05 -0800 | |
commit | b1ca7fac0b934cf8fc7ffa3284431bd93e14cc23 (patch) | |
tree | c99575107787502f467681539207a9f3600edce8 /src/main/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/main/java/com/google')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/exec/local/LocalExecutionOptions.java | 13 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunner.java | 60 |
2 files changed, 57 insertions, 16 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/exec/local/LocalExecutionOptions.java b/src/main/java/com/google/devtools/build/lib/exec/local/LocalExecutionOptions.java index e87f2b5cc1..1b851f2b09 100644 --- a/src/main/java/com/google/devtools/build/lib/exec/local/LocalExecutionOptions.java +++ b/src/main/java/com/google/devtools/build/lib/exec/local/LocalExecutionOptions.java @@ -17,6 +17,7 @@ import com.google.devtools.common.options.Converters; import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionDocumentationCategory; import com.google.devtools.common.options.OptionEffectTag; +import com.google.devtools.common.options.OptionMetadataTag; import com.google.devtools.common.options.OptionsBase; import java.util.regex.Pattern; @@ -49,4 +50,16 @@ public class LocalExecutionOptions extends OptionsBase { + "all actions are allowed to execute locally" ) public Pattern allowedLocalAction; + + @Option( + name = "experimental_collect_local_action_metrics", + defaultValue = "false", + documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, + effectTags = {OptionEffectTag.EXECUTION}, + metadataTags = {OptionMetadataTag.DEPRECATED}, + help = + "When enabled, execution statistics (such as user and system time) are recorded for " + + "locally executed actions" + ) + public boolean collectLocalExecutionStatistics; } diff --git a/src/main/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunner.java index b1bdc8a9f0..bd5f8ff33b 100644 --- a/src/main/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunner.java @@ -36,6 +36,7 @@ import com.google.devtools.build.lib.shell.AbnormalTerminationException; import com.google.devtools.build.lib.shell.Command; import com.google.devtools.build.lib.shell.CommandException; import com.google.devtools.build.lib.shell.CommandResult; +import com.google.devtools.build.lib.shell.ExecutionStatistics; import com.google.devtools.build.lib.util.NetUtil; import com.google.devtools.build.lib.util.OS; import com.google.devtools.build.lib.util.OsUtils; @@ -48,6 +49,7 @@ import java.time.Duration; import java.util.EnumMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ThreadLocalRandom; import java.util.function.LongSupplier; import java.util.logging.Level; @@ -156,6 +158,10 @@ public final class LocalSpawnRunner implements SpawnRunner { return tempDirPath; } + private static Path createActionTemp(Path execRoot) throws IOException { + return createActionTemp(execRoot, () -> ThreadLocalRandom.current().nextLong()); + } + private final class SubprocessHandler { private final Spawn spawn; private final SpawnExecutionPolicy policy; @@ -263,31 +269,40 @@ public final class LocalSpawnRunner implements SpawnRunner { stepLog(INFO, "running locally"); setState(State.LOCAL_ACTION_RUNNING); - Path tmpDir = createActionTemp(execRoot, () -> ThreadLocalRandom.current().nextLong()); + Path tmpDir = createActionTemp(execRoot); + Optional<String> statisticsPath = Optional.empty(); try { Command cmd; - OutputStream stdOut = ByteStreams.nullOutputStream(); - OutputStream stdErr = ByteStreams.nullOutputStream(); + OutputStream stdOut; + OutputStream stdErr; + Path commandTmpDir = tmpDir.getRelative("work"); + commandTmpDir.createDirectory(); if (useProcessWrapper) { // If the process wrapper is enabled, we use its timeout feature, which first interrupts // the subprocess and only kills it after a grace period so that the subprocess can output // a stack trace, test log or similar, which is incredibly helpful for debugging. The // process wrapper also supports output file redirection, so we don't need to stream the // output through this process. - List<String> cmdLine = + stdOut = ByteStreams.nullOutputStream(); + stdErr = ByteStreams.nullOutputStream(); + ProcessWrapperUtil.CommandLineBuilder commandLineBuilder = ProcessWrapperUtil.commandLineBuilder() .setProcessWrapperPath(processWrapper) .setCommandArguments(spawn.getArguments()) .setStdoutPath(getPathOrDevNull(outErr.getOutputPath())) .setStderrPath(getPathOrDevNull(outErr.getErrorPath())) .setTimeout(policy.getTimeout()) - .setKillDelay(Duration.ofSeconds(localExecutionOptions.localSigkillGraceSeconds)) - .build(); + .setKillDelay(Duration.ofSeconds(localExecutionOptions.localSigkillGraceSeconds)); + if (localExecutionOptions.collectLocalExecutionStatistics) { + statisticsPath = Optional.of(tmpDir.getRelative("stats.out").getPathString()); + commandLineBuilder.setStatisticsPath(statisticsPath.get()); + } + List<String> cmdLine = commandLineBuilder.build(); cmd = new Command( cmdLine.toArray(new String[0]), localEnvProvider.rewriteLocalEnv( - spawn.getEnvironment(), execRoot, tmpDir, productName), + spawn.getEnvironment(), execRoot, commandTmpDir, productName), execRoot.getPathFile()); } else { stdOut = outErr.getOutputStream(); @@ -296,7 +311,7 @@ public final class LocalSpawnRunner implements SpawnRunner { new Command( spawn.getArguments().toArray(new String[0]), localEnvProvider.rewriteLocalEnv( - spawn.getEnvironment(), execRoot, tmpDir, productName), + spawn.getEnvironment(), execRoot, commandTmpDir, productName), execRoot.getPathFile(), policy.getTimeout()); } @@ -342,14 +357,27 @@ public final class LocalSpawnRunner implements SpawnRunner { wasTimeout ? Status.TIMEOUT : (exitCode == 0 ? Status.SUCCESS : Status.NON_ZERO_EXIT); - return new SpawnResult.Builder() - .setStatus(status) - .setExitCode(exitCode) - .setExecutorHostname(hostName) - .setWallTime(wallTime) - .setUserTime(commandResult.getUserExecutionTime()) - .setSystemTime(commandResult.getSystemExecutionTime()) - .build(); + SpawnResult.Builder spawnResultBuilder = + new SpawnResult.Builder() + .setStatus(status) + .setExitCode(exitCode) + .setExecutorHostname(hostName) + .setWallTime(wallTime); + if (statisticsPath.isPresent()) { + Optional<ExecutionStatistics.ResourceUsage> resourceUsage = + ExecutionStatistics.getResourceUsage(statisticsPath.get()); + if (resourceUsage.isPresent()) { + spawnResultBuilder.setUserTime(resourceUsage.get().getUserExecutionTime()); + spawnResultBuilder.setSystemTime(resourceUsage.get().getSystemExecutionTime()); + spawnResultBuilder.setNumBlockOutputOperations( + resourceUsage.get().getBlockOutputOperations()); + spawnResultBuilder.setNumBlockInputOperations( + resourceUsage.get().getBlockInputOperations()); + spawnResultBuilder.setNumInvoluntaryContextSwitches( + resourceUsage.get().getInvoluntaryContextSwitches()); + } + } + return spawnResultBuilder.build(); } finally { // Delete the temp directory tree, so the next action that this thread executes will get a // fresh, empty temp directory. |