Modify LogcatParser to use new utils.

Change-Id: Ib7df6364179123eaaec19ceb2f2e3024550f18de
diff --git a/src/com/android/loganalysis/item/GenericLogcatItem.java b/src/com/android/loganalysis/item/GenericLogcatItem.java
index 4a3642d..4ca5243 100644
--- a/src/com/android/loganalysis/item/GenericLogcatItem.java
+++ b/src/com/android/loganalysis/item/GenericLogcatItem.java
@@ -102,28 +102,28 @@
     }
 
     /**
-     * Get the last preamble for of the event.
+     * Get the last preamble for the event.
      */
     public String getLastPreamble() {
         return (String) getAttribute(LAST_PREAMBLE);
     }
 
     /**
-     * Set the last preamble for of the event.
+     * Set the last preamble for the event.
      */
     public void setLastPreamble(String preamble) {
         setAttribute(LAST_PREAMBLE, preamble);
     }
 
     /**
-     * Get the process preamble for of the event.
+     * Get the process preamble for the event.
      */
     public String getProcessPreamble() {
         return (String) getAttribute(PROC_PREAMBLE);
     }
 
     /**
-     * Set the process preamble for of the event.
+     * Set the process preamble for the event.
      */
     public void setProcessPreamble(String preamble) {
         setAttribute(PROC_PREAMBLE, preamble);
diff --git a/src/com/android/loganalysis/item/LogcatItem.java b/src/com/android/loganalysis/item/LogcatItem.java
index 7bf42a2..883773a 100644
--- a/src/com/android/loganalysis/item/LogcatItem.java
+++ b/src/com/android/loganalysis/item/LogcatItem.java
@@ -128,4 +128,18 @@
         }
         return ncs;
     }
+
+    /**
+     * Get the list of all {@link MiscLogcatItem} events for a cateogry.
+     */
+    public List<MiscLogcatItem> getMiscEvents(String category) {
+        List<MiscLogcatItem> items = new LinkedList<MiscLogcatItem>();
+        for (IItem item : getEvents()) {
+            if (item instanceof MiscLogcatItem &&
+                    ((MiscLogcatItem) item).getCategory().equals(category)) {
+                items.add((MiscLogcatItem) item);
+            }
+        }
+        return items;
+    }
 }
diff --git a/src/com/android/loganalysis/item/MiscLogcatItem.java b/src/com/android/loganalysis/item/MiscLogcatItem.java
new file mode 100644
index 0000000..c835bb9
--- /dev/null
+++ b/src/com/android/loganalysis/item/MiscLogcatItem.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 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.loganalysis.item;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An {@link IItem} used to store miscellaneous logcat info.
+ */
+public class MiscLogcatItem extends GenericLogcatItem {
+    public static final String TYPE = "MISC LOGCAT";
+
+    private static final String CATEGORY = "CATEGORY";
+    private static final String MESSAGE = "MESSAGE";
+
+    private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
+            CATEGORY, MESSAGE));
+
+    /**
+     * The constructor for {@link MiscLogcatItem}.
+     */
+    public MiscLogcatItem() {
+        super(TYPE, ATTRIBUTES);
+    }
+
+    /**
+     * Get the category of the event.
+     */
+    public String getCategory() {
+        return (String) getAttribute(CATEGORY);
+    }
+
+    /**
+     * Set the category of the event.
+     */
+    public void setCategory(String category) {
+        setAttribute(CATEGORY, category);
+    }
+
+    /**
+     * Get the message for the event.
+     */
+    public String getMessage() {
+        return (String) getAttribute(MESSAGE);
+    }
+
+    /**
+     * Set the message for the event.
+     */
+    public void setMessage(String message) {
+        setAttribute(MESSAGE, message);
+    }
+}
diff --git a/src/com/android/loganalysis/parser/LogcatParser.java b/src/com/android/loganalysis/parser/LogcatParser.java
index 9edbede..88f029a 100644
--- a/src/com/android/loganalysis/parser/LogcatParser.java
+++ b/src/com/android/loganalysis/parser/LogcatParser.java
@@ -17,7 +17,10 @@
 
 import com.android.loganalysis.item.GenericLogcatItem;
 import com.android.loganalysis.item.LogcatItem;
+import com.android.loganalysis.item.MiscLogcatItem;
 import com.android.loganalysis.util.ArrayUtil;
+import com.android.loganalysis.util.LogPatternUtil;
+import com.android.loganalysis.util.LogTailUtil;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -28,7 +31,6 @@
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.ListIterator;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -43,6 +45,9 @@
  * </p>
  */
 public class LogcatParser implements IParser {
+    public static final String HIGH_CPU_USAGE = "HIGH_CPU_USAGE";
+    public static final String HIGH_MEMORY_USAGE = "HIGH_MEMORY_USAGE";
+    public static final String RUNTIME_RESTART = "RUNTIME_RESTART";
 
     /**
      * Match a single line of `logcat -v threadtime`, such as:
@@ -86,11 +91,9 @@
         }
     }
 
-    private static final int MAX_BUFF_SIZE = 500;
-    private static final int MAX_LAST_PREAMBLE_SIZE = 15;
-    private static final int MAX_PROC_PREAMBLE_SIZE = 15;
+    private LogPatternUtil mPatternUtil = new LogPatternUtil();
+    private LogTailUtil mPreambleUtil = new LogTailUtil();
 
-    private LinkedList<String> mRingBuffer = new LinkedList<String>();
     private String mYear = null;
 
     LogcatItem mLogcat = new LogcatItem();
@@ -105,6 +108,7 @@
      * Constructor for {@link LogcatParser}.
      */
     public LogcatParser() {
+        initPatterns();
     }
 
     /**
@@ -113,6 +117,7 @@
      * @param year The year as a string.
      */
     public LogcatParser(String year) {
+        this();
         setYear(year);
     }
 
@@ -201,8 +206,8 @@
             String key = encodeLine(pid, tid, level, tag);
             LogcatData data;
             if (!mDataMap.containsKey(key) || AnrParser.START.matcher(msg).matches()) {
-                data = new LogcatData(pid, tid, time, level, tag, getLastPreamble(),
-                        getProcPreamble(pid));
+                data = new LogcatData(pid, tid, time, level, tag, mPreambleUtil.getLastTail(),
+                        mPreambleUtil.getIdTail(pid));
                 mDataMap.put(key, data);
                 mDataList.add(data);
             } else {
@@ -217,8 +222,8 @@
             String key = encodeLine(pid, tid, level, tag);
             LogcatData data;
             if (!mDataMap.containsKey(key)) {
-                data = new LogcatData(pid, tid, time, level, tag, getLastPreamble(),
-                        getProcPreamble(pid));
+                data = new LogcatData(pid, tid, time, level, tag, mPreambleUtil.getLastTail(),
+                        mPreambleUtil.getIdTail(pid));
                 mDataMap.put(key, data);
                 mDataList.add(data);
             } else {
@@ -227,11 +232,16 @@
             data.mLines.add(msg);
         }
 
-        // After parsing the line, add it the the buffer for the preambles.
-        mRingBuffer.add(line);
-        if (mRingBuffer.size() > MAX_BUFF_SIZE) {
-            mRingBuffer.removeFirst();
+        // Check the message here but add it in commit()
+        if (mPatternUtil.checkMessage(msg) != null) {
+            LogcatData data = new LogcatData(pid, tid, time, level, tag,
+                    mPreambleUtil.getLastTail(), mPreambleUtil.getIdTail(pid));
+            data.mLines.add(msg);
+            mDataList.add(data);
         }
+
+        // After parsing the line, add it the the buffer for the preambles.
+        mPreambleUtil.addLine(pid, line);
     }
 
     /**
@@ -249,6 +259,15 @@
             } else if ("I".equals(data.mLevel) && "DEBUG".equals(data.mTag)) {
                 // CLog.v("Parsing native crash: %s", data.mLines);
                 item = new NativeCrashParser().parse(data.mLines);
+            } else {
+                String msg = ArrayUtil.join("\n", data.mLines);
+                String category = mPatternUtil.checkMessage(msg);
+                if (category != null) {
+                    MiscLogcatItem logcatItem = new MiscLogcatItem();
+                    logcatItem.setCategory(category);
+                    logcatItem.setMessage(msg);
+                    item = logcatItem;
+                }
             }
             if (item != null) {
                 item.setEventTime(data.mTime);
@@ -298,55 +317,17 @@
         }
     }
 
-    /**
-     * Get the last {@value #MAX_LAST_PREAMBLE_SIZE} lines of logcat.
-     */
-    private String getLastPreamble() {
-        final int size = mRingBuffer.size();
-        List<String> preamble;
-        if (size > getLastPreambleSize()) {
-            preamble = mRingBuffer.subList(size - getLastPreambleSize(), size);
-        } else {
-            preamble = mRingBuffer;
-        }
-        return ArrayUtil.join("\n", preamble).trim();
-    }
+    private void initPatterns() {
+        // High CPU usage
+        mPatternUtil.addPattern(Pattern.compile(".* timed out \\(is the CPU pegged\\?\\).*"),
+                HIGH_CPU_USAGE);
 
-    /**
-     * Get the last {@value #MAX_PROC_PREAMBLE_SIZE} lines of logcat which match the given pid.
-     */
-    private String getProcPreamble(int pid) {
-        LinkedList<String> preamble = new LinkedList<String>();
+        // High memory usage
+        mPatternUtil.addPattern(Pattern.compile(
+                "GetBufferLock timed out for thread \\d+ buffer .*"), HIGH_MEMORY_USAGE);
 
-        ListIterator<String> li = mRingBuffer.listIterator(mRingBuffer.size());
-        while (li.hasPrevious()) {
-            String line = li.previous();
-
-            Matcher m = THREADTIME_LINE.matcher(line);
-            Matcher tm = TIME_LINE.matcher(line);
-            if ((m.matches() && pid == Integer.parseInt(m.group(2))) ||
-                    (tm.matches() && pid == Integer.parseInt(tm.group(4)))) {
-                preamble.addFirst(line);
-            }
-
-            if (preamble.size() == getProcPreambleSize()) {
-                return ArrayUtil.join("\n", preamble).trim();
-            }
-        }
-        return ArrayUtil.join("\n", preamble).trim();
-    }
-
-    /**
-     * Get the number of lines in the last preamble. Exposed for unit testing.
-     */
-    int getLastPreambleSize() {
-        return MAX_LAST_PREAMBLE_SIZE;
-    }
-
-    /**
-     * Get the number of lines in the process preamble. Exposed for unit testing.
-     */
-    int getProcPreambleSize() {
-        return MAX_PROC_PREAMBLE_SIZE;
+        // Runtime restarts
+        mPatternUtil.addPattern(Pattern.compile("\\*\\*\\* WATCHDOG KILLING SYSTEM PROCESS.*"),
+                RUNTIME_RESTART);
     }
 }
diff --git a/tests/src/com/android/loganalysis/parser/LogcatParserTest.java b/tests/src/com/android/loganalysis/parser/LogcatParserTest.java
index 37a39b6..0ac0338 100644
--- a/tests/src/com/android/loganalysis/parser/LogcatParserTest.java
+++ b/tests/src/com/android/loganalysis/parser/LogcatParserTest.java
@@ -16,6 +16,7 @@
 package com.android.loganalysis.parser;
 
 import com.android.loganalysis.item.LogcatItem;
+import com.android.loganalysis.item.MiscLogcatItem;
 import com.android.loganalysis.util.ArrayUtil;
 
 import junit.framework.TestCase;
@@ -103,6 +104,40 @@
                 logcat.getNativeCrashes().get(0).getEventTime());
     }
 
+    public void testParse_misc_events() throws ParseException {
+        List<String> lines = Arrays.asList(
+                "04-25 18:33:27.273  1676  1821 W AudioTrack: obtainBuffer timed out (is the CPU pegged?) 0x361378 user=0000116a, server=00000000",
+                "04-25 18:33:28.273  7813  7813 E gralloc : GetBufferLock timed out for thread 7813 buffer 0x61 usage 0x200 LockState 1",
+                "04-25 18:33:29.273   395   637 W Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: null");
+
+        LogcatItem logcat = new LogcatParser("2012").parse(lines);
+        assertNotNull(logcat);
+        assertEquals(parseTime("2012-04-25 18:33:27.273"), logcat.getStartTime());
+        assertEquals(parseTime("2012-04-25 18:33:29.273"), logcat.getStopTime());
+        assertEquals(3, logcat.getEvents().size());
+        assertEquals(1, logcat.getMiscEvents(LogcatParser.HIGH_CPU_USAGE).size());
+        assertEquals(1, logcat.getMiscEvents(LogcatParser.HIGH_MEMORY_USAGE).size());
+        assertEquals(1, logcat.getMiscEvents(LogcatParser.RUNTIME_RESTART).size());
+
+        MiscLogcatItem item = logcat.getMiscEvents(LogcatParser.HIGH_CPU_USAGE).get(0);
+
+        assertEquals(1676, item.getPid().intValue());
+        assertEquals(1821, item.getTid().intValue());
+        assertEquals(parseTime("2012-04-25 18:33:27.273"), item.getEventTime());
+
+        item = logcat.getMiscEvents(LogcatParser.HIGH_MEMORY_USAGE).get(0);
+
+        assertEquals(7813, item.getPid().intValue());
+        assertEquals(7813, item.getTid().intValue());
+        assertEquals(parseTime("2012-04-25 18:33:28.273"), item.getEventTime());
+
+        item = logcat.getMiscEvents(LogcatParser.RUNTIME_RESTART).get(0);
+
+        assertEquals(395, item.getPid().intValue());
+        assertEquals(637, item.getTid().intValue());
+        assertEquals(parseTime("2012-04-25 18:33:29.273"), item.getEventTime());
+    }
+
     /**
      * Test that multiple events can be parsed.
      */
@@ -225,9 +260,9 @@
     }
 
     /**
-     * Test that the preambles are set correctly if there's only partial preambles.
+     * Test that the preambles are set correctly.
      */
-    public void testParse_partial_preambles() throws ParseException {
+    public void testParse_preambles() throws ParseException {
         List<String> lines = Arrays.asList(
                 "04-25 09:15:47.799   123  3082 I tag: message 1",
                 "04-25 09:20:47.799  3064  3082 I tag: message 2",
@@ -275,61 +310,6 @@
     }
 
     /**
-     * Test that the preambles are set correctly if there's only full preambles.
-     */
-    public void testParse_preambles() throws ParseException {
-        List<String> lines = Arrays.asList(
-                "04-25 09:43:47.799  3064  3082 I tag: message 1",
-                "04-25 09:44:47.799   123  3082 I tag: message 2",
-                "04-25 09:45:47.799  3064  3082 I tag: message 3",
-                "04-25 09:46:47.799   234  3082 I tag: message 4",
-                "04-25 09:47:47.799  3064  3082 I tag: message 5",
-                "04-25 09:48:47.799   345  3082 I tag: message 6",
-                "04-25 09:49:47.799  3064  3082 I tag: message 7",
-                "04-25 09:50:47.799   456  3082 I tag: message 8",
-                "04-25 09:55:47.799  3064  3082 E AndroidRuntime: java.lang.Exception",
-                "04-25 09:55:47.799  3064  3082 E AndroidRuntime: \tat class.method1(Class.java:1)",
-                "04-25 09:55:47.799  3064  3082 E AndroidRuntime: \tat class.method2(Class.java:2)",
-                "04-25 09:55:47.799  3064  3082 E AndroidRuntime: \tat class.method3(Class.java:3)");
-
-        List<String> expectedLastPreamble = Arrays.asList(
-                "04-25 09:48:47.799   345  3082 I tag: message 6",
-                "04-25 09:49:47.799  3064  3082 I tag: message 7",
-                "04-25 09:50:47.799   456  3082 I tag: message 8");
-
-        List<String> expectedProcPreamble = Arrays.asList(
-                "04-25 09:45:47.799  3064  3082 I tag: message 3",
-                "04-25 09:47:47.799  3064  3082 I tag: message 5",
-                "04-25 09:49:47.799  3064  3082 I tag: message 7");
-
-        LogcatItem logcat = new LogcatParser("2012") {
-            @Override
-            int getLastPreambleSize() {
-                return 3;
-            }
-
-            @Override
-            int getProcPreambleSize() {
-                return 3;
-            }
-        }.parse(lines);
-
-        assertNotNull(logcat);
-        assertEquals(parseTime("2012-04-25 09:43:47.799"), logcat.getStartTime());
-        assertEquals(parseTime("2012-04-25 09:55:47.799"), logcat.getStopTime());
-        assertEquals(1, logcat.getEvents().size());
-        assertEquals(1, logcat.getJavaCrashes().size());
-        assertEquals(3064, logcat.getJavaCrashes().get(0).getPid().intValue());
-        assertEquals(3082, logcat.getJavaCrashes().get(0).getTid().intValue());
-        assertEquals(ArrayUtil.join("\n", expectedLastPreamble),
-                logcat.getJavaCrashes().get(0).getLastPreamble());
-        assertEquals(ArrayUtil.join("\n", expectedProcPreamble),
-                logcat.getJavaCrashes().get(0).getProcessPreamble());
-        assertEquals(parseTime("2012-04-25 09:55:47.799"),
-                logcat.getJavaCrashes().get(0).getEventTime());
-    }
-
-    /**
      * Test that the time logcat format can be parsed.
      */
     public void testParse_time() throws ParseException {