aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google
diff options
context:
space:
mode:
authorGravatar ruperts <ruperts@google.com>2017-12-12 20:57:29 -0800
committerGravatar Copybara-Service <copybara-piper@google.com>2017-12-12 20:59:05 -0800
commitb1ca7fac0b934cf8fc7ffa3284431bd93e14cc23 (patch)
treec99575107787502f467681539207a9f3600edce8 /src/main/java/com/google
parentb28285a51ab39c0983478731f03ac1a213f20ada (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.java13
-rw-r--r--src/main/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunner.java60
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.