Include serialization of startup profile providers in dumps

Fixes: b/232032895
Change-Id: I14e797acf1f16187828367d6d601a4cfb53ff7da
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index dc5bca3..1694c22 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -144,7 +144,8 @@
         .setMinApi(getMinApiLevel())
         .setOptimizeMultidexForLinearAlloc(isOptimizeMultidexForLinearAlloc())
         .setThreadCount(getThreadCount())
-        .setDesugarState(getDesugarState());
+        .setDesugarState(getDesugarState())
+        .setStartupProfileProviders(getStartupProfileProviders());
     if (getAndroidPlatformBuild()) {
       builder.setAndroidPlatformBuild(true);
     }
diff --git a/src/main/java/com/android/tools/r8/dump/DumpOptions.java b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
index 1d55987..27dddcc 100644
--- a/src/main/java/com/android/tools/r8/dump/DumpOptions.java
+++ b/src/main/java/com/android/tools/r8/dump/DumpOptions.java
@@ -10,9 +10,11 @@
 import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.startup.StartupProfileProvider;
 import com.android.tools.r8.utils.InternalOptions.DesugarState;
 import com.android.tools.r8.utils.ThreadUtils;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -62,6 +64,7 @@
   private final FeatureSplitConfiguration featureSplitConfiguration;
   private final ProguardConfiguration proguardConfiguration;
   private final List<ProguardConfigurationRule> mainDexKeepRules;
+  private final Collection<StartupProfileProvider> startupProfileProviders;
   private final boolean enableMissingLibraryApiModeling;
   private final boolean isAndroidPlatformBuild;
 
@@ -86,6 +89,7 @@
       FeatureSplitConfiguration featureSplitConfiguration,
       ProguardConfiguration proguardConfiguration,
       List<ProguardConfigurationRule> mainDexKeepRules,
+      Collection<StartupProfileProvider> startupProfileProviders,
       boolean enableMissingLibraryApiModeling,
       boolean isAndroidPlatformBuild,
       Map<String, String> systemProperties,
@@ -105,6 +109,7 @@
     this.featureSplitConfiguration = featureSplitConfiguration;
     this.proguardConfiguration = proguardConfiguration;
     this.mainDexKeepRules = mainDexKeepRules;
+    this.startupProfileProviders = startupProfileProviders;
     this.enableMissingLibraryApiModeling = enableMissingLibraryApiModeling;
     this.isAndroidPlatformBuild = isAndroidPlatformBuild;
     this.systemProperties = systemProperties;
@@ -268,6 +273,14 @@
     return mainDexKeepRules;
   }
 
+  public boolean hasStartupProfileProviders() {
+    return startupProfileProviders != null && !startupProfileProviders.isEmpty();
+  }
+
+  public Collection<StartupProfileProvider> getStartupProfileProviders() {
+    return startupProfileProviders;
+  }
+
   public boolean dumpInputToFile() {
     return dumpInputToFile;
   }
@@ -293,6 +306,7 @@
     private FeatureSplitConfiguration featureSplitConfiguration;
     private ProguardConfiguration proguardConfiguration;
     private List<ProguardConfigurationRule> mainDexKeepRules;
+    private Collection<StartupProfileProvider> startupProfileProviders;
 
     private boolean enableMissingLibraryApiModeling = false;
     private boolean isAndroidPlatformBuild = false;
@@ -386,6 +400,12 @@
       return this;
     }
 
+    public Builder setStartupProfileProviders(
+        Collection<StartupProfileProvider> startupProfileProviders) {
+      this.startupProfileProviders = startupProfileProviders;
+      return this;
+    }
+
     public Builder setEnableMissingLibraryApiModeling(boolean value) {
       enableMissingLibraryApiModeling = value;
       return this;
@@ -432,6 +452,7 @@
           featureSplitConfiguration,
           proguardConfiguration,
           mainDexKeepRules,
+          startupProfileProviders,
           enableMissingLibraryApiModeling,
           isAndroidPlatformBuild,
           systemProperties,
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
index bcd5b25..ae95407 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupProfileProviderUtils.java
@@ -4,11 +4,16 @@
 
 package com.android.tools.r8.experimental.startup;
 
+import com.android.tools.r8.experimental.startup.profile.StartupItem;
+import com.android.tools.r8.experimental.startup.profile.StartupProfile;
+import com.android.tools.r8.experimental.startup.profile.art.ARTProfileBuilderUtils.SyntheticToSyntheticContextGeneralization;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.startup.StartupProfileBuilder;
 import com.android.tools.r8.startup.StartupProfileProvider;
+import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
 import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.UTF8TextInputStream;
 import java.io.IOException;
 import java.io.UncheckedIOException;
@@ -35,4 +40,31 @@
       }
     };
   }
+
+  /** Serialize the given {@param startupProfileProvider} to a string for writing it to a dump. */
+  public static String serializeToString(
+      InternalOptions options, StartupProfileProvider startupProfileProvider) {
+    // Do not report missing items.
+    MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder =
+        MissingStartupProfileItemsDiagnostic.Builder.nop();
+    // Do not generalize synthetic items to their synthetic context.
+    SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization =
+        SyntheticToSyntheticContextGeneralization.createForD8();
+    StartupProfile.Builder startupProfileBuilder =
+        StartupProfile.builder(
+            options,
+            missingItemsDiagnosticBuilder,
+            startupProfileProvider,
+            syntheticToSyntheticContextGeneralization);
+    // Do not report warnings for lines that cannot be parsed.
+    startupProfileBuilder.setIgnoreWarnings();
+    // Populate the startup profile builder.
+    startupProfileProvider.getStartupProfile(startupProfileBuilder);
+    // Serialize the startup items.
+    StringBuilder resultBuilder = new StringBuilder();
+    for (StartupItem startupItem : startupProfileBuilder.build().getStartupItems()) {
+      resultBuilder.append(startupItem.serializeToString()).append('\n');
+    }
+    return resultBuilder.toString();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupClass.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupClass.java
index dbd8ef6..d281a65 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupClass.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupClass.java
@@ -75,6 +75,11 @@
     return type.hashCode();
   }
 
+  @Override
+  public String serializeToString() {
+    return getReference().toDescriptorString();
+  }
+
   public static class Builder implements StartupClassBuilder {
 
     private final DexItemFactory dexItemFactory;
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupItem.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupItem.java
index 1854c07..b7e6f1c 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupItem.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupItem.java
@@ -45,4 +45,6 @@
     assert false;
     return null;
   }
+
+  public abstract String serializeToString();
 }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupMethod.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupMethod.java
index 9e45798..18045ef 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupMethod.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupMethod.java
@@ -75,6 +75,11 @@
     return method.hashCode();
   }
 
+  @Override
+  public String serializeToString() {
+    return method.toSmaliString();
+  }
+
   public static class Builder implements StartupMethodBuilder {
 
     private final DexItemFactory dexItemFactory;
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
index 842e181..767ec7f 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/StartupProfile.java
@@ -19,9 +19,9 @@
 import com.android.tools.r8.startup.StartupProfileProvider;
 import com.android.tools.r8.startup.SyntheticStartupMethodBuilder;
 import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
-import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic.Builder;
 import com.android.tools.r8.utils.Box;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.LinkedHashSet;
@@ -108,7 +108,7 @@
 
     private final DexItemFactory dexItemFactory;
     private final MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder;
-    private final InternalOptions options;
+    private Reporter reporter;
     private final StartupProfileProvider startupProfileProvider;
     private final SyntheticToSyntheticContextGeneralization
         syntheticToSyntheticContextGeneralization;
@@ -122,7 +122,7 @@
         SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
       this.dexItemFactory = options.dexItemFactory();
       this.missingItemsDiagnosticBuilder = missingItemsDiagnosticBuilder;
-      this.options = options;
+      this.reporter = options.reporter;
       this.startupProfileProvider = startupProfileProvider;
       this.syntheticToSyntheticContextGeneralization = syntheticToSyntheticContextGeneralization;
     }
@@ -180,7 +180,7 @@
 
       HumanReadableARTProfileParser parser =
           HumanReadableARTProfileParser.builder()
-              .setReporter(options.reporter)
+              .setReporter(reporter)
               .setProfileBuilder(
                   ARTProfileBuilderUtils.createBuilderForARTProfileToStartupProfileConversion(
                       this, rulePredicateBox.get(), syntheticToSyntheticContextGeneralization))
@@ -199,6 +199,15 @@
       return this;
     }
 
+    public Builder setIgnoreWarnings() {
+      return setReporter(null);
+    }
+
+    public Builder setReporter(Reporter reporter) {
+      this.reporter = reporter;
+      return this;
+    }
+
     public StartupProfile build() {
       return new StartupProfile(startupItems);
     }
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/profile/SyntheticStartupMethod.java b/src/main/java/com/android/tools/r8/experimental/startup/profile/SyntheticStartupMethod.java
index eedcadd..c68a198 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/profile/SyntheticStartupMethod.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/profile/SyntheticStartupMethod.java
@@ -75,6 +75,11 @@
     return syntheticContextType.hashCode();
   }
 
+  @Override
+  public String serializeToString() {
+    return 'S' + syntheticContextType.toDescriptorString();
+  }
+
   public static class Builder implements SyntheticStartupMethodBuilder {
 
     private final DexItemFactory dexItemFactory;
diff --git a/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java b/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java
index 716480d..5faf4b7 100644
--- a/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java
+++ b/src/main/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnostic.java
@@ -77,6 +77,10 @@
       this.definitions = definitions;
     }
 
+    public static Builder nop() {
+      return new Builder(null);
+    }
+
     public boolean hasMissingStartupItems() {
       return !missingStartupItems.isEmpty();
     }
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 14b4d67..679e3c7 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -35,6 +35,7 @@
 import com.android.tools.r8.errors.InternalCompilerError;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.experimental.startup.StartupProfileProviderUtils;
 import com.android.tools.r8.features.ClassToFeatureSplitMap;
 import com.android.tools.r8.features.FeatureSplitConfiguration;
 import com.android.tools.r8.graph.DexType;
@@ -42,6 +43,7 @@
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.shaking.FilteredClassPath;
+import com.android.tools.r8.startup.StartupProfileProvider;
 import com.android.tools.r8.synthesis.SyntheticItems;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -524,6 +526,9 @@
             StringUtils.joinLines(dumpOptions.getMainDexKeepRules()).getBytes(),
             ZipEntry.DEFLATED);
       }
+      if (dumpOptions.hasStartupProfileProviders()) {
+        dumpStartupProfileProviders(dumpOptions.getStartupProfileProviders(), options, out);
+      }
       nextDexIndex =
           dumpProgramResources(
               dumpProgramFileName,
@@ -553,6 +558,24 @@
     return nextDexIndex;
   }
 
+  private void dumpStartupProfileProviders(
+      Collection<StartupProfileProvider> startupProfileProviders,
+      InternalOptions options,
+      ZipOutputStream out)
+      throws IOException {
+    int startupProfileProviderIndex = 1;
+    for (StartupProfileProvider startupProfileProvider : startupProfileProviders) {
+      String startupProfileProviderFileName =
+          "startup-profile-" + startupProfileProviderIndex + ".txt";
+      writeToZipStream(
+          out,
+          startupProfileProviderFileName,
+          StartupProfileProviderUtils.serializeToString(options, startupProfileProvider).getBytes(),
+          ZipEntry.DEFLATED);
+      startupProfileProviderIndex++;
+    }
+  }
+
   private static ClassFileResourceProvider createClassFileResourceProvider(
       Map<String, ProgramResource> classPathResources) {
     return new ClassFileResourceProvider() {
diff --git a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
index 8e240d7..4e1a4bc 100644
--- a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
@@ -115,6 +115,13 @@
         methodReference.getMethodName());
   }
 
+  public static String toSmaliString(MethodReference methodReference) {
+    return methodReference.getHolderClass().getDescriptor()
+        + "->"
+        + methodReference.getMethodName()
+        + methodReference.getMethodDescriptor();
+  }
+
   public static String toSourceStringWithoutHolderAndReturnType(MethodReference methodReference) {
     return toSourceString(methodReference, false, false);
   }
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index d14b01f..8f465ff 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.benchmarks.BenchmarkResults;
 import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.startup.StartupProfileProvider;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 import java.nio.file.Path;
@@ -30,6 +31,16 @@
   private StringBuilder proguardMapOutputBuilder = null;
 
   @Override
+  public boolean isD8TestBuilder() {
+    return true;
+  }
+
+  @Override
+  public D8TestBuilder asD8TestBuilder() {
+    return this;
+  }
+
+  @Override
   D8TestBuilder self() {
     return this;
   }
@@ -118,4 +129,16 @@
     getBuilder().setProguardMapConsumer((s, h) -> proguardMapOutputBuilder.append(s));
     return self();
   }
+
+  public D8TestBuilder addStartupProfileProviders(
+      StartupProfileProvider... startupProfileProviders) {
+    builder.addStartupProfileProviders(startupProfileProviders);
+    return self();
+  }
+
+  public D8TestBuilder addStartupProfileProviders(
+      Collection<StartupProfileProvider> startupProfileProviders) {
+    builder.addStartupProfileProviders(startupProfileProviders);
+    return self();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 824203a..53ff395 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -28,6 +28,7 @@
 import com.android.tools.r8.shaking.NoVerticalClassMergingRule;
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.startup.StartupProfileProvider;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
@@ -72,6 +73,16 @@
   private boolean createDefaultProguardMapConsumer = true;
 
   @Override
+  public boolean isR8TestBuilder() {
+    return true;
+  }
+
+  @Override
+  public R8TestBuilder<?> asR8TestBuilder() {
+    return this;
+  }
+
+  @Override
   R8TestCompileResult internalCompile(
       Builder builder,
       Consumer<InternalOptions> optionsConsumer,
@@ -766,4 +777,14 @@
     createDefaultProguardMapConsumer = false;
     return self();
   }
+
+  public T addStartupProfileProviders(StartupProfileProvider... startupProfileProviders) {
+    builder.addStartupProfileProviders(startupProfileProviders);
+    return self();
+  }
+
+  public T addStartupProfileProviders(Collection<StartupProfileProvider> startupProfileProviders) {
+    builder.addStartupProfileProviders(startupProfileProviders);
+    return self();
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 5d9d7f5..141f879 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -91,6 +91,22 @@
   LibraryDesugaringTestConfiguration libraryDesugaringTestConfiguration =
       LibraryDesugaringTestConfiguration.DISABLED;
 
+  public boolean isD8TestBuilder() {
+    return false;
+  }
+
+  public D8TestBuilder asD8TestBuilder() {
+    return null;
+  }
+
+  public boolean isR8TestBuilder() {
+    return false;
+  }
+
+  public R8TestBuilder<?> asR8TestBuilder() {
+    return null;
+  }
+
   public boolean isTestShrinkerBuilder() {
     return false;
   }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
index f9b2f74..8546374 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
@@ -48,31 +48,6 @@
     testForR8(parameters.getBackend())
         .addInnerClasses(getClass())
         .addKeepClassAndMembersRules(Main.class)
-        .addOptionsModification(
-            options -> {
-              StartupProfileProvider startupProfileProvider =
-                  new StartupProfileProvider() {
-
-                    @Override
-                    public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
-                      for (Class<?> startupClass : getStartupClasses()) {
-                        ClassReference startupClassReference =
-                            Reference.classFromClass(startupClass);
-                        startupProfileBuilder.addStartupClass(
-                            startupClassBuilder ->
-                                startupClassBuilder.setClassReference(startupClassReference));
-                      }
-                    }
-
-                    @Override
-                    public Origin getOrigin() {
-                      return Origin.unknown();
-                    }
-                  };
-              options
-                  .getStartupOptions()
-                  .setStartupProfileProviders(Collections.singleton(startupProfileProvider));
-            })
         .addHorizontallyMergedClassesInspector(
             inspector ->
                 inspector
@@ -89,6 +64,24 @@
                                 .assertIsCompleteMergeGroup(
                                     OnClickHandlerA.class, OnClickHandlerB.class))
                     .assertNoOtherClassesMerged())
+        .addStartupProfileProviders(
+            new StartupProfileProvider() {
+
+              @Override
+              public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+                for (Class<?> startupClass : getStartupClasses()) {
+                  ClassReference startupClassReference = Reference.classFromClass(startupClass);
+                  startupProfileBuilder.addStartupClass(
+                      startupClassBuilder ->
+                          startupClassBuilder.setClassReference(startupClassReference));
+                }
+              }
+
+              @Override
+              public Origin getOrigin() {
+                return Origin.unknown();
+              }
+            })
         .enableInliningAnnotations()
         .setMinApi(parameters.getApiLevel())
         .compile()
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
index 35ef96e..dbeb66b 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
@@ -38,7 +38,6 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.Collections;
 import java.util.concurrent.ExecutionException;
 import org.junit.After;
 import org.junit.Test;
@@ -145,18 +144,20 @@
     R8TestCompileResult r8CompileResult =
         compileApplicationWithR8(
             testBuilder ->
-                testBuilder.addOptionsModification(
-                    options -> {
-                      if (startupProfileProvider != null) {
-                        options
-                            .getStartupOptions()
-                            .setStartupProfileProviders(
-                                Collections.singleton(startupProfileProvider))
-                            .setEnableMinimalStartupDex(enableMinimalStartupDex)
-                            .setEnableStartupBoundaryOptimizations(
-                                enableStartupBoundaryOptimizations);
-                      }
-                    }));
+                testBuilder
+                    .addOptionsModification(
+                        options -> {
+                          if (startupProfileProvider != null) {
+                            options
+                                .getStartupOptions()
+                                .setEnableMinimalStartupDex(enableMinimalStartupDex)
+                                .setEnableStartupBoundaryOptimizations(
+                                    enableStartupBoundaryOptimizations);
+                          }
+                        })
+                    .applyIf(
+                        startupProfileProvider != null,
+                        b -> b.addStartupProfileProviders(startupProfileProvider)));
 
     // Compile desugared library using cf backend (without keep rules).
     L8TestCompileResult l8CompileResult = compileDesugaredLibraryWithL8();
diff --git a/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
index 051126a..f6ac982 100644
--- a/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
@@ -19,7 +19,6 @@
 import com.android.tools.r8.ThrowableConsumer;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.experimental.startup.StartupProfileProviderUtils;
-import com.android.tools.r8.startup.StartupProfileProvider;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ZipUtils;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -27,7 +26,6 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.Collections;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -236,18 +234,19 @@
       boolean enableStartupBoundaryOptimizations,
       Path outDirectory)
       throws Exception {
-    StartupProfileProvider startupProfileProvider =
-        StartupProfileProviderUtils.createFromHumanReadableARTProfile(
-            chromeDirectory.resolve("startup.txt"));
     buildR8(
         testBuilder ->
-            testBuilder.addOptionsModification(
-                options ->
-                    options
-                        .getStartupOptions()
-                        .setEnableMinimalStartupDex(enableMinimalStartupDex)
-                        .setEnableStartupBoundaryOptimizations(enableStartupBoundaryOptimizations)
-                        .setStartupProfileProviders(Collections.singleton(startupProfileProvider))),
+            testBuilder
+                .addOptionsModification(
+                    options ->
+                        options
+                            .getStartupOptions()
+                            .setEnableMinimalStartupDex(enableMinimalStartupDex)
+                            .setEnableStartupBoundaryOptimizations(
+                                enableStartupBoundaryOptimizations))
+                .addStartupProfileProviders(
+                    StartupProfileProviderUtils.createFromHumanReadableARTProfile(
+                        chromeDirectory.resolve("startup.txt"))),
         outDirectory);
   }
 
diff --git a/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java b/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
index f687df4..60cf897 100644
--- a/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
+++ b/src/test/java/com/android/tools/r8/startup/diagnostic/MissingStartupProfileItemsDiagnosticTest.java
@@ -44,11 +44,7 @@
   public void testD8() throws Exception {
     testForD8(Backend.DEX)
         .addProgramClasses(Main.class)
-        .addOptionsModification(
-            options ->
-                options
-                    .getStartupOptions()
-                    .setStartupProfileProviders(getStartupProfileProviders()))
+        .addStartupProfileProviders(getStartupProfileProviders())
         .release()
         .setIntermediate(true)
         .setMinApi(AndroidApiLevel.LATEST)
@@ -60,11 +56,7 @@
     testForR8(Backend.DEX)
         .addProgramClasses(Main.class)
         .addKeepMainRule(Main.class)
-        .addOptionsModification(
-            options ->
-                options
-                    .getStartupOptions()
-                    .setStartupProfileProviders(getStartupProfileProviders()))
+        .addStartupProfileProviders(getStartupProfileProviders())
         .allowDiagnosticWarningMessages()
         .setMinApi(AndroidApiLevel.LATEST)
         .compileWithExpectedDiagnostics(this::inspectDiagnostics);
diff --git a/src/test/java/com/android/tools/r8/startup/dump/DumpStartupProfileProvidersTest.java b/src/test/java/com/android/tools/r8/startup/dump/DumpStartupProfileProvidersTest.java
new file mode 100644
index 0000000..f2cb975
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/startup/dump/DumpStartupProfileProvidersTest.java
@@ -0,0 +1,127 @@
+// 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.startup.dump;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.startup.StartupProfileBuilder;
+import com.android.tools.r8.startup.StartupProfileProvider;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DumpInputFlags;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DumpStartupProfileProvidersTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withNoneRuntime().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    Path dump = temp.newFile("dump.zip").toPath();
+    try {
+      testForR8(Backend.DEX)
+          .addProgramClasses(Main.class)
+          .addKeepMainRule(Main.class)
+          .addOptionsModification(
+              options -> options.setDumpInputFlags(DumpInputFlags.dumpToFile(dump)))
+          .addStartupProfileProviders(getStartupProfileProviders())
+          .allowDiagnosticErrorMessages()
+          .setMinApi(AndroidApiLevel.LATEST)
+          .compileWithExpectedDiagnostics(
+              diagnostics ->
+                  diagnostics.assertErrorsMatch(
+                      diagnosticMessage(containsString("Dumped compilation inputs to:"))));
+      fail("Expected compilation to fail");
+    } catch (CompilationFailedException e) {
+      // Expected.
+    }
+    verifyDump(dump);
+  }
+
+  private Collection<StartupProfileProvider> getStartupProfileProviders() {
+    return ImmutableList.of(
+        new StartupProfileProvider() {
+
+          @Override
+          public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+            startupProfileBuilder.addStartupClass(
+                startupClassBuilder ->
+                    startupClassBuilder.setClassReference(Reference.classFromClass(Main.class)));
+          }
+
+          @Override
+          public Origin getOrigin() {
+            return Origin.unknown();
+          }
+        },
+        new StartupProfileProvider() {
+
+          @Override
+          public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+            startupProfileBuilder.addStartupMethod(
+                startupMethodBuilder ->
+                    startupMethodBuilder.setMethodReference(
+                        MethodReferenceUtils.mainMethod(Main.class)));
+          }
+
+          @Override
+          public Origin getOrigin() {
+            return Origin.unknown();
+          }
+        });
+  }
+
+  private void verifyDump(Path dump) throws IOException {
+    assertTrue(Files.exists(dump));
+    Path unzipped = temp.newFolder().toPath();
+    ZipUtils.unzip(dump.toString(), unzipped.toFile());
+
+    Path startupProfile1 = unzipped.resolve("startup-profile-1.txt");
+    assertTrue(Files.exists(startupProfile1));
+    assertEquals(
+        Lists.newArrayList(Reference.classFromClass(Main.class).getDescriptor()),
+        FileUtils.readAllLines(startupProfile1));
+
+    Path startupProfile2 = unzipped.resolve("startup-profile-2.txt");
+    assertTrue(Files.exists(startupProfile2));
+    assertEquals(
+        Lists.newArrayList(
+            MethodReferenceUtils.toSmaliString(MethodReferenceUtils.mainMethod(Main.class))),
+        FileUtils.readAllLines(startupProfile2));
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {}
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
index d81aadf..7007c61 100644
--- a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
@@ -42,7 +42,6 @@
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.function.Consumer;
 import org.junit.rules.TemporaryFolder;
 
@@ -190,41 +189,40 @@
   public static void setStartupConfiguration(
       TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder,
       Collection<ExternalStartupItem> startupItems) {
-    testBuilder.addOptionsModification(
-        options -> {
-          StartupProfileProvider startupProfileProvider =
-              new StartupProfileProvider() {
-                @Override
-                public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
-                  for (ExternalStartupItem startupItem : startupItems) {
-                    startupItem.apply(
-                        startupClass ->
-                            startupProfileBuilder.addStartupClass(
-                                startupClassBuilder ->
-                                    startupClassBuilder.setClassReference(
-                                        startupClass.getReference())),
-                        startupMethod ->
-                            startupProfileBuilder.addStartupMethod(
-                                startupMethodBuilder ->
-                                    startupMethodBuilder.setMethodReference(
-                                        startupMethod.getReference())),
-                        syntheticStartupMethod ->
-                            startupProfileBuilder.addSyntheticStartupMethod(
-                                syntheticStartupMethodBuilder ->
-                                    syntheticStartupMethodBuilder.setSyntheticContextReference(
-                                        syntheticStartupMethod.getSyntheticContextReference())));
-                  }
-                }
+    StartupProfileProvider startupProfileProvider =
+        new StartupProfileProvider() {
+          @Override
+          public void getStartupProfile(StartupProfileBuilder startupProfileBuilder) {
+            for (ExternalStartupItem startupItem : startupItems) {
+              startupItem.apply(
+                  startupClass ->
+                      startupProfileBuilder.addStartupClass(
+                          startupClassBuilder ->
+                              startupClassBuilder.setClassReference(startupClass.getReference())),
+                  startupMethod ->
+                      startupProfileBuilder.addStartupMethod(
+                          startupMethodBuilder ->
+                              startupMethodBuilder.setMethodReference(
+                                  startupMethod.getReference())),
+                  syntheticStartupMethod ->
+                      startupProfileBuilder.addSyntheticStartupMethod(
+                          syntheticStartupMethodBuilder ->
+                              syntheticStartupMethodBuilder.setSyntheticContextReference(
+                                  syntheticStartupMethod.getSyntheticContextReference())));
+            }
+          }
 
-                @Override
-                public Origin getOrigin() {
-                  return Origin.unknown();
-                }
-              };
-          options
-              .getStartupOptions()
-              .setStartupProfileProviders(Collections.singleton(startupProfileProvider));
-        });
+          @Override
+          public Origin getOrigin() {
+            return Origin.unknown();
+          }
+        };
+    if (testBuilder.isD8TestBuilder()) {
+      testBuilder.asD8TestBuilder().addStartupProfileProviders(startupProfileProvider);
+    } else {
+      assertTrue(testBuilder.isR8TestBuilder());
+      testBuilder.asR8TestBuilder().addStartupProfileProviders(startupProfileProvider);
+    }
   }
 
   private static byte[] getTransformedAndroidUtilLog() throws IOException {