Merge "Dump imgdiag data for persistent processes" into main
diff --git a/test_scripts/src/main/java/com/android/art/targetprep/AllProcessesImgdiag.java b/test_scripts/src/main/java/com/android/art/targetprep/AllProcessesImgdiag.java
new file mode 100644
index 0000000..d0a9b29
--- /dev/null
+++ b/test_scripts/src/main/java/com/android/art/targetprep/AllProcessesImgdiag.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.art.targetprep;
+
+import com.android.art.tests.AppLaunchImgdiagTest;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.log.ITestLogger;
+import com.android.tradefed.result.FileInputStreamSource;
+import com.android.tradefed.result.ITestLoggerReceiver;
+import com.android.tradefed.result.LogDataType;
+import com.android.tradefed.targetprep.ITargetPreparer;
+
+import org.junit.Assert;
+
+import java.io.File;
+
+/** Collect imgdiag data from all zygote children at the end of the run. */
+public class AllProcessesImgdiag implements ITestLoggerReceiver, ITargetPreparer {
+    @Option(
+            name = "imgdiag-out-path",
+            description = "Path to directory containing imgdiag output files.")
+    private String mImgdiagOutPath;
+
+    private ITestLogger mTestLogger;
+
+    @Override
+    public void setTestLogger(ITestLogger testLogger) {
+        mTestLogger = testLogger;
+    }
+
+    @Override
+    public void setUp(TestInformation testInformation) {}
+
+    @Override
+    public void tearDown(TestInformation testInformation, Throwable e)
+            throws DeviceNotAvailableException {
+        Assert.assertTrue(testInformation.getDevice().doesFileExist(mImgdiagOutPath));
+
+        String zygoteChildren =
+                testInformation
+                        .getDevice()
+                        .executeShellCommand("ps --ppid `pidof zygote64` -o pid,args");
+
+        // Skip "PID ARGS" header.
+        for (String line : zygoteChildren.lines().skip(1).toList()) {
+            String[] vals = line.strip().split("\\s+");
+            Assert.assertEquals(2, vals.length);
+
+            String targetPid = vals[0];
+            String targetName = vals[1];
+
+            String outFileName = String.format("imgdiag_%s_%s.txt", targetName, targetPid);
+            String outFilePath = new File(mImgdiagOutPath, outFileName).getAbsolutePath();
+
+            String imgdiagCmd = AppLaunchImgdiagTest.getImgdiagRunCmd(targetPid, outFilePath);
+            testInformation.getDevice().executeShellCommand(imgdiagCmd);
+
+            File imgdiagFile = testInformation.getDevice().pullFile(outFilePath);
+            mTestLogger.testLog(
+                    outFileName, LogDataType.HOST_LOG, new FileInputStreamSource(imgdiagFile));
+        }
+    }
+}
diff --git a/test_scripts/src/main/java/com/android/art/tests/AppLaunchImgdiagTest.java b/test_scripts/src/main/java/com/android/art/tests/AppLaunchImgdiagTest.java
index 853bf1e..08b9344 100644
--- a/test_scripts/src/main/java/com/android/art/tests/AppLaunchImgdiagTest.java
+++ b/test_scripts/src/main/java/com/android/art/tests/AppLaunchImgdiagTest.java
@@ -48,18 +48,13 @@
         String outFileName = String.format("imgdiag_%s_%s.txt", mPackageName, targetPid);
         String outFilePath = new File(mImgdiagOutPath, outFileName).getAbsolutePath();
 
-        String imgdiagCmd =
-                String.format(
-                        "imgdiag --zygote-diff-pid=`pidof zygote64` --image-diff-pid=%2$s"
-                                + " --output="
-                                + outFilePath
-                                + " --dump-dirty-objects --boot-image="
-                                + "/data/misc/apexdata/com.android.art/dalvik-cache/boot.art",
-                        mPackageName,
-                        targetPid);
+        String imgdiagCmd = getImgdiagRunCmd(targetPid, outFilePath);
         CommandResult res = getDevice().executeShellV2Command(imgdiagCmd);
         Assert.assertEquals(
-                "Failed to run imgdiag. " + res.toString(), CommandStatus.SUCCESS, res.getStatus());
+                String.format(
+                        "Failed to run imgdiag:\n%s\nResult:\n%s", imgdiagCmd, res.toString()),
+                CommandStatus.SUCCESS,
+                res.getStatus());
 
         File imgdiagFile = getDevice().pullFile(outFilePath);
         TestUtils testUtils = TestUtils.getInstance(getTestInformation(), mLogData);
@@ -69,4 +64,21 @@
 
         super.tearDown();
     }
+
+    /**
+     * Constructs the command line string to run the `imgdiag` tool for gathering dirty image
+     * objects.
+     *
+     * @param targetPid The process ID of the target process to analyze.
+     * @param outFilePath The absolute file path on the device where the imgdiag data will be saved.
+     * @return The complete `imgdiag` command that can be run with `adb shell`.
+     */
+    public static String getImgdiagRunCmd(String targetPid, String outFilePath) {
+        return String.format(
+                "imgdiag --zygote-diff-pid=`pidof zygote64` --image-diff-pid=%s"
+                        + " --output=%s"
+                        + " --dump-dirty-objects --boot-image="
+                        + "/data/misc/apexdata/com.android.art/dalvik-cache/boot.art",
+                targetPid, outFilePath);
+    }
 }
diff --git a/test_targets/imgdiag-app-launch/plan.xml b/test_targets/imgdiag-app-launch/plan.xml
index 9f7158b..163c79a 100644
--- a/test_targets/imgdiag-app-launch/plan.xml
+++ b/test_targets/imgdiag-app-launch/plan.xml
@@ -18,6 +18,9 @@
     <option name="run-command" value="mkdir /data/local/tmp/imgdiag_out/"/>
     <option name="teardown-command" value="rm -r /data/local/tmp/imgdiag_out/"/>
   </target_preparer>
+  <target_preparer class="com.android.art.targetprep.AllProcessesImgdiag">
+    <option name="imgdiag-out-path" value="/data/local/tmp/imgdiag_out/"/>
+  </target_preparer>
   <target_preparer class="com.android.art.targetprep.AggregateImgdiagOutput">
     <option name="imgdiag-out-path" value="/data/local/tmp/imgdiag_out/"/>
   </target_preparer>