diff options
author | Maxim Siniavine <siniavine@google.com> | 2014-12-03 17:39:33 -0800 |
---|---|---|
committer | Maxim Siniavine <siniavine@google.com> | 2014-12-09 15:30:51 -0800 |
commit | c2f8ca20c05a22b5438c2fdbdceb32070321b243 (patch) | |
tree | 62acb1fbd447109970ed810b3bf086c26647a333 | |
parent | de0755858206387d9632365410c429ec05689312 (diff) | |
download | loganalysis-c2f8ca20c05a22b5438c2fdbdceb32070321b243.tar.gz |
Allow the use of custom tags to detect java crahes
Change-Id: I949bf99eae6ae4c45140ab90659b7fc9b20f703e
4 files changed, 207 insertions, 2 deletions
diff --git a/src/com/android/loganalysis/parser/JavaCrashParser.java b/src/com/android/loganalysis/parser/JavaCrashParser.java index f1c87a1..e8d561a 100644 --- a/src/com/android/loganalysis/parser/JavaCrashParser.java +++ b/src/com/android/loganalysis/parser/JavaCrashParser.java @@ -41,6 +41,10 @@ public class JavaCrashParser implements IParser { */ private static final Pattern AT = Pattern.compile("^\tat .+$"); + // Sometimes logcat explicitly marks where exception begins and ends + private static final String BEGIN_MARKER = "----- begin exception -----"; + private static final String END_MARKER = "----- end exception -----"; + /** * {@inheritDoc} * @@ -56,6 +60,18 @@ public class JavaCrashParser implements IParser { boolean inStack = false; for (String line : lines) { + if (line.contains(BEGIN_MARKER)) { + inMessage = false; + inCausedBy = false; + inStack = false; + stack = new StringBuilder(); + message = new StringBuilder(); + jc = null; + continue; + } + if (line.contains(END_MARKER)) { + break; + } if (!inStack) { Matcher exceptionMatch = EXCEPTION.matcher(line); if (exceptionMatch.matches()) { diff --git a/src/com/android/loganalysis/parser/LogcatParser.java b/src/com/android/loganalysis/parser/LogcatParser.java index 7011646..f729bb6 100644 --- a/src/com/android/loganalysis/parser/LogcatParser.java +++ b/src/com/android/loganalysis/parser/LogcatParser.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; @@ -127,10 +128,14 @@ public class LogcatParser implements IParser { private Map<Integer, String> mPids = new HashMap<Integer, String>(); + private List<JavaCrashTag> mJavaCrashTags = new ArrayList<JavaCrashTag>(); + /** * Constructor for {@link LogcatParser}. */ public LogcatParser() { + // Add default tag for java crash + addJavaCrashTag("E", "AndroidRuntime", LogcatParser.JAVA_CRASH); initPatterns(); } @@ -284,7 +289,7 @@ public class LogcatParser implements IParser { } // PID and TID are enough to separate Java crashes. - if (("E".equals(level) && "AndroidRuntime".equals(tag))) { + if (anyJavaCrashTagMatches(level, tag)) { String key = encodeLine(pid, tid, level, tag); LogcatData data; if (!mDataMap.containsKey(key)) { @@ -321,7 +326,7 @@ public class LogcatParser implements IParser { MiscLogcatItem item = null; if ("E".equals(data.mLevel) && "ActivityManager".equals(data.mTag)) { item = new AnrParser().parse(data.mLines); - } else if ("E".equals(data.mLevel) && "AndroidRuntime".equals(data.mTag)) { + } else if (anyJavaCrashTagMatches(data.mLevel, data.mTag)) { // Get the process name/PID from the Java crash, then pass the rest of the lines to // the parser. Integer pid = null; @@ -349,6 +354,7 @@ public class LogcatParser implements IParser { if (item != null) { item.setApp(app); item.setPid(pid); + item.setCategory(getCategory(data.mLevel, data.mTag)); } } else if ("I".equals(data.mLevel) && "DEBUG".equals(data.mTag)) { // CLog.v("Parsing native crash: %s", data.mLines); @@ -481,4 +487,83 @@ public class LogcatParser implements IParser { return false; } } + + /** + * Allows Java crashes to be picked up from different parts of logcat. Normally the crashes + * are error level messages from AndroidRuntime, but they could also be from other sources. + * Use this method to parse java crashes from those other sources. + * + * @param level log level on which to look for java crashes + * @param tag log tag where to look for java crashes + */ + public void addJavaCrashTag(String level, String tag, String category) { + mJavaCrashTags.add(new JavaCrashTag(level, tag, category)); + } + + /** + * Determines if any of the java crash tags is matching a logcat line. + * + * @param level log level of the logcat line + * @param tag tag of the logcat line + * @return True if any java crash tag matches the current level and tag. False otherwise. + */ + private boolean anyJavaCrashTagMatches(String level, String tag) { + return findCrashTag(level, tag) != null; + } + + /** + * Finds JavaCrashTag matching given level and tag. + * + * @param level level to find + * @param tag tag to find + * @return matching JavaCrashTag or null if no matches exist. + */ + private JavaCrashTag findCrashTag(String level, String tag) { + for (JavaCrashTag t : mJavaCrashTags) { + if (t.matches(level, tag)) { + return t; + } + } + return null; + } + + /** + * Returns category for a given JavaCrashTag. + * + * @param level level of the JavaCrashTag + * @param tag tag of the JavaCrashTag + * @return category of the JavaCrashTag, matching search criteria. If nothing was found + * returns default value for the standard java crash. + */ + private String getCategory(String level, String tag) { + JavaCrashTag jct = findCrashTag(level, tag); + if (jct == null) { + return LogcatParser.JAVA_CRASH; + } else { + return jct.getCategory(); + } + } + + /** + * Class to encapsulate the tags that indicate which java crashes should be parsed. + */ + private class JavaCrashTag { + private String mLevel; + private String mTag; + private String mCategory; + + public JavaCrashTag(String level, String tag, String category) { + mLevel = level; + mTag = tag; + mCategory = category; + } + + public boolean matches(String level, String tag) { + return mLevel.equals(level) && mTag.equals(tag); + } + + public String getCategory() { + return mCategory; + } + } } diff --git a/tests/src/com/android/loganalysis/parser/JavaCrashParserTest.java b/tests/src/com/android/loganalysis/parser/JavaCrashParserTest.java index 723633f..a7d06b3 100644 --- a/tests/src/com/android/loganalysis/parser/JavaCrashParserTest.java +++ b/tests/src/com/android/loganalysis/parser/JavaCrashParserTest.java @@ -124,4 +124,28 @@ public class JavaCrashParserTest extends TestCase { assertEquals("This is the message", jc.getMessage()); assertEquals(ArrayUtil.join("\n", lines.subList(0, lines.size()-2)), jc.getStack()); } + + /** + * Tests that only parts between the markers are parsed. + */ + public void testParse_begin_end_markers() { + List<String> lines = Arrays.asList( + "error: this message has begin and end", + "----- begin exception -----", + "java.lang.Exception: This message", + "is many lines", + "long.", + "\tat class.method1(Class.java:1)", + "\tat class.method2(Class.java:2)", + "\tat class.method3(Class.java:3)", + "----- end exception -----"); + + JavaCrashItem jc = new JavaCrashParser().parse(lines); + assertNotNull(jc); + assertEquals("java.lang.Exception", jc.getException()); + assertEquals("This message\nis many lines\nlong.", jc.getMessage()); + assertNotNull(jc.getStack()); + assertFalse(jc.getStack().contains("begin exception")); + assertFalse(jc.getStack().contains("end exception")); + } } diff --git a/tests/src/com/android/loganalysis/parser/LogcatParserTest.java b/tests/src/com/android/loganalysis/parser/LogcatParserTest.java index 44f3151..9b6dff0 100644 --- a/tests/src/com/android/loganalysis/parser/LogcatParserTest.java +++ b/tests/src/com/android/loganalysis/parser/LogcatParserTest.java @@ -106,6 +106,86 @@ public class LogcatParserTest extends TestCase { logcat.getJavaCrashes().get(0).getEventTime()); } + public void testParse_test_exception() throws ParseException { + List<String> lines = Arrays.asList( + "11-25 19:26:53.581 5832 7008 I TestRunner: ----- begin exception -----", + "11-25 19:26:53.589 5832 7008 I TestRunner: ", + "11-25 19:26:53.589 5832 7008 I TestRunner: java.util.concurrent.TimeoutException", + "11-25 19:26:53.589 5832 7008 I TestRunner: at android.support.test.uiautomator.WaitMixin.wait(WaitMixin.java:49)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at android.support.test.uiautomator.WaitMixin.wait(WaitMixin.java:36)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at android.support.test.uiautomator.UiDevice.wait(UiDevice.java:169)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at com.android.test.uiautomator.common.helpers.MapsHelper.doSearch(MapsHelper.java:87)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at com.android.test.uiautomator.aupt.MapsTest.testMaps(MapsTest.java:58)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at java.lang.reflect.Method.invoke(Native Method)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at java.lang.reflect.Method.invoke(Method.java:372)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at junit.framework.TestCase.runBare(TestCase.java:134)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at junit.framework.TestResult$1.protect(TestResult.java:115)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at junit.framework.TestResult.runProtected(TestResult.java:133)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at junit.framework.TestResult.run(TestResult.java:118)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at junit.framework.TestCase.run(TestCase.java:124)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at android.support.test.aupt.AuptTestRunner$AuptPrivateTestRunner.runTest(AuptTestRunner.java:182)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)", + "11-25 19:26:53.589 5832 7008 I TestRunner: at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1848)", + "11-25 19:26:53.589 5832 7008 I TestRunner: ----- end exception -----" + ); + + LogcatParser logcatParser = new LogcatParser("2012"); + logcatParser.addJavaCrashTag("I", "TestRunner", LogcatParser.JAVA_CRASH); + LogcatItem logcat = logcatParser.parse(lines); + assertNotNull(logcat); + assertEquals(1, logcat.getEvents().size()); + assertEquals(1, logcat.getJavaCrashes().size()); + assertEquals(5832, logcat.getJavaCrashes().get(0).getPid().intValue()); + assertEquals(7008, logcat.getJavaCrashes().get(0).getTid().intValue()); + assertEquals("", logcat.getJavaCrashes().get(0).getLastPreamble()); + assertEquals("", logcat.getJavaCrashes().get(0).getProcessPreamble()); + assertEquals(LogcatParser.JAVA_CRASH, logcat.getJavaCrashes().get(0).getCategory()); + } + + public void testParse_test_exception_with_exras() throws ParseException { + List<String> lines = Arrays.asList( + "12-06 17:19:18.746 6598 7960 I TestRunner: failed: testYouTube(com.android.test.uiautomator.aupt.YouTubeTest)", + "12-06 17:19:18.746 6598 7960 I TestRunner: ----- begin exception -----", + "12-06 17:19:18.747 6598 7960 I TestRunner: ", + "12-06 17:19:18.747 6598 7960 I TestRunner: java.util.concurrent.TimeoutException", + "12-06 17:19:18.747 6598 7960 I TestRunner: at android.support.test.uiautomator.WaitMixin.wait(WaitMixin.java:49)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at android.support.test.uiautomator.WaitMixin.wait(WaitMixin.java:36)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at android.support.test.uiautomator.UiDevice.wait(UiDevice.java:169)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at android.support.test.aupt.AppLauncher.launchApp(AppLauncher.java:127)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at com.android.test.uiautomator.common.helpers.YouTubeHelper.open(YouTubeHelper.java:49)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at com.android.test.uiautomator.aupt.YouTubeTest.setUp(YouTubeTest.java:44)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at junit.framework.TestCase.runBare(TestCase.java:132)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at junit.framework.TestResult$1.protect(TestResult.java:115)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at junit.framework.TestResult.runProtected(TestResult.java:133)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at junit.framework.TestResult.run(TestResult.java:118)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at junit.framework.TestCase.run(TestCase.java:124)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at android.support.test.aupt.AuptTestRunner$AuptPrivateTestRunner.runTest(AuptTestRunner.java:182)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:555)", + "12-06 17:19:18.747 6598 7960 I TestRunner: at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1851)", + "12-06 17:19:18.747 6598 7960 I TestRunner: ----- end exception -----" + ); + + LogcatParser logcatParser = new LogcatParser("2012"); + logcatParser.addJavaCrashTag("I", "TestRunner", LogcatParser.JAVA_CRASH); + LogcatItem logcat = logcatParser.parse(lines); + assertNotNull(logcat); + assertEquals(1, logcat.getEvents().size()); + assertEquals(1, logcat.getJavaCrashes().size()); + assertEquals(6598, logcat.getJavaCrashes().get(0).getPid().intValue()); + assertEquals(7960, logcat.getJavaCrashes().get(0).getTid().intValue()); + assertEquals("", logcat.getJavaCrashes().get(0).getLastPreamble()); + assertEquals("", logcat.getJavaCrashes().get(0).getProcessPreamble()); + // Check that lines not related to java crash are absent + assertFalse(logcat.getJavaCrashes().get(0).getStack().contains("begin exception")); + assertFalse(logcat.getJavaCrashes().get(0).getStack().contains("end exception")); + assertFalse(logcat.getJavaCrashes().get(0).getStack().contains("failed: testYouTube")); + //System.out.println(logcat.getJavaCrashes().get(0).getStack()); + } + /** * Test that Java crashes from system server can be parsed. */ |