Snap for 7132927 from df3091a0e5bad5d41e6dad8653298ec5910af7b7 to mainline-documentsui-release

Change-Id: Ic40c672bba048af2a3bc66eb3595865c87a66727
diff --git a/src/com/android/tradefed/util/NativeCodeCoverageFlusher.java b/src/com/android/tradefed/util/NativeCodeCoverageFlusher.java
index 151ea6b..c755d7f 100644
--- a/src/com/android/tradefed/util/NativeCodeCoverageFlusher.java
+++ b/src/com/android/tradefed/util/NativeCodeCoverageFlusher.java
@@ -22,6 +22,7 @@
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
 
+import com.google.common.collect.ImmutableList;
 import java.util.List;
 import java.util.StringJoiner;
 
@@ -31,6 +32,9 @@
  */
 public final class NativeCodeCoverageFlusher {
 
+    private static final String EXTRACT_SIGCGT_FORMAT =
+            "cat /proc/%d/status | grep SigCgt | awk '{ print $2 }'";
+    private static final long SIGNAL_37_BIT = 1 << (37 - 1);
     private static final String COVERAGE_FLUSH_COMMAND_FORMAT = "kill -37 %s";
     private static final String CLEAR_CLANG_COVERAGE_FILES =
             "find /data/misc/trace -name '*.profraw' -delete";
@@ -66,28 +70,59 @@
     public void forceCoverageFlush() throws DeviceNotAvailableException {
         checkState(mDevice.isAdbRoot(), "adb root is required to flush native coverage data.");
 
-        if ((mProcessNames == null) || mProcessNames.isEmpty()) {
-            // Use the special pid -1 to trigger a coverage flush of all running processes.
-            mDevice.executeShellCommand(String.format(COVERAGE_FLUSH_COMMAND_FORMAT, "-1"));
-        } else {
-            // Look up the pid of the processes to send them the coverage flush signal.
-            StringJoiner pidString = new StringJoiner(" ");
-            for (String processName : mProcessNames) {
-                String pid = mDevice.getProcessPid(processName);
-                if (pid == null) {
-                    CLog.w("Did not find pid for process \"%s\".", processName);
-                } else {
-                    pidString.add(pid);
-                }
-            }
+        List<Integer> signalHandlingPids = findSignalHandlingPids(mProcessNames);
+        StringJoiner pidString = new StringJoiner(" ");
 
-            if (pidString.length() > 0) {
-                mDevice.executeShellCommand(
-                        String.format(COVERAGE_FLUSH_COMMAND_FORMAT, pidString.toString()));
-            }
+        CLog.d("Signal handling pids: %s", signalHandlingPids.toString());
+
+        for (Integer pid : signalHandlingPids) {
+            pidString.add(pid.toString());
+        }
+
+        if (pidString.length() > 0) {
+            mDevice.executeShellCommand(
+                    String.format(COVERAGE_FLUSH_COMMAND_FORMAT, pidString.toString()));
         }
 
         // Wait up to 5 minutes for the device to be available after flushing coverage data.
         mDevice.waitForDeviceAvailable(5 * 60 * 1000);
     }
+
+    /** Finds processes that handle the native coverage flush signal (37). */
+    private List<Integer> findSignalHandlingPids(List<String> processNames)
+            throws DeviceNotAvailableException {
+        // Get a list of all running pids.
+        List<ProcessInfo> allProcessInfo =
+                PsParser.getProcesses(mDevice.executeShellCommand("ps -e"));
+        ImmutableList.Builder<Integer> signalHandlingPids = ImmutableList.builder();
+
+        // Check SigCgt from /proc/<pid>/status to see if the bit for signal 37 is set.
+        for (ProcessInfo processInfo : allProcessInfo) {
+            CommandResult result =
+                    mDevice.executeShellV2Command(
+                            String.format(EXTRACT_SIGCGT_FORMAT, processInfo.getPid()));
+
+            if (!result.getStatus().equals(CommandStatus.SUCCESS) || (result.getExitCode() != 0)) {
+                CLog.w(
+                        "Failed to read /proc/%d/status for %s",
+                        processInfo.getPid(), processInfo.getName());
+            } else if (result.getStdout().trim().isEmpty()) {
+                CLog.w(
+                        "Empty string when retrieving SigCgt for %s (pid %d)",
+                        processInfo.getName(), processInfo.getPid());
+            } else {
+                long sigCgt = Long.parseLong(result.getStdout().trim(), 16);
+
+                // Check the signal bit is set and either no processes are set, or this specific
+                // process is in the process list.
+                if ((sigCgt & SIGNAL_37_BIT) == SIGNAL_37_BIT
+                        && (processNames.isEmpty()
+                                || processNames.contains(processInfo.getName()))) {
+                    signalHandlingPids.add(processInfo.getPid());
+                }
+            }
+        }
+
+        return signalHandlingPids.build();
+    }
 }
diff --git a/tests/src/com/android/tradefed/device/metric/ClangCodeCoverageCollectorTest.java b/tests/src/com/android/tradefed/device/metric/ClangCodeCoverageCollectorTest.java
index c7545a2..f355326 100644
--- a/tests/src/com/android/tradefed/device/metric/ClangCodeCoverageCollectorTest.java
+++ b/tests/src/com/android/tradefed/device/metric/ClangCodeCoverageCollectorTest.java
@@ -89,6 +89,10 @@
     private static final int TEST_COUNT = 5;
     private static final long ELAPSED_TIME = 1000;
 
+    private static final String PS_OUTPUT =
+            "USER       PID   PPID  VSZ   RSS   WCHAN       PC  S NAME\n"
+                    + "shell       123  1366  123    456   SyS_epoll+   0  S adbd\n";
+
     @Rule public TemporaryFolder folder = new TemporaryFolder();
 
     private HashMap<String, MetricMeasurement.Metric> mMetrics;
@@ -127,6 +131,13 @@
 
         doReturn(ImmutableList.of(mMockDevice)).when(mMockContext).getDevices();
 
+        doReturn(PS_OUTPUT).when(mMockDevice).executeShellCommand("ps -e");
+
+        CommandResult result = new CommandResult(CommandStatus.SUCCESS);
+        result.setStdout("ffffffffff\n");
+        result.setExitCode(0);
+        when(mMockDevice.executeShellV2Command(anyString())).thenReturn(result);
+
         mListener = new ClangCodeCoverageCollector();
         mListener.setConfiguration(mMockConfiguration);
         mListener.setRunUtil(mCommandArgumentCaptor);
@@ -175,9 +186,9 @@
         mListener.testRunEnded(ELAPSED_TIME, mMetrics);
         mListener.invocationEnded(ELAPSED_TIME);
 
-        // Verify the flush-all-coverage command was called twice - once on init() and once during
+        // Verify the flush-coverage command was called twice - once on init() and once during
         // the end of the test run.
-        verify(mMockDevice, times(2)).executeShellCommand("kill -37 -1");
+        verify(mMockDevice, times(2)).executeShellCommand("kill -37 123");
     }
 
     @Test
@@ -383,7 +394,9 @@
         InOrder inOrder = Mockito.inOrder(mMockDevice);
         inOrder.verify(mMockDevice).isAdbRoot();
         inOrder.verify(mMockDevice).enableAdbRoot();
-        inOrder.verify(mMockDevice).executeShellCommand("kill -37 -1");
+        inOrder.verify(mMockDevice).executeShellCommand("ps -e");
+        inOrder.verify(mMockDevice).executeShellV2Command(anyString());
+        inOrder.verify(mMockDevice).executeShellCommand("kill -37 123");
         inOrder.verify(mMockDevice, times(2)).executeShellCommand(anyString());
         inOrder.verify(mMockDevice).disableAdbRoot();
     }
diff --git a/tests/src/com/android/tradefed/device/metric/GcovCodeCoverageCollectorTest.java b/tests/src/com/android/tradefed/device/metric/GcovCodeCoverageCollectorTest.java
index 4dc8562..66159e2 100644
--- a/tests/src/com/android/tradefed/device/metric/GcovCodeCoverageCollectorTest.java
+++ b/tests/src/com/android/tradefed/device/metric/GcovCodeCoverageCollectorTest.java
@@ -34,6 +34,8 @@
 import com.android.tradefed.result.LogDataType;
 import com.android.tradefed.testtype.coverage.CoverageOptions;
 import com.android.tradefed.util.proto.TfMetricProtoUtil;
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -76,6 +78,10 @@
     private static final int TEST_COUNT = 5;
     private static final long ELAPSED_TIME = 1000;
 
+    private static final String PS_OUTPUT =
+            "USER       PID   PPID  VSZ   RSS   WCHAN       PC  S NAME\n"
+                    + "shell       123  1366  123    456   SyS_epoll+   0  S adbd\n";
+
     @Rule public TemporaryFolder folder = new TemporaryFolder();
 
     @Mock IConfiguration mMockConfiguration;
@@ -93,7 +99,7 @@
     GcovCodeCoverageCollector mCodeCoverageListener;
 
     @Before
-    public void setUp() throws ConfigurationException {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         mCoverageOptions = new CoverageOptions();
@@ -102,6 +108,13 @@
         doReturn(mCoverageOptions).when(mMockConfiguration).getCoverageOptions();
         doReturn(ImmutableList.of(mMockDevice)).when(mMockContext).getDevices();
 
+        doReturn(PS_OUTPUT).when(mMockDevice).executeShellCommand("ps -e");
+
+        CommandResult result = new CommandResult(CommandStatus.SUCCESS);
+        result.setStdout("ffffffffffff\n");
+        result.setExitCode(0);
+        when(mMockDevice.executeShellV2Command(anyString())).thenReturn(result);
+
         mCodeCoverageListener = new GcovCodeCoverageCollector();
         mCodeCoverageListener.setConfiguration(mMockConfiguration);
     }
@@ -185,9 +198,9 @@
         Map<String, String> metric = new HashMap<>();
         mCodeCoverageListener.testRunEnded(ELAPSED_TIME, TfMetricProtoUtil.upgradeConvert(metric));
 
-        // Verify the flush-all-coverage command was called twice - once on init(...) and once
+        // Verify the flush-coverage command was called twice - once on init(...) and once
         // on test run end.
-        verify(mMockDevice, times(2)).executeShellCommand("kill -37 -1");
+        verify(mMockDevice, times(2)).executeShellCommand("kill -37 123");
     }
 
     @Test
@@ -198,8 +211,6 @@
         mCoverageOptionsSetter.setOptionValue("coverage-processes", "adbd");
 
         doReturn(true).when(mMockDevice).isAdbRoot();
-        doReturn("123").when(mMockDevice).getProcessPid("mediaserver");
-        doReturn("56789").when(mMockDevice).getProcessPid("adbd");
         doReturn(createTar(ImmutableMap.of())).when(mMockDevice).pullFile(anyString());
 
         // Simulate a test run.
@@ -210,7 +221,7 @@
 
         // Verify the flush-coverage command was called with the specific pids twice - once on
         // init(...) and once on test run end.
-        verify(mMockDevice, times(2)).executeShellCommand("kill -37 123 56789");
+        verify(mMockDevice, times(2)).executeShellCommand("kill -37 123");
     }
 
     @Test
@@ -289,7 +300,7 @@
         InOrder inOrder = Mockito.inOrder(mMockDevice);
         inOrder.verify(mMockDevice).isAdbRoot();
         inOrder.verify(mMockDevice).enableAdbRoot();
-        inOrder.verify(mMockDevice).executeShellCommand("kill -37 -1");
+        inOrder.verify(mMockDevice).executeShellCommand("kill -37 123");
         inOrder.verify(mMockDevice, times(2)).executeShellCommand(anyString());
         inOrder.verify(mMockDevice).disableAdbRoot();
     }
diff --git a/tests/src/com/android/tradefed/util/NativeCodeCoverageFlusherTest.java b/tests/src/com/android/tradefed/util/NativeCodeCoverageFlusherTest.java
index 481b0ef..6c2ff47 100644
--- a/tests/src/com/android/tradefed/util/NativeCodeCoverageFlusherTest.java
+++ b/tests/src/com/android/tradefed/util/NativeCodeCoverageFlusherTest.java
@@ -18,9 +18,11 @@
 
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.contains;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
@@ -39,20 +41,32 @@
 @RunWith(JUnit4.class)
 public final class NativeCodeCoverageFlusherTest {
 
+    private static final String PS_OUTPUT =
+            "USER       PID   PPID  VSZ   RSS   WCHAN       PC  S NAME\n"
+                    + "shell       123  1366  123    456   SyS_epoll+   0  S adbd\n"
+                    + "root        234     1 7890   123   binder_io+   0  S logcat\n"
+                    + "root        456  1234  567   890   binder_io+   0  S media.swcodec\n";
     @Mock ITestDevice mMockDevice;
 
     // Object under test
     NativeCodeCoverageFlusher mFlusher;
 
     @Before
-    public void setUp() {
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        doReturn(PS_OUTPUT).when(mMockDevice).executeShellCommand("ps -e");
     }
 
     @Test
     public void testClearCoverageMeasurements_rmCommandCalled() throws DeviceNotAvailableException {
         doReturn(true).when(mMockDevice).isAdbRoot();
 
+        CommandResult result = new CommandResult(CommandStatus.SUCCESS);
+        result.setStdout("ffffffffffff\n");
+        result.setExitCode(0);
+
+        when(mMockDevice.executeShellV2Command(anyString())).thenReturn(result);
+
         mFlusher = new NativeCodeCoverageFlusher(mMockDevice, ImmutableList.of());
         mFlusher.resetCoverage();
 
@@ -82,27 +96,120 @@
             throws DeviceNotAvailableException {
         doReturn(true).when(mMockDevice).isAdbRoot();
 
+        CommandResult result = new CommandResult(CommandStatus.SUCCESS);
+        result.setStdout("ffffffffffff\n");
+        result.setExitCode(0);
+
+        when(mMockDevice.executeShellV2Command(anyString())).thenReturn(result);
+
         mFlusher = new NativeCodeCoverageFlusher(mMockDevice, ImmutableList.of());
         mFlusher.forceCoverageFlush();
 
-        // Verify that the flush command for all processes was called.
-        verify(mMockDevice).executeShellCommand("kill -37 -1");
+        // Verify that the flush command for all individual processes was called.
+        verify(mMockDevice).executeShellCommand("kill -37 123 234 456");
     }
 
     @Test
     public void testFlushCoverageSpecificProcesses_flushSpecificCommandCalled()
             throws DeviceNotAvailableException {
-        List<String> processes = ImmutableList.of("mediaserver", "mediaextractor");
+        List<String> processes = ImmutableList.of("adbd", "logcat");
 
         doReturn(true).when(mMockDevice).isAdbRoot();
-        doReturn("12").when(mMockDevice).getProcessPid(processes.get(0));
-        doReturn("789").when(mMockDevice).getProcessPid(processes.get(1));
+
+        CommandResult result = new CommandResult(CommandStatus.SUCCESS);
+        result.setStdout("ffffffffffff\n");
+        result.setExitCode(0);
+
+        when(mMockDevice.executeShellV2Command(anyString())).thenReturn(result);
 
         mFlusher = new NativeCodeCoverageFlusher(mMockDevice, processes);
         mFlusher.forceCoverageFlush();
 
         // Verify that the flush command for the specific processes was called.
-        verify(mMockDevice).executeShellCommand("kill -37 12 789");
+        verify(mMockDevice).executeShellCommand("kill -37 123 234");
+    }
+
+    @Test
+    public void testFlushNotHandled_flushNotCalled() throws DeviceNotAvailableException {
+        List<String> processes = ImmutableList.of("adbd");
+
+        doReturn(true).when(mMockDevice).isAdbRoot();
+
+        CommandResult result = new CommandResult(CommandStatus.SUCCESS);
+        result.setStdout("0000000000\n");
+        result.setExitCode(0);
+
+        when(mMockDevice.executeShellV2Command(anyString())).thenReturn(result);
+
+        mFlusher = new NativeCodeCoverageFlusher(mMockDevice, processes);
+        mFlusher.forceCoverageFlush();
+
+        // Verify that the flush command was not called.
+        verify(mMockDevice, never()).executeShellCommand("kill -37 123");
+    }
+
+    @Test
+    public void testFlushStatusReadFailed_flushNotCalled() throws DeviceNotAvailableException {
+        List<String> processes = ImmutableList.of("adbd");
+
+        doReturn(true).when(mMockDevice).isAdbRoot();
+
+        CommandResult result = new CommandResult(CommandStatus.SUCCESS);
+        result.setExitCode(-1);
+
+        when(mMockDevice.executeShellV2Command(anyString())).thenReturn(result);
+
+        mFlusher = new NativeCodeCoverageFlusher(mMockDevice, processes);
+        mFlusher.forceCoverageFlush();
+
+        // Verify that the flush command was not called.
+        verify(mMockDevice, never()).executeShellCommand("kill -37 123");
+    }
+
+    @Test
+    public void testFlushStatusReadEmpty_flushNotCalled() throws DeviceNotAvailableException {
+        List<String> processes = ImmutableList.of("adbd");
+
+        doReturn(true).when(mMockDevice).isAdbRoot();
+
+        CommandResult result = new CommandResult(CommandStatus.SUCCESS);
+        result.setStdout("");
+        result.setExitCode(0);
+
+        when(mMockDevice.executeShellV2Command(anyString())).thenReturn(result);
+
+        mFlusher = new NativeCodeCoverageFlusher(mMockDevice, processes);
+        mFlusher.forceCoverageFlush();
+
+        // Verify that the flush command was not called.
+        verify(mMockDevice, never()).executeShellCommand("kill -37 123");
+    }
+
+    @Test
+    public void testFlushOnlySigCgt_flushSpecificProcesses() throws DeviceNotAvailableException {
+        doReturn(true).when(mMockDevice).isAdbRoot();
+
+        CommandResult resultNotHandled = new CommandResult(CommandStatus.SUCCESS);
+        resultNotHandled.setStdout("0000000000\n");
+        resultNotHandled.setExitCode(0);
+
+        CommandResult resultHandled = new CommandResult(CommandStatus.SUCCESS);
+        resultHandled.setStdout("ffffffffffff\n");
+        resultHandled.setExitCode(0);
+
+        CommandResult resultEmpty = new CommandResult(CommandStatus.SUCCESS);
+        resultEmpty.setStdout("\n");
+        resultEmpty.setExitCode(0);
+
+        when(mMockDevice.executeShellV2Command(contains("123"))).thenReturn(resultNotHandled);
+        when(mMockDevice.executeShellV2Command(contains("234"))).thenReturn(resultHandled);
+        when(mMockDevice.executeShellV2Command(contains("456"))).thenReturn(resultEmpty);
+
+        mFlusher = new NativeCodeCoverageFlusher(mMockDevice, ImmutableList.of());
+        mFlusher.forceCoverageFlush();
+
+        // Verify that the flush command was only called for pid 234.
+        verify(mMockDevice).executeShellCommand("kill -37 234");
     }
 
     @Test