Add support for benchmark timeout.

Bug: b/221813759
Change-Id: I2dcf113bcbe61e9b6efac98cf64bfbc7cb5815a0
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java
index 6111c7e..c7336f7 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkCollectionPrinter.java
@@ -118,6 +118,11 @@
     printSemi("final name = " + quote(benchmarkName));
     printSemi("final metrics = " + StringUtils.join(", ", metrics, BraceType.SQUARE));
     printSemi("final benchmark = StandardBenchmark(name, metrics)");
+    BenchmarkTimeout timeout = BenchmarkConfig.getCommonTimeout(benchmarkVariants);
+    if (timeout != null) {
+      printSemi("final timeout = const Duration(seconds: " + timeout.asSeconds() + ")");
+      printSemi("ExecutionManagement.addTimeoutConstraint(timeout, benchmark: benchmark)");
+    }
     for (BenchmarkConfig benchmark : benchmarkVariants) {
       scopeBraces(
           () -> {
@@ -141,8 +146,9 @@
             for (BenchmarkDependency dependency : benchmark.getDependencies()) {
               scopeBraces(
                   () -> {
-                    addGolemResource(dependency.getName(), dependency.getTarball());
-                    printSemi("options.resources.add(dependency)");
+                    String dependencyName = dependency.getName();
+                    addGolemResource(dependencyName, dependency.getTarball());
+                    printSemi("options.resources.add(" + dependencyName + ")");
                   });
             }
           });
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
index 596fc05..56ea759 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkConfig.java
@@ -10,6 +10,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 public class BenchmarkConfig {
 
@@ -39,6 +40,20 @@
     return getConsistentRepresentative(variants).getSuite();
   }
 
+  // Use the largest configured timeout as the timeout for the full group.
+  public static BenchmarkTimeout getCommonTimeout(List<BenchmarkConfig> variants) {
+    BenchmarkTimeout timeout = null;
+    for (BenchmarkConfig variant : variants) {
+      BenchmarkTimeout variantTimeout = variant.getTimeout();
+      if (timeout == null) {
+        timeout = variantTimeout;
+      } else if (variantTimeout != null && timeout.asSeconds() < variantTimeout.asSeconds()) {
+        timeout = variantTimeout;
+      }
+    }
+    return timeout;
+  }
+
   private static BenchmarkConfig getConsistentRepresentative(List<BenchmarkConfig> variants) {
     if (variants.isEmpty()) {
       throw new BenchmarkConfigError("Unexpected attempt to check consistency of empty collection");
@@ -59,6 +74,7 @@
     private BenchmarkSuite suite = BenchmarkSuite.getDefault();
     private Collection<BenchmarkDependency> dependencies = new ArrayList<>();
     private int fromRevision = -1;
+    private BenchmarkTimeout timeout = null;
 
     private Builder() {}
 
@@ -88,7 +104,8 @@
           ImmutableSet.copyOf(metrics),
           suite,
           fromRevision,
-          dependencies);
+          dependencies,
+          timeout);
     }
 
     public Builder setName(String name) {
@@ -135,6 +152,11 @@
       dependencies.add(dependency);
       return this;
     }
+
+    public Builder setTimeout(long duration, TimeUnit unit) {
+      timeout = new BenchmarkTimeout(duration, unit);
+      return this;
+    }
   }
 
   public static Builder builder() {
@@ -147,6 +169,7 @@
   private final BenchmarkSuite suite;
   private final Collection<BenchmarkDependency> dependencies;
   private final int fromRevision;
+  private final BenchmarkTimeout timeout;
 
   private BenchmarkConfig(
       String name,
@@ -155,13 +178,15 @@
       ImmutableSet<BenchmarkMetric> metrics,
       BenchmarkSuite suite,
       int fromRevision,
-      Collection<BenchmarkDependency> dependencies) {
+      Collection<BenchmarkDependency> dependencies,
+      BenchmarkTimeout timeout) {
     this.id = new BenchmarkIdentifier(name, target);
     this.method = benchmarkMethod;
     this.metrics = metrics;
     this.suite = suite;
     this.fromRevision = fromRevision;
     this.dependencies = dependencies;
+    this.timeout = timeout;
   }
 
   public BenchmarkIdentifier getIdentifier() {
@@ -204,6 +229,10 @@
     return dependencies;
   }
 
+  public BenchmarkTimeout getTimeout() {
+    return timeout;
+  }
+
   public void run(BenchmarkEnvironment environment) throws Exception {
     method.run(environment);
   }
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkTimeout.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkTimeout.java
new file mode 100644
index 0000000..37ad175
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkTimeout.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.benchmarks;
+
+import java.util.concurrent.TimeUnit;
+
+public class BenchmarkTimeout {
+
+  public final long duration;
+  public final TimeUnit unit;
+
+  public BenchmarkTimeout(long duration, TimeUnit unit) {
+    this.duration = duration;
+    this.unit = unit;
+    if (asSeconds() == 0) {
+      throw new BenchmarkConfigError("Benchmark timeout must be at least one second.");
+    }
+  }
+
+  public long asSeconds() {
+    return TimeUnit.SECONDS.convert(duration, unit);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
index b2a6fe0..683d4f5 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.dump.DumpOptions;
 import java.io.IOException;
 import java.nio.file.Path;
+import java.util.concurrent.TimeUnit;
 
 public class AppDumpBenchmarkBuilder {
 
@@ -74,6 +75,7 @@
         .addDependency(dumpDependency)
         .measureRunTime()
         .measureCodeSize()
+        .setTimeout(10, TimeUnit.MINUTES)
         .build();
   }