Add traces parser.

Bug: 6429055
Change-Id: I581d886f839b80c11ebb62627f99e6b4c07fd5e2
diff --git a/src/com/android/tradefed/util/brillopad/BugreportParser.java b/src/com/android/tradefed/util/brillopad/BugreportParser.java
index 7714c6f..1c8e8a6 100644
--- a/src/com/android/tradefed/util/brillopad/BugreportParser.java
+++ b/src/com/android/tradefed/util/brillopad/BugreportParser.java
@@ -17,11 +17,13 @@
 
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.util.brillopad.item.AnrItem;
 import com.android.tradefed.util.brillopad.item.BugreportItem;
 import com.android.tradefed.util.brillopad.item.LogcatItem;
 import com.android.tradefed.util.brillopad.item.MemInfoItem;
 import com.android.tradefed.util.brillopad.item.ProcrankItem;
 import com.android.tradefed.util.brillopad.item.SystemPropsItem;
+import com.android.tradefed.util.brillopad.item.TracesItem;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -32,6 +34,7 @@
 import java.text.SimpleDateFormat;
 import java.util.Date;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -43,6 +46,7 @@
     private static final String PROCRANK_SECTION_REGEX = "------ PROCRANK .*";
     private static final String SYSTEM_PROP_SECTION_REGEX = "------ SYSTEM PROPERTIES .*";
     private static final String SYSTEM_LOG_SECTION_REGEX = "------ SYSTEM LOG .*";
+    private static final String ANR_TRACES_SECTION_REGEX = "------ VM TRACES AT LAST ANR .*";
     private static final String NOOP_SECTION_REGEX = "------ .*";
 
     /**
@@ -122,6 +126,7 @@
         addSectionParser(new MemInfoParser(), MEM_INFO_SECTION_REGEX);
         addSectionParser(new ProcrankParser(), PROCRANK_SECTION_REGEX);
         addSectionParser(new SystemPropsParser(), SYSTEM_PROP_SECTION_REGEX);
+        addSectionParser(new TracesParser(), ANR_TRACES_SECTION_REGEX);
         addSectionParser(mLogcatParser, SYSTEM_LOG_SECTION_REGEX);
         addSectionParser(new NoopParser(), NOOP_SECTION_REGEX);
     }
@@ -139,6 +144,29 @@
             mBugreport.setProcrank((ProcrankItem) getSection(ProcrankItem.TYPE));
             mBugreport.setSystemLog((LogcatItem) getSection(LogcatItem.TYPE));
             mBugreport.setSystemProps((SystemPropsItem) getSection(SystemPropsItem.TYPE));
+
+            TracesItem traces = (TracesItem) getSection(TracesItem.TYPE);
+            if (traces != null && traces.getApp() != null && traces.getStack() != null &&
+                    mBugreport.getSystemLog() != null) {
+                addAnrTrace(mBugreport.getSystemLog().getAnrs(), traces.getApp(),
+                        traces.getStack());
+
+            }
+        }
+    }
+
+    /**
+     * Add the trace from {@link TracesItem} to the last seen {@link AnrItem} matching a given app.
+     */
+    private void addAnrTrace(List<AnrItem> anrs, String app, String trace) {
+        ListIterator<AnrItem> li = anrs.listIterator(anrs.size());
+
+        while (li.hasPrevious()) {
+            AnrItem anr = li.previous();
+            if (app.equals(anr.getApp())) {
+                anr.setTrace(trace);
+                return;
+            }
         }
     }
 
diff --git a/src/com/android/tradefed/util/brillopad/MonkeyLogParser.java b/src/com/android/tradefed/util/brillopad/MonkeyLogParser.java
index 6b06762..cb91efc 100644
--- a/src/com/android/tradefed/util/brillopad/MonkeyLogParser.java
+++ b/src/com/android/tradefed/util/brillopad/MonkeyLogParser.java
@@ -17,9 +17,11 @@
 
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.InputStreamSource;
+import com.android.tradefed.util.brillopad.item.AnrItem;
 import com.android.tradefed.util.brillopad.item.GenericLogcatItem;
 import com.android.tradefed.util.brillopad.item.MonkeyLogItem;
 import com.android.tradefed.util.brillopad.item.MonkeyLogItem.DroppedCategory;
+import com.android.tradefed.util.brillopad.item.TracesItem;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -37,15 +39,15 @@
  * A {@link IParser} to parse monkey logs.
  */
 public class MonkeyLogParser implements IParser {
-    private final static Pattern THROTTLE = Pattern.compile(
+    private static final Pattern THROTTLE = Pattern.compile(
             "adb shell monkey.* --throttle (\\d+).*");
-    private final static Pattern SEED_AND_TARGET_COUNT = Pattern.compile(
+    private static final Pattern SEED_AND_TARGET_COUNT = Pattern.compile(
             ":Monkey: seed=(\\d+) count=(\\d+)");
-    private final static Pattern SECURITY_EXCEPTIONS = Pattern.compile(
+    private static final Pattern SECURITY_EXCEPTIONS = Pattern.compile(
             "adb shell monkey.* --ignore-security-exceptions.*");
 
-    private final static Pattern PACKAGES = Pattern.compile(":AllowPackage: (\\S+)");
-    private final static Pattern CATEGORIES = Pattern.compile(":IncludeCategory: (\\S+)");
+    private static final Pattern PACKAGES = Pattern.compile(":AllowPackage: (\\S+)");
+    private static final Pattern CATEGORIES = Pattern.compile(":IncludeCategory: (\\S+)");
 
     private static final Pattern START_UPTIME = Pattern.compile(
             "# (.*) - device uptime = (\\d+\\.\\d+): Monkey command used for this test:");
@@ -53,28 +55,32 @@
             "# (.*) - device uptime = (\\d+\\.\\d+): Monkey command ran for: " +
             "(\\d+):(\\d+) \\(mm:ss\\)");
 
-    private final static Pattern INTERMEDIATE_COUNT = Pattern.compile(
+    private static final Pattern INTERMEDIATE_COUNT = Pattern.compile(
             "\\s+// Sending event #(\\d+)");
-    private final static Pattern FINISHED = Pattern.compile("// Monkey finished");
-    private final static Pattern FINAL_COUNT = Pattern.compile("Events injected: (\\d+)");
+    private static final Pattern FINISHED = Pattern.compile("// Monkey finished");
+    private static final Pattern FINAL_COUNT = Pattern.compile("Events injected: (\\d+)");
 
-    private final static Pattern DROPPED_KEYS = Pattern.compile(":Dropped: .*keys=(\\d+).*");
-    private final static Pattern DROPPED_POINTERS = Pattern.compile(
+    private static final Pattern DROPPED_KEYS = Pattern.compile(":Dropped: .*keys=(\\d+).*");
+    private static final Pattern DROPPED_POINTERS = Pattern.compile(
             ":Dropped: .*pointers=(\\d+).*");
-    private final static Pattern DROPPED_TRACKBALLS = Pattern.compile(
+    private static final Pattern DROPPED_TRACKBALLS = Pattern.compile(
             ":Dropped: .*trackballs=(\\d+).*");
-    private final static Pattern DROPPED_FLIPS = Pattern.compile(":Dropped: .*flips=(\\d+).*");
-    private final static Pattern DROPPED_ROTATIONS = Pattern.compile(
+    private static final Pattern DROPPED_FLIPS = Pattern.compile(":Dropped: .*flips=(\\d+).*");
+    private static final Pattern DROPPED_ROTATIONS = Pattern.compile(
             ":Dropped: .*rotations=(\\d+).*");
 
-    private final static Pattern ANR = Pattern.compile(
+    private static final Pattern ANR = Pattern.compile(
             "// NOT RESPONDING: (\\S+) \\(pid (\\d+)\\)");
-    private final static Pattern JAVA_CRASH = Pattern.compile(
+    private static final Pattern JAVA_CRASH = Pattern.compile(
             "// CRASH: (\\S+) \\(pid (\\d+)\\)");
 
+    private static final Pattern TRACES_START = Pattern.compile("anr traces:");
+    private static final Pattern TRACES_STOP = Pattern.compile("// anr traces status was \\d+");
+
     private boolean mMatchingAnr = false;
     private boolean mMatchingJavaCrash = false;
-    private List<String> mCrash = null;
+    private boolean mMatchingTraces = false;
+    private List<String> mBlock = null;
     private String mApp = null;
     private int mPid = 0;
 
@@ -126,6 +132,8 @@
      * Parse a line of input.
      */
     private void parseLine(String line) {
+        Matcher m;
+
         if (mMatchingAnr || mMatchingJavaCrash) {
             if (mMatchingJavaCrash) {
                 line = line.replace("// ", "");
@@ -133,9 +141,9 @@
             if ("".equals(line)) {
                 GenericLogcatItem crash;
                 if (mMatchingAnr) {
-                    crash = new AnrParser().parse(mCrash);
+                    crash = new AnrParser().parse(mBlock);
                 } else {
-                    crash = new JavaCrashParser().parse(mCrash);
+                    crash = new JavaCrashParser().parse(mBlock);
                 }
                 crash.setPid(mPid);
                 crash.setApp(mApp);
@@ -143,16 +151,35 @@
 
                 mMatchingAnr = false;
                 mMatchingJavaCrash = false;
-                mCrash = null;
+                mBlock = null;
                 mApp = null;
                 mPid = 0;
             } else {
-                mCrash.add(line);
+                mBlock.add(line);
             }
             return;
         }
 
-        Matcher m = THROTTLE.matcher(line);
+        if (mMatchingTraces) {
+            m = TRACES_STOP.matcher(line);
+            if (m.matches()) {
+                TracesItem traces = new TracesParser().parse(mBlock);
+
+                // Set the trace if the crash is an ANR and if the app for the crash and trace match
+                if (traces != null && traces.getApp() != null && traces.getStack() != null &&
+                        mMonkeyLog.getCrash() instanceof AnrItem &&
+                        traces.getApp().equals(mMonkeyLog.getCrash().getApp())) {
+                    ((AnrItem) mMonkeyLog.getCrash()).setTrace(traces.getStack());
+                }
+
+                mMatchingTraces = false;
+                mBlock = null;
+            } else {
+                mBlock.add(line);
+            }
+        }
+
+        m = THROTTLE.matcher(line);
         if (m.matches()) {
             mMonkeyLog.setThrottle(Integer.parseInt(m.group(1)));
         }
@@ -221,16 +248,21 @@
         if (m.matches()) {
             mApp = m.group(1);
             mPid = Integer.parseInt(m.group(2));
-            mCrash = new LinkedList<String>();
+            mBlock = new LinkedList<String>();
             mMatchingAnr = true;
         }
         m = JAVA_CRASH.matcher(line);
         if (m.matches()) {
             mApp = m.group(1);
             mPid = Integer.parseInt(m.group(2));
-            mCrash = new LinkedList<String>();
+            mBlock = new LinkedList<String>();
             mMatchingJavaCrash = true;
         }
+        m = TRACES_START.matcher(line);
+        if (m.matches()) {
+            mBlock = new LinkedList<String>();
+            mMatchingTraces = true;
+        }
     }
 
     /**
diff --git a/src/com/android/tradefed/util/brillopad/TracesParser.java b/src/com/android/tradefed/util/brillopad/TracesParser.java
new file mode 100644
index 0000000..122f61d
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/TracesParser.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2011 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.brillopad;
+
+import com.android.tradefed.util.brillopad.item.TracesItem;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A {@link IParser} to parse Android traces files.
+ * <p>
+ * For now, this only extracts the main stack trace from the first process. It is used to get a
+ * stack from {@code /data/anr/traces.txt} which can be used to give some context about the ANR. If
+ * there is a need, this parser can be expanded to parse all stacks from all processes.
+ */
+public class TracesParser implements IParser {
+
+    /**
+     * Matches: ----- pid PID at YYYY-MM-DD hh:mm:ss -----
+     */
+    private static final Pattern PID = Pattern.compile(
+            "^----- pid (\\d+) at \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} -----$");
+
+    /**
+     * Matches: Cmd line: APP
+     */
+    private static final Pattern APP = Pattern.compile("^Cmd line: (\\S+)$");
+
+    /**
+     * Matches: "main" prio=5 tid=1 STATE
+     */
+    private static final Pattern STACK = Pattern.compile("^\"main\" .*$");
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return The {@link TracesItem}.
+     */
+    @Override
+    public TracesItem parse(List<String> lines) {
+        TracesItem traces = new TracesItem();
+        StringBuffer stack = null;
+
+        for (String line : lines) {
+            if (stack == null) {
+                Matcher m = PID.matcher(line);
+                if (m.matches()) {
+                    traces.setPid(Integer.parseInt(m.group(1)));
+                }
+                m = APP.matcher(line);
+                if (m.matches()) {
+                    traces.setApp(m.group(1));
+                }
+                m = STACK.matcher(line);
+                if (m.matches()) {
+                    stack = new StringBuffer();
+                    stack.append(line);
+                    stack.append("\n");
+                }
+            } else if (!"".equals(line)) {
+                stack.append(line);
+                stack.append("\n");
+            } else {
+                traces.setStack(stack.toString().trim());
+                return traces;
+            }
+        }
+        traces.setStack(stack.toString().trim());
+        return traces;
+    }
+
+}
diff --git a/src/com/android/tradefed/util/brillopad/item/AnrItem.java b/src/com/android/tradefed/util/brillopad/item/AnrItem.java
index d1c0a2b..ed9e02d 100644
--- a/src/com/android/tradefed/util/brillopad/item/AnrItem.java
+++ b/src/com/android/tradefed/util/brillopad/item/AnrItem.java
@@ -47,6 +47,7 @@
     private static final String ACTIVITY = "ACTIVITY";
     private static final String REASON = "REASON";
     private static final String STACK = "STACK";
+    private static final String TRACE = "TRACE";
 
     private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
         CpuUsageCategory.TOTAL.toString(),
@@ -56,7 +57,7 @@
         LoadCategory.LOAD_1.toString(),
         LoadCategory.LOAD_5.toString(),
         LoadCategory.LOAD_15.toString(),
-        ACTIVITY, REASON, STACK));
+        ACTIVITY, REASON, STACK, TRACE));
 
     /**
      * The constructor for {@link AnrItem}.
@@ -134,4 +135,18 @@
     public void setStack(String stack) {
         setAttribute(STACK, stack);
     }
+
+    /**
+     * Get the main trace for the ANR.
+     */
+    public String getTrace() {
+        return (String) getAttribute(TRACE);
+    }
+
+    /**
+     * Set the main trace for the ANR.
+     */
+    public void setTrace(String trace) {
+        setAttribute(TRACE, trace);
+    }
 }
diff --git a/src/com/android/tradefed/util/brillopad/item/MonkeyLogItem.java b/src/com/android/tradefed/util/brillopad/item/MonkeyLogItem.java
index 501ba9b..41ceb10 100644
--- a/src/com/android/tradefed/util/brillopad/item/MonkeyLogItem.java
+++ b/src/com/android/tradefed/util/brillopad/item/MonkeyLogItem.java
@@ -37,25 +37,25 @@
         ROTATIONS
     }
 
-    private final static String TYPE = "MONKEY_LOG";
+    private static final String TYPE = "MONKEY_LOG";
 
-    private final static String START_TIME = "START_TIME";
-    private final static String STOP_TIME = "STOP_TIME";
-    private final static String PACKAGES = "PACKAGES";
-    private final static String CATEGORIES = "CATEGORIES";
-    private final static String THROTTLE = "THROTTLE";
-    private final static String SEED = "SEED";
-    private final static String TARGET_COUNT = "TARGET_COUNT";
-    private final static String IGNORE_SECURITY_EXCEPTIONS = "IGNORE_SECURITY_EXCEPTIONS";
-    private final static String TOTAL_DURATION = "TOTAL_TIME";
-    private final static String START_UPTIME_DURATION = "START_UPTIME";
-    private final static String STOP_UPTIME_DURATION = "STOP_UPTIME";
-    private final static String IS_FINISHED = "IS_FINISHED";
-    private final static String INTERMEDIATE_COUNT = "INTERMEDIATE_COUNT";
-    private final static String FINAL_COUNT = "FINAL_COUNT";
-    private final static String CRASH = "CRASH";
+    private static final String START_TIME = "START_TIME";
+    private static final String STOP_TIME = "STOP_TIME";
+    private static final String PACKAGES = "PACKAGES";
+    private static final String CATEGORIES = "CATEGORIES";
+    private static final String THROTTLE = "THROTTLE";
+    private static final String SEED = "SEED";
+    private static final String TARGET_COUNT = "TARGET_COUNT";
+    private static final String IGNORE_SECURITY_EXCEPTIONS = "IGNORE_SECURITY_EXCEPTIONS";
+    private static final String TOTAL_DURATION = "TOTAL_TIME";
+    private static final String START_UPTIME_DURATION = "START_UPTIME";
+    private static final String STOP_UPTIME_DURATION = "STOP_UPTIME";
+    private static final String IS_FINISHED = "IS_FINISHED";
+    private static final String INTERMEDIATE_COUNT = "INTERMEDIATE_COUNT";
+    private static final String FINAL_COUNT = "FINAL_COUNT";
+    private static final String CRASH = "CRASH";
 
-    private final static Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
+    private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
             START_TIME, STOP_TIME, PACKAGES, CATEGORIES, THROTTLE, SEED, TARGET_COUNT,
             IGNORE_SECURITY_EXCEPTIONS, TOTAL_DURATION, START_UPTIME_DURATION, STOP_UPTIME_DURATION,
             IS_FINISHED, INTERMEDIATE_COUNT, FINAL_COUNT, CRASH,
diff --git a/src/com/android/tradefed/util/brillopad/item/TracesItem.java b/src/com/android/tradefed/util/brillopad/item/TracesItem.java
new file mode 100644
index 0000000..6d3f763
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/item/TracesItem.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 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.brillopad.item;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * An {@link IItem} used to store traces info.
+ * <p>
+ * For now, this only stores info about the main stack trace from the first process. It is used to
+ * get a stack from {@code /data/anr/traces.txt} which can be used to give some context about the
+ * ANR. If there is a need, this item can be expanded to store all stacks from all processes.
+ * </p>
+ */
+public class TracesItem extends GenericItem {
+    public static final String TYPE = "TRACES_ITEM";
+
+    private static final String PID = "PID";
+    private static final String APP = "APP";
+    private static final String STACK = "STACK";
+
+    private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
+            PID, APP, STACK));
+
+    /**
+     * The constructor for {@link TracesItem}.
+     */
+    public TracesItem() {
+        super(TYPE, ATTRIBUTES);
+    }
+
+    /**
+     * Get the PID of the event.
+     */
+    public Integer getPid() {
+        return (Integer) getAttribute(PID);
+    }
+
+    /**
+     * Set the PID of the event.
+     */
+    public void setPid(Integer pid) {
+        setAttribute(PID, pid);
+    }
+
+    /**
+     * Get the app or package name of the event.
+     */
+    public String getApp() {
+        return (String) getAttribute(APP);
+    }
+
+    /**
+     * Set the app or package name of the event.
+     */
+    public void setApp(String app) {
+        setAttribute(APP, app);
+    }
+
+    /**
+     * Get the stack for the crash.
+     */
+    public String getStack() {
+        return (String) getAttribute(STACK);
+    }
+
+    /**
+     * Set the stack for the crash.
+     */
+    public void setStack(String stack) {
+        setAttribute(STACK, stack);
+    }
+}
diff --git a/tests/src/com/android/tradefed/util/brillopad/BrillopadTests.java b/tests/src/com/android/tradefed/util/brillopad/BrillopadTests.java
index b646b45..a80bcfb 100644
--- a/tests/src/com/android/tradefed/util/brillopad/BrillopadTests.java
+++ b/tests/src/com/android/tradefed/util/brillopad/BrillopadTests.java
@@ -40,6 +40,7 @@
         addTestSuite(NativeCrashParserTest.class);
         addTestSuite(ProcrankParserTest.class);
         addTestSuite(SystemPropsParserTest.class);
+        addTestSuite(TracesParserTest.class);
 
         // item
         addTestSuite(GenericItemTest.class);
diff --git a/tests/src/com/android/tradefed/util/brillopad/BugreportParserTest.java b/tests/src/com/android/tradefed/util/brillopad/BugreportParserTest.java
index b14a37b..71cad3e 100644
--- a/tests/src/com/android/tradefed/util/brillopad/BugreportParserTest.java
+++ b/tests/src/com/android/tradefed/util/brillopad/BugreportParserTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.tradefed.util.brillopad;
 
+import com.android.tradefed.util.ArrayUtil;
 import com.android.tradefed.util.brillopad.item.BugreportItem;
 
 import junit.framework.TestCase;
@@ -79,6 +80,27 @@
                 "[gsm.version.ril-impl]: [android moto-ril-multimode 1.0]",
                 "",
                 "------ SECTION ------",
+                "",
+                "------ VM TRACES AT LAST ANR (/data/anr/traces.txt: 2012-04-25 17:17:08) ------",
+                "",
+                "",
+                "----- pid 2887 at 2012-04-25 17:17:08 -----",
+                "Cmd line: com.android.package",
+                "",
+                "DALVIK THREADS:",
+                "(mutexes: tll=0 tsl=0 tscl=0 ghl=0)",
+                "",
+                "\"main\" prio=5 tid=1 SUSPENDED",
+                "  | group=\"main\" sCount=1 dsCount=0 obj=0x00000001 self=0x00000001",
+                "  | sysTid=2887 nice=0 sched=0/0 cgrp=foreground handle=0000000001",
+                "  | schedstat=( 0 0 0 ) utm=5954 stm=1017 core=0",
+                "  at class.method1(Class.java:1)",
+                "  at class.method2(Class.java:2)",
+                "  at class.method2(Class.java:2)",
+                "",
+                "----- end 2887 -----",
+                "",
+                "------ SECTION ------",
                 "");
 
         BugreportItem bugreport = new BugreportParser().parse(lines);
@@ -95,6 +117,8 @@
         assertEquals(parseTime("2012-04-25 09:55:47.799"), bugreport.getSystemLog().getStartTime());
         assertEquals(parseTime("2012-04-25 18:33:27.273"), bugreport.getSystemLog().getStopTime());
         assertEquals(3, bugreport.getSystemLog().getEvents().size());
+        assertEquals(1, bugreport.getSystemLog().getAnrs().size());
+        assertNotNull(bugreport.getSystemLog().getAnrs().get(0).getTrace());
 
         assertNotNull(bugreport.getSystemProps());
         assertEquals(4, bugreport.getSystemProps().size());
@@ -121,6 +145,140 @@
         assertEquals(parseTime("1999-01-01 01:02:04.000"), bugreport.getSystemLog().getStopTime());
     }
 
+    /**
+     * Test that the trace is set correctly if there is only one ANR.
+     */
+    public void testSetAnrTrace_single() {
+        List<String> lines = Arrays.asList(
+                "========================================================",
+                "== dumpstate: 2012-04-25 20:45:10",
+                "========================================================",
+                "------ SYSTEM LOG (logcat -v threadtime -d *:v) ------",
+                "04-25 17:17:08.445   312   366 E ActivityManager: ANR (application not responding) in process: com.android.package",
+                "04-25 17:17:08.445   312   366 E ActivityManager: Reason: keyDispatchingTimedOut",
+                "04-25 17:17:08.445   312   366 E ActivityManager: Load: 0.71 / 0.83 / 0.51",
+                "04-25 17:17:08.445   312   366 E ActivityManager: 33% TOTAL: 21% user + 11% kernel + 0.3% iowait",
+                "",
+                "------ VM TRACES AT LAST ANR (/data/anr/traces.txt: 2012-04-25 17:17:08) ------",
+                "",
+                "----- pid 2887 at 2012-04-25 17:17:08 -----",
+                "Cmd line: com.android.package",
+                "",
+                "DALVIK THREADS:",
+                "(mutexes: tll=0 tsl=0 tscl=0 ghl=0)",
+                "",
+                "\"main\" prio=5 tid=1 SUSPENDED",
+                "  | group=\"main\" sCount=1 dsCount=0 obj=0x00000001 self=0x00000001",
+                "  | sysTid=2887 nice=0 sched=0/0 cgrp=foreground handle=0000000001",
+                "  | schedstat=( 0 0 0 ) utm=5954 stm=1017 core=0",
+                "  at class.method1(Class.java:1)",
+                "  at class.method2(Class.java:2)",
+                "  at class.method2(Class.java:2)",
+                "",
+                "----- end 2887 -----",
+                "");
+
+        List<String> expectedStack = Arrays.asList(
+                "\"main\" prio=5 tid=1 SUSPENDED",
+                "  | group=\"main\" sCount=1 dsCount=0 obj=0x00000001 self=0x00000001",
+                "  | sysTid=2887 nice=0 sched=0/0 cgrp=foreground handle=0000000001",
+                "  | schedstat=( 0 0 0 ) utm=5954 stm=1017 core=0",
+                "  at class.method1(Class.java:1)",
+                "  at class.method2(Class.java:2)",
+                "  at class.method2(Class.java:2)");
+
+        BugreportItem bugreport = new BugreportParser().parse(lines);
+
+        assertNotNull(bugreport.getSystemLog());
+        assertEquals(1, bugreport.getSystemLog().getAnrs().size());
+        assertEquals(ArrayUtil.join("\n", expectedStack),
+                bugreport.getSystemLog().getAnrs().get(0).getTrace());
+    }
+
+    /**
+     * Test that the trace is set correctly if there are multiple ANRs.
+     */
+    public void testSetAnrTrace_multiple() {
+        List<String> lines = Arrays.asList(
+                "========================================================",
+                "== dumpstate: 2012-04-25 20:45:10",
+                "========================================================",
+                "------ SYSTEM LOG (logcat -v threadtime -d *:v) ------",
+                "04-25 17:17:08.445   312   366 E ActivityManager: ANR (application not responding) in process: com.android.package",
+                "04-25 17:17:08.445   312   366 E ActivityManager: Reason: keyDispatchingTimedOut",
+                "04-25 17:17:08.445   312   366 E ActivityManager: Load: 0.71 / 0.83 / 0.51",
+                "04-25 17:17:08.445   312   366 E ActivityManager: 33% TOTAL: 21% user + 11% kernel + 0.3% iowait",
+                "04-25 17:18:08.445   312   366 E ActivityManager: ANR (application not responding) in process: com.android.package",
+                "04-25 17:18:08.445   312   366 E ActivityManager: Reason: keyDispatchingTimedOut",
+                "04-25 17:18:08.445   312   366 E ActivityManager: Load: 0.71 / 0.83 / 0.51",
+                "04-25 17:18:08.445   312   366 E ActivityManager: 33% TOTAL: 21% user + 11% kernel + 0.3% iowait",
+                "04-25 17:19:08.445   312   366 E ActivityManager: ANR (application not responding) in process: com.android.different.pacakge",
+                "04-25 17:19:08.445   312   366 E ActivityManager: Reason: keyDispatchingTimedOut",
+                "04-25 17:19:08.445   312   366 E ActivityManager: Load: 0.71 / 0.83 / 0.51",
+                "04-25 17:19:08.445   312   366 E ActivityManager: 33% TOTAL: 21% user + 11% kernel + 0.3% iowait",
+                "",
+                "------ VM TRACES AT LAST ANR (/data/anr/traces.txt: 2012-04-25 17:18:08) ------",
+                "",
+                "----- pid 2887 at 2012-04-25 17:17:08 -----",
+                "Cmd line: com.android.package",
+                "",
+                "DALVIK THREADS:",
+                "(mutexes: tll=0 tsl=0 tscl=0 ghl=0)",
+                "",
+                "\"main\" prio=5 tid=1 SUSPENDED",
+                "  | group=\"main\" sCount=1 dsCount=0 obj=0x00000001 self=0x00000001",
+                "  | sysTid=2887 nice=0 sched=0/0 cgrp=foreground handle=0000000001",
+                "  | schedstat=( 0 0 0 ) utm=5954 stm=1017 core=0",
+                "  at class.method1(Class.java:1)",
+                "  at class.method2(Class.java:2)",
+                "  at class.method2(Class.java:2)",
+                "",
+                "----- end 2887 -----",
+                "");
+
+        List<String> expectedStack = Arrays.asList(
+                "\"main\" prio=5 tid=1 SUSPENDED",
+                "  | group=\"main\" sCount=1 dsCount=0 obj=0x00000001 self=0x00000001",
+                "  | sysTid=2887 nice=0 sched=0/0 cgrp=foreground handle=0000000001",
+                "  | schedstat=( 0 0 0 ) utm=5954 stm=1017 core=0",
+                "  at class.method1(Class.java:1)",
+                "  at class.method2(Class.java:2)",
+                "  at class.method2(Class.java:2)");
+
+        BugreportItem bugreport = new BugreportParser().parse(lines);
+
+        assertNotNull(bugreport.getSystemLog());
+        assertEquals(3, bugreport.getSystemLog().getAnrs().size());
+        assertNull(bugreport.getSystemLog().getAnrs().get(0).getTrace());
+        assertEquals(ArrayUtil.join("\n", expectedStack),
+                bugreport.getSystemLog().getAnrs().get(1).getTrace());
+        assertNull(bugreport.getSystemLog().getAnrs().get(2).getTrace());
+    }
+
+    /**
+     * Test that the trace is set correctly if there is not traces file.
+     */
+    public void testSetAnrTrace_no_traces() {
+        List<String> lines = Arrays.asList(
+                "========================================================",
+                "== dumpstate: 2012-04-25 20:45:10",
+                "========================================================",
+                "------ SYSTEM LOG (logcat -v threadtime -d *:v) ------",
+                "04-25 17:17:08.445   312   366 E ActivityManager: ANR (application not responding) in process: com.android.package",
+                "04-25 17:17:08.445   312   366 E ActivityManager: Reason: keyDispatchingTimedOut",
+                "04-25 17:17:08.445   312   366 E ActivityManager: Load: 0.71 / 0.83 / 0.51",
+                "04-25 17:17:08.445   312   366 E ActivityManager: 33% TOTAL: 21% user + 11% kernel + 0.3% iowait",
+                "",
+                "*** NO ANR VM TRACES FILE (/data/anr/traces.txt): No such file or directory",
+                "");
+
+        BugreportItem bugreport = new BugreportParser().parse(lines);
+
+        assertNotNull(bugreport.getSystemLog());
+        assertEquals(1, bugreport.getSystemLog().getAnrs().size());
+        assertNull(bugreport.getSystemLog().getAnrs().get(0).getTrace());
+    }
+
     private Date parseTime(String timeStr) throws ParseException {
         DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
         return formatter.parse(timeStr);
diff --git a/tests/src/com/android/tradefed/util/brillopad/MonkeyLogParserTest.java b/tests/src/com/android/tradefed/util/brillopad/MonkeyLogParserTest.java
index ee70999..3f81812 100644
--- a/tests/src/com/android/tradefed/util/brillopad/MonkeyLogParserTest.java
+++ b/tests/src/com/android/tradefed/util/brillopad/MonkeyLogParserTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.tradefed.util.brillopad;
 
+import com.android.tradefed.util.ArrayUtil;
 import com.android.tradefed.util.brillopad.item.AnrItem;
 import com.android.tradefed.util.brillopad.item.JavaCrashItem;
 import com.android.tradefed.util.brillopad.item.MonkeyLogItem;
@@ -133,6 +134,27 @@
                 "  10% 120/mediaserver: 6.8% user + 3.6% kernel / faults: 1189 minor",
                 "34% TOTAL: 19% user + 13% kernel + 0.2% iowait + 1% softirq",
                 "",
+                "procrank:",
+                "// procrank status was 0",
+                "anr traces:",
+                "",
+                "",
+                "----- pid 2887 at 2012-04-25 17:17:08 -----",
+                "Cmd line: com.google.android.youtube",
+                "",
+                "DALVIK THREADS:",
+                "(mutexes: tll=0 tsl=0 tscl=0 ghl=0)",
+                "",
+                "\"main\" prio=5 tid=1 SUSPENDED",
+                "  | group=\"main\" sCount=1 dsCount=0 obj=0x00000001 self=0x00000001",
+                "  | sysTid=2887 nice=0 sched=0/0 cgrp=foreground handle=0000000001",
+                "  | schedstat=( 0 0 0 ) utm=5954 stm=1017 core=0",
+                "  at class.method1(Class.java:1)",
+                "  at class.method2(Class.java:2)",
+                "  at class.method2(Class.java:2)",
+                "",
+                "----- end 2887 -----",
+                "// anr traces status was 0",
                 "** Monkey aborted due to error.",
                 "Events injected: 5322",
                 ":Sending rotation degree=0, persist=false",
@@ -145,6 +167,15 @@
                 "----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------",
                 "");
 
+        List<String> expectedStack = Arrays.asList(
+                "\"main\" prio=5 tid=1 SUSPENDED",
+                "  | group=\"main\" sCount=1 dsCount=0 obj=0x00000001 self=0x00000001",
+                "  | sysTid=2887 nice=0 sched=0/0 cgrp=foreground handle=0000000001",
+                "  | schedstat=( 0 0 0 ) utm=5954 stm=1017 core=0",
+                "  at class.method1(Class.java:1)",
+                "  at class.method2(Class.java:2)",
+                "  at class.method2(Class.java:2)");
+
         MonkeyLogItem monkeyLog = new MonkeyLogParser().parse(lines);
         assertNotNull(monkeyLog);
         assertEquals(parseTime("2012-04-24 17:23:30"), monkeyLog.getStartTime());
@@ -168,6 +199,8 @@
         assertEquals("com.google.android.youtube", monkeyLog.getCrash().getApp());
         assertEquals(3301, monkeyLog.getCrash().getPid().intValue());
         assertEquals("keyDispatchingTimedOut", ((AnrItem) monkeyLog.getCrash()).getReason());
+        assertEquals(ArrayUtil.join("\n", expectedStack),
+                ((AnrItem) monkeyLog.getCrash()).getTrace());
     }
 
     /**
diff --git a/tests/src/com/android/tradefed/util/brillopad/TracesParserTest.java b/tests/src/com/android/tradefed/util/brillopad/TracesParserTest.java
new file mode 100644
index 0000000..60edccf
--- /dev/null
+++ b/tests/src/com/android/tradefed/util/brillopad/TracesParserTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2011 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.brillopad;
+
+import com.android.tradefed.util.ArrayUtil;
+import com.android.tradefed.util.brillopad.item.TracesItem;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link TracesParser}
+ */
+public class TracesParserTest extends TestCase {
+
+    /**
+     * Test that the parser parses the correct stack.
+     */
+    public void testTracesParser() {
+        List<String> lines = Arrays.asList(
+                "",
+                "",
+                "----- pid 2887 at 2012-05-02 16:43:41 -----",
+                "Cmd line: com.android.package",
+                "",
+                "DALVIK THREADS:",
+                "(mutexes: tll=0 tsl=0 tscl=0 ghl=0)",
+                "",
+                "\"main\" prio=5 tid=1 SUSPENDED",
+                "  | group=\"main\" sCount=1 dsCount=0 obj=0x00000001 self=0x00000001",
+                "  | sysTid=2887 nice=0 sched=0/0 cgrp=foreground handle=0000000001",
+                "  | schedstat=( 0 0 0 ) utm=5954 stm=1017 core=0",
+                "  at class.method1(Class.java:1)",
+                "  at class.method2(Class.java:2)",
+                "  at class.method2(Class.java:2)",
+                "",
+                "\"Task_1\" prio=5 tid=27 WAIT",
+                "  | group=\"main\" sCount=1 dsCount=0 obj=0x00000001 self=0x00000001",
+                "  | sysTid=4789 nice=10 sched=0/0 cgrp=bg_non_interactive handle=0000000001",
+                "  | schedstat=( 0 0 0 ) utm=0 stm=0 core=0",
+                "  at class.method1(Class.java:1)",
+                "  - waiting on <0x00000001> (a java.lang.Thread) held by tid=27 (Task_1)",
+                "  at class.method2(Class.java:2)",
+                "  at class.method2(Class.java:2)",
+                "",
+                "\"Task_2\" prio=5 tid=26 NATIVE",
+                "  | group=\"main\" sCount=1 dsCount=0 obj=0x00000001 self=0x00000001",
+                "  | sysTid=4343 nice=0 sched=0/0 cgrp=foreground handle=0000000001",
+                "  | schedstat=( 0 0 0 ) utm=6 stm=3 core=0",
+                "  #00  pc 00001234  /system/lib/lib.so (addr+8)",
+                "  #01  pc 00001235  /system/lib/lib.so (addr+16)",
+                "  #02  pc 00001236  /system/lib/lib.so (addr+24)",
+                "  at class.method1(Class.java:1)",
+                "",
+                "----- end 2887 -----",
+                "",
+                "",
+                "----- pid 256 at 2012-05-02 16:43:41 -----",
+                "Cmd line: system",
+                "",
+                "DALVIK THREADS:",
+                "(mutexes: tll=0 tsl=0 tscl=0 ghl=0)",
+                "",
+                "\"main\" prio=5 tid=1 NATIVE",
+                "  | group=\"main\" sCount=1 dsCount=0 obj=0x00000001 self=0x00000001",
+                "  | sysTid=256 nice=0 sched=0/0 cgrp=foreground handle=0000000001",
+                "  | schedstat=( 0 0 0 ) utm=175 stm=41 core=0",
+                "  #00  pc 00001234  /system/lib/lib.so (addr+8)",
+                "  #01  pc 00001235  /system/lib/lib.so (addr+16)",
+                "  #02  pc 00001236  /system/lib/lib.so (addr+24)",
+                "  at class.method1(Class.java:1)",
+                "  at class.method2(Class.java:2)",
+                "  at class.method2(Class.java:2)",
+                "",
+                "----- end 256 -----",
+                "");
+
+        List<String> expectedStack = Arrays.asList(
+                "\"main\" prio=5 tid=1 SUSPENDED",
+                "  | group=\"main\" sCount=1 dsCount=0 obj=0x00000001 self=0x00000001",
+                "  | sysTid=2887 nice=0 sched=0/0 cgrp=foreground handle=0000000001",
+                "  | schedstat=( 0 0 0 ) utm=5954 stm=1017 core=0",
+                "  at class.method1(Class.java:1)",
+                "  at class.method2(Class.java:2)",
+                "  at class.method2(Class.java:2)");
+
+        TracesItem traces = new TracesParser().parse(lines);
+        assertEquals(2887, traces.getPid().intValue());
+        assertEquals("com.android.package", traces.getApp());
+        assertEquals(ArrayUtil.join("\n", expectedStack), traces.getStack());
+    }
+}