aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2017-06-23 07:09:36 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2017-06-23 07:09:36 +0000
commitdeb028cdc52bae937841cf95866d1812c7f35328 (patch)
treec69ce3c488d83c8f9df742f2403d4b06b1a32903
parent6d89ea629c4820229cc3c50762bdb5603c365088 (diff)
parent9cd6c25090e5d949325292a88b77f30049987271 (diff)
downloadtradefederation-oreo-r3-release.tar.gz
Change-Id: Iab70b7a9fa12e08062e41550abb59fbaa2702e80
-rw-r--r--src/com/android/tradefed/testtype/TfTestLauncher.java48
-rw-r--r--src/com/android/tradefed/util/HprofAllocSiteParser.java98
-rw-r--r--tests/src/com/android/tradefed/UnitTests.java2
-rw-r--r--tests/src/com/android/tradefed/util/HprofAllocSiteParserTest.java107
4 files changed, 248 insertions, 7 deletions
diff --git a/src/com/android/tradefed/testtype/TfTestLauncher.java b/src/com/android/tradefed/testtype/TfTestLauncher.java
index c5cc4eca7..87aae1f17 100644
--- a/src/com/android/tradefed/testtype/TfTestLauncher.java
+++ b/src/com/android/tradefed/testtype/TfTestLauncher.java
@@ -26,6 +26,7 @@ import com.android.tradefed.result.LogDataType;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.HprofAllocSiteParser;
import com.android.tradefed.util.RunUtil;
import com.android.tradefed.util.StreamUtil;
@@ -209,13 +210,7 @@ public class TfTestLauncher extends SubprocessTfLauncher {
}
}
if (mEnableHprof) {
- InputStreamSource memory = null;
- try {
- memory = new FileInputStreamSource(mHprofFile);
- listener.testLog("hprof", LogDataType.TEXT, memory);
- } finally {
- StreamUtil.cancel(memory);
- }
+ logHprofResults(mHprofFile, listener);
}
if (mTmpDir != null) {
@@ -359,4 +354,43 @@ public class TfTestLauncher extends SubprocessTfLauncher {
listener.testEnded(tid, Collections.emptyMap());
listener.testRunEnded(0, Collections.emptyMap());
}
+
+ /**
+ * Helper to log and report as metric the hprof data.
+ *
+ * @param hprofFile file containing the Hprof report
+ * @param listener the {@link ITestInvocationListener} where to report the test.
+ */
+ private void logHprofResults(File hprofFile, ITestInvocationListener listener) {
+ if (hprofFile == null) {
+ CLog.w("Hprof file was null. Skipping parsing.");
+ return;
+ }
+ if (!hprofFile.exists()) {
+ CLog.w("Hprof file %s was not found. Skipping parsing.", hprofFile.getAbsolutePath());
+ return;
+ }
+ InputStreamSource memory = null;
+ try {
+ memory = new FileInputStreamSource(hprofFile);
+ listener.testLog("hprof", LogDataType.TEXT, memory);
+ } finally {
+ StreamUtil.cancel(memory);
+ }
+ HprofAllocSiteParser parser = new HprofAllocSiteParser();
+ try {
+ Map<String, String> results = parser.parse(hprofFile);
+ if (results.isEmpty()) {
+ CLog.d("No allocation site found from hprof file");
+ return;
+ }
+ listener.testRunStarted("hprofAllocSites", 1);
+ TestIdentifier tid = new TestIdentifier("hprof", "allocationSites");
+ listener.testStarted(tid);
+ listener.testEnded(tid, results);
+ listener.testRunEnded(0, Collections.emptyMap());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/src/com/android/tradefed/util/HprofAllocSiteParser.java b/src/com/android/tradefed/util/HprofAllocSiteParser.java
new file mode 100644
index 000000000..53519b726
--- /dev/null
+++ b/src/com/android/tradefed/util/HprofAllocSiteParser.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 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 com.android.tradefed.log.LogUtil.CLog;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Helper class to parse info from an Allocation Sites section of hprof reports. */
+public class HprofAllocSiteParser {
+
+ private static final String ALLOC_SITES_START_PATTERN = "SITES BEGIN";
+ private static final String ALLOC_SITES_END_PATTERN = "SITES END";
+ private boolean mHasAllocSiteStarted = false;
+ // format:
+ // percent live alloc'ed stack class
+ // rank self accum bytes objs bytes objs trace name
+ // 1 12.24% 12.24% 12441616 1 12441616 1 586322 byte[]
+ private static final Pattern RANK_PATTERN =
+ Pattern.compile(
+ "(\\s+)([0-9]*)(\\s+)([0-9]*\\.?[0-9]+%)(\\s+)([0-9]*\\.?[0-9]+%)(\\s+)"
+ + "([0-9]+)(\\s+)([0-9]+)(\\s+)([0-9]+)(\\s+)([0-9]+)(\\s+)([0-9]+)"
+ + "(\\s+)(.*)");
+
+ /**
+ * Parse a text hprof report.
+ *
+ * @param hprofReport file containing the hprof report.
+ * @return a Map containing the results
+ */
+ public Map<String, String> parse(File hprofReport) throws IOException {
+ Map<String, String> results = new HashMap<>();
+ if (hprofReport == null || !hprofReport.exists()) {
+ return results;
+ }
+ internalParse(hprofReport, results);
+ return results;
+ }
+
+ /**
+ * Actual parsing line by line of the report to extract information.
+ *
+ * @param report the {@link File} containing the hprof report.
+ * @param currentRes the {@link Map} where the allocation sites will be stored.
+ */
+ private void internalParse(File report, Map<String, String> currentRes) throws IOException {
+ try (BufferedReader br = new BufferedReader(new FileReader(report))) {
+ for (String line; (line = br.readLine()) != null; ) {
+ handleAllocSites(line, currentRes);
+ }
+ }
+ }
+
+ /** Handles the allocation sites in the hprof report. */
+ private void handleAllocSites(String line, Map<String, String> currentRes) {
+ if (line.startsWith(ALLOC_SITES_START_PATTERN)) {
+ mHasAllocSiteStarted = true;
+ } else if (line.startsWith(ALLOC_SITES_END_PATTERN)) {
+ mHasAllocSiteStarted = false;
+ } else if (mHasAllocSiteStarted) {
+ Matcher m = RANK_PATTERN.matcher(line);
+ if (m.find()) {
+ CLog.d(
+ "Rank %s-%s-%s-%s-%s-%s-%s-%s-%s",
+ m.group(2),
+ m.group(4),
+ m.group(6),
+ m.group(8),
+ m.group(10),
+ m.group(12),
+ m.group(14),
+ m.group(16),
+ m.group(18));
+ currentRes.put(String.format("Rank%s", m.group(2)), m.group(12));
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/tradefed/UnitTests.java b/tests/src/com/android/tradefed/UnitTests.java
index a30198576..62ff47996 100644
--- a/tests/src/com/android/tradefed/UnitTests.java
+++ b/tests/src/com/android/tradefed/UnitTests.java
@@ -174,6 +174,7 @@ import com.android.tradefed.util.EmmaXmlReporterParserTest;
import com.android.tradefed.util.FakeTestsZipFolderTest;
import com.android.tradefed.util.FileUtilTest;
import com.android.tradefed.util.FixedByteArrayOutputStreamTest;
+import com.android.tradefed.util.HprofAllocSiteParserTest;
import com.android.tradefed.util.JUnitXmlParserTest;
import com.android.tradefed.util.KeyguardControllerStateTest;
import com.android.tradefed.util.ListInstrumentationParserTest;
@@ -418,6 +419,7 @@ import org.junit.runners.Suite.SuiteClasses;
FakeTestsZipFolderTest.class,
FileUtilTest.class,
FixedByteArrayOutputStreamTest.class,
+ HprofAllocSiteParserTest.class,
HttpHelperTest.class,
HttpMultipartPostTest.class,
JUnitXmlParserTest.class,
diff --git a/tests/src/com/android/tradefed/util/HprofAllocSiteParserTest.java b/tests/src/com/android/tradefed/util/HprofAllocSiteParserTest.java
new file mode 100644
index 000000000..829d76a03
--- /dev/null
+++ b/tests/src/com/android/tradefed/util/HprofAllocSiteParserTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 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.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.util.Map;
+
+/** Unit tests for {@link HprofAllocSiteParser}. */
+@RunWith(JUnit4.class)
+public class HprofAllocSiteParserTest {
+
+ private static final String TEST_STRING =
+ "java.util.WeakHashMap$KeySet.iterator\n"
+ + " java.util.AbstractCollection.toArray(AbstractCollection.java:180)\n"
+ + " com.sun.imageio.stream.StreamCloser$1.run(StreamCloser.java:70)\n"
+ + " java.lang.Thread.run(Thread.java:745)\n"
+ + "SITES BEGIN (ordered by live bytes) Mon Jun 5 04:35:20 2017\n"
+ + " percent live alloc'ed stack class\n"
+ + " rank self accum bytes objs bytes objs trace name\n"
+ + " 1 12.24% 12.24% 12441616 1 12441616 1 586322 byte[]\n"
+ + " 2 6.87% 19.12% 6983280 145485 10509264 218943 977169 HeapChaBuffer\n"
+ + " 3 6.12% 25.24% 6220816 1 6220816 1 586500 byte[]\n"
+ + " 4 4.60% 29.84% 4676976 97437 6912816 144017 977186 HeapChaBuffer\n"
+ + " 5 3.44% 33.28% 3491640 145485 5254632 218943 977168 char[]\n"
+ + " 6 2.68% 35.96% 2727808 17547 2831208 18167 303028 char[]\n"
+ + " 7 2.30% 38.26% 2338488 97437 3456408 144017 977185 char[]\n"
+ + " 8 2.26% 40.52% 2294880 6 2294880 6 1069475 char[]\n"
+ + " 9 2.06% 42.59% 2097168 1 2097168 1 1063791 byte[]\n"
+ + " 10 1.82% 44.41% 1853888 33022 1881488 33455 303005 char[]\n"
+ + " 11 1.21% 45.62% 1227208 15226 2047424 33452 303003 char[]\n"
+ + " 12 1.11% 46.72% 1123248 1 1123248 1 1063811 byte[]\n"
+ + " 13 1.04% 47.76% 1056704 33022 1070560 33455 303051 OptionDef\n"
+ + " 14 1.03% 48.79% 1048592 1 1048592 1 970204 byte[]\n"
+ + " 15 1.03% 49.83% 1048592 1 2080976 13 975125 byte[]\n"
+ + "SITES END";
+
+ private HprofAllocSiteParser mParser;
+
+ @Before
+ public void setUp() {
+ mParser = new HprofAllocSiteParser();
+ }
+
+ /** Test that {@link HprofAllocSiteParser#parse(File)} returns correctly a map of results. */
+ @Test
+ public void testParse() throws Exception {
+ File f = FileUtil.createTempFile("hprof", ".test");
+ try {
+ FileUtil.writeToFile(TEST_STRING, f);
+ Map<String, String> results = mParser.parse(f);
+ assertFalse(results.isEmpty());
+ assertEquals(15, results.size());
+ assertEquals("2294880", results.get("Rank8"));
+ } finally {
+ FileUtil.deleteFile(f);
+ }
+ }
+
+ /** Test that when the parsing does not find any valid pattern, we return no results. */
+ @Test
+ public void testParse_invalidContent() throws Exception {
+ File f = FileUtil.createTempFile("hprof", ".test");
+ try {
+ FileUtil.writeToFile("SITES BEGIN\nugh, we are in big trouble", f);
+ Map<String, String> results = mParser.parse(f);
+ assertTrue(results.isEmpty());
+ } finally {
+ FileUtil.deleteFile(f);
+ }
+ }
+
+ /** Assert that if the file passed for parsing is null we return empty results. */
+ @Test
+ public void testParse_noFile() throws Exception {
+ Map<String, String> results = mParser.parse(null);
+ assertNotNull(results);
+ assertTrue(results.isEmpty());
+ }
+
+ /** Assert that if the file passed for parsing does not exists we return empty results. */
+ @Test
+ public void testParse_fileDoesNotExists() throws Exception {
+ Map<String, String> results = mParser.parse(new File("thisdoesnotexistsatall"));
+ assertNotNull(results);
+ assertTrue(results.isEmpty());
+ }
+}