diff options
author | Oliver Nguyen <olivernguyen@google.com> | 2019-07-02 15:48:43 -0700 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-07-02 15:48:43 -0700 |
commit | f445f4dc3cd65a6064260b2c79ca622bcd2ef875 (patch) | |
tree | ba88d3fff73ff9d326093f3605bd1df84190e424 | |
parent | 3fa684e12962bbdddf2766d63cb01e37219b2a10 (diff) | |
parent | 98826bcbc670b2e03492f8e6991db46660fdcb76 (diff) | |
download | tradefederation-f445f4dc3cd65a6064260b2c79ca622bcd2ef875.tar.gz |
Merge "Add utility class to handle clearing and flushing native coverage data." into qt-dev
am: 98826bcbc6
Change-Id: Ic156f767b3010f2cba6aba9d49ce88cc0a90de2c
3 files changed, 208 insertions, 0 deletions
diff --git a/src/com/android/tradefed/util/NativeCodeCoverageFlusher.java b/src/com/android/tradefed/util/NativeCodeCoverageFlusher.java new file mode 100644 index 000000000..b53542a9e --- /dev/null +++ b/src/com/android/tradefed/util/NativeCodeCoverageFlusher.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 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.tradefed.util; + +import static com.google.common.base.Preconditions.checkState; + +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.log.LogUtil.CLog; + +import java.util.List; +import java.util.StringJoiner; + +/** + * A utility class that clears native coverage measurements and forces a flush of native coverage + * data from processes on the device. + */ +public final class NativeCodeCoverageFlusher { + + private static final String COVERAGE_FLUSH_COMMAND_FORMAT = "kill -37 %s"; + private static final String CLEAR_NATIVE_COVERAGE_FILES = "rm -rf /data/misc/trace/*"; + + private final ITestDevice mDevice; + + public NativeCodeCoverageFlusher(ITestDevice device) { + mDevice = device; + } + + /** + * Clears coverage measurements from disk on the device. Device must be in adb root. + * + * @throws DeviceNotAvailableException + */ + public void clearCoverageMeasurements() throws DeviceNotAvailableException { + checkState(mDevice.isAdbRoot(), "adb root is required to clear coverage files."); + mDevice.executeShellCommand(CLEAR_NATIVE_COVERAGE_FILES); + } + + /** + * Forces a flush of native coverage data from processes running on the device. Device must be + * in adb root. + * + * @param processNames the name of processes to target for flushing; if empty, flushes from all + * running native processes on the device. + * @throws DeviceNotAvailableException + */ + public void forceCoverageFlush(List<String> processNames) throws DeviceNotAvailableException { + checkState(mDevice.isAdbRoot(), "adb root is required to flush native coverage data."); + + if ((processNames == null) || processNames.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 : processNames) { + String pid = mDevice.getProcessPid(processName); + if (pid == null) { + CLog.w("Did not find pid for process \"%s\".", processName); + } else { + pidString.add(pid); + } + } + + if (pidString.length() > 0) { + mDevice.executeShellCommand( + String.format(COVERAGE_FLUSH_COMMAND_FORMAT, pidString.toString())); + } + } + } +} diff --git a/tests/src/com/android/tradefed/UnitTests.java b/tests/src/com/android/tradefed/UnitTests.java index 422933f78..2b855a306 100644 --- a/tests/src/com/android/tradefed/UnitTests.java +++ b/tests/src/com/android/tradefed/UnitTests.java @@ -308,6 +308,7 @@ import com.android.tradefed.util.ListInstrumentationParserTest; import com.android.tradefed.util.LocalRunInstructionBuilderTest; import com.android.tradefed.util.LogcatEventParserTest; import com.android.tradefed.util.MultiMapTest; +import com.android.tradefed.util.NativeCodeCoverageFlusherTest; import com.android.tradefed.util.NullUtilTest; import com.android.tradefed.util.PairTest; import com.android.tradefed.util.PropertyChangerTest; @@ -747,6 +748,7 @@ import org.junit.runners.Suite.SuiteClasses; ListInstrumentationParserTest.class, LogcatEventParserTest.class, MultiMapTest.class, + NativeCodeCoverageFlusherTest.class, NullUtilTest.class, PairTest.class, PsParserTest.class, diff --git a/tests/src/com/android/tradefed/util/NativeCodeCoverageFlusherTest.java b/tests/src/com/android/tradefed/util/NativeCodeCoverageFlusherTest.java new file mode 100644 index 000000000..523655c85 --- /dev/null +++ b/tests/src/com/android/tradefed/util/NativeCodeCoverageFlusherTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2019 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.tradefed.util; + +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; + +import com.google.common.collect.ImmutableList; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.lang.IllegalStateException; +import java.util.List; + +@RunWith(JUnit4.class) +public final class NativeCodeCoverageFlusherTest { + + @Mock ITestDevice mMockDevice; + + // Object under test + NativeCodeCoverageFlusher mFlusher; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mFlusher = new NativeCodeCoverageFlusher(mMockDevice); + } + + @Test + public void testClearCoverageMeasurements_rmCommandCalled() throws DeviceNotAvailableException { + doReturn(true).when(mMockDevice).isAdbRoot(); + + mFlusher.clearCoverageMeasurements(); + + // Verify that the rm command was executed. + verify(mMockDevice).executeShellCommand("rm -rf /data/misc/trace/*"); + } + + @Test + public void testNoAdbRootClearCoverageMeasurements_noOp() throws DeviceNotAvailableException { + doReturn(false).when(mMockDevice).isAdbRoot(); + + try { + mFlusher.clearCoverageMeasurements(); + fail("Should have thrown an exception"); + } catch (IllegalStateException e) { + // Expected + } + + // Verify that no shell command was executed. + verify(mMockDevice, never()).executeShellCommand(anyString()); + } + + @Test + public void testFlushCoverageAllProcesses_flushAllCommandCalled() + throws DeviceNotAvailableException { + doReturn(true).when(mMockDevice).isAdbRoot(); + + mFlusher.forceCoverageFlush(ImmutableList.of()); + + // Verify that the flush command for all processes was called. + verify(mMockDevice).executeShellCommand("kill -37 -1"); + } + + @Test + public void testFlushCoverageSpecificProcesses_flushSpecificCommandCalled() + throws DeviceNotAvailableException { + List<String> processes = ImmutableList.of("mediaserver", "mediaextractor"); + + doReturn(true).when(mMockDevice).isAdbRoot(); + doReturn("12").when(mMockDevice).getProcessPid(processes.get(0)); + doReturn("789").when(mMockDevice).getProcessPid(processes.get(1)); + + mFlusher.forceCoverageFlush(processes); + + // Verify that the flush command for the specific processes was called. + verify(mMockDevice).executeShellCommand("kill -37 12 789"); + } + + @Test + public void testNoAdbRootFlush_noOp() throws DeviceNotAvailableException { + doReturn(false).when(mMockDevice).isAdbRoot(); + + try { + mFlusher.forceCoverageFlush(ImmutableList.of("mediaserver")); + fail("Should have thrown an exception"); + } catch (IllegalStateException e) { + // Expected + } + + // Verify no shell commands or pid lookups were executed. + verify(mMockDevice, never()).executeShellCommand(anyString()); + verify(mMockDevice, never()).getProcessPid(anyString()); + } +} |