Create a hierarchical Brillopad parser for Android's bugreport format
Currently includes parsers for the following Bugreport sections:
Memory Info
Proc Rank
System Log (picks out ANRs, Java crashes, and Native crashes)
System Properties
It's been end-to-end tested against bugreports from a variety of
current devices, as well as an ANR bugreport from kila (Dream) running
Donut Burger build 3890, circa June, 2009.
Change-Id: I08d56aaab01278c5e52c2b9cf6e4c84b6fb2cebe
diff --git a/src/com/android/tradefed/util/brillopad/AbstractBlockParser.java b/src/com/android/tradefed/util/brillopad/AbstractBlockParser.java
new file mode 100644
index 0000000..30a8fc0
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/AbstractBlockParser.java
@@ -0,0 +1,52 @@
+/*
+ * 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.log.LogUtil.CLog;
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.ILineParser;
+
+import java.util.List;
+
+/**
+ * A shim class that implements IBlockParser by calling ILineParser methods
+ */
+public abstract class AbstractBlockParser implements IBlockParser, ILineParser {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parseBlock(List<String> input, ItemList itemlist) {
+ for (String line : input) {
+ if (line == null) {
+ CLog.w("Encountered unexpected null line; skipping...");
+ continue;
+ }
+ parseLine(line, itemlist);
+ }
+
+ // signal EOF
+ commit(itemlist);
+ }
+
+ /**
+ * Parses a single {@link String}
+ *
+ * @param line
+ */
+ abstract public void parseLine(String line, ItemList itemlist);
+}
diff --git a/src/com/android/tradefed/util/brillopad/AbstractParser.java b/src/com/android/tradefed/util/brillopad/AbstractParser.java
deleted file mode 100644
index 12b9f0b..0000000
--- a/src/com/android/tradefed/util/brillopad/AbstractParser.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 java.io.BufferedReader;
-import java.io.IOException;
-
-/**
- * Abstract parser to implement common methods of all parsers.
- */
-public abstract class AbstractParser implements IParser {
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void parse(BufferedReader input) throws IOException {
- String line;
- while ((line = input.readLine()) != null) {
- parse(line);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void parse(String[] input) throws IOException {
- for (String line : input) {
- if (line == null) {
- throw new IOException("Encountered a null line");
- }
- parse(line);
- }
- }
-}
diff --git a/src/com/android/tradefed/util/brillopad/BugreportParser.java b/src/com/android/tradefed/util/brillopad/BugreportParser.java
new file mode 100644
index 0000000..b39025e
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/BugreportParser.java
@@ -0,0 +1,69 @@
+/*
+ * 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.RegexTrie;
+import com.android.tradefed.util.brillopad.IBlockParser;
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.section.AbstractSectionParser;
+import com.android.tradefed.util.brillopad.section.MemInfoParser;
+import com.android.tradefed.util.brillopad.section.NoopSectionParser;
+import com.android.tradefed.util.brillopad.section.ProcRankParser;
+import com.android.tradefed.util.brillopad.section.SystemLogParser;
+import com.android.tradefed.util.brillopad.section.SystemPropParser;
+import com.android.tradefed.util.brillopad.section.syslog.AnrParser;
+import com.android.tradefed.util.brillopad.section.syslog.JavaCrashParser;
+import com.android.tradefed.util.brillopad.section.syslog.NativeCrashParser;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+
+/**
+ * A brillopad parser that understands the Android bugreport format
+ */
+public class BugreportParser extends AbstractSectionParser {
+ // For convenience, since these will likely be the most common events that people are
+ // interested in.
+ public static final String ANR = AnrParser.SECTION_NAME;
+ public static final String JAVA_CRASH = JavaCrashParser.SECTION_NAME;
+ public static final String NATIVE_CRASH = NativeCrashParser.SECTION_NAME;
+
+ /**
+ * Parse a bugreport file into a {@link ItemList} object. This is the entrance method to the
+ * parser.
+ */
+ public void parse(BufferedReader input, ItemList itemlist) throws IOException {
+ String line;
+ while ((line = input.readLine()) != null) {
+ parseLine(line, itemlist);
+ }
+
+ // signal EOF
+ commit(itemlist);
+ }
+
+ @Override
+ public void addDefaultSectionParsers(RegexTrie<IBlockParser> sectionTrie) {
+ sectionTrie.put(new MemInfoParser(), MemInfoParser.SECTION_REGEX);
+ sectionTrie.put(new ProcRankParser(), ProcRankParser.SECTION_REGEX);
+ sectionTrie.put(new SystemPropParser(), SystemPropParser.SECTION_REGEX);
+ sectionTrie.put(new SystemLogParser(), SystemLogParser.SECTION_REGEX);
+
+ // Add a default section parser so that the Trie will commit prior sections
+ sectionTrie.put(new NoopSectionParser(), NoopSectionParser.SECTION_REGEX);
+ }
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/IBlockParser.java b/src/com/android/tradefed/util/brillopad/IBlockParser.java
new file mode 100644
index 0000000..f4a0b7e
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/IBlockParser.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+// note: import used for javadoc
+import com.android.tradefed.util.brillopad.item.IItem;
+
+import java.util.List;
+
+/**
+ * This interface defines the behavior for a block-oriented parser. The parser will receive a block
+ * of input that it should consider complete. It should do whatever is necessary to parse the input
+ * and commit the parsed data as arbitrarily many {@link IItem} instances in the passed
+ * {@link ItemList}. Furthermore, the parser should be robust against invalid input -- the input
+ * format may drift over time.
+ */
+public interface IBlockParser {
+
+ /**
+ * Parses a block of input, and stores the parsed {@link IItem}s in the passed {@link ItemList}.
+ * <p/>
+ * This method will be called at most once per parser instance.
+ */
+ public void parseBlock(List<String> input, ItemList itemlist);
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/ILineParser.java b/src/com/android/tradefed/util/brillopad/ILineParser.java
new file mode 100644
index 0000000..bba94f1
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/ILineParser.java
@@ -0,0 +1,47 @@
+/*
+ * 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.ItemList;
+
+import java.util.List;
+
+/**
+ * This interface defines the behavior for a line-oriented parser. The parser should parse one
+ * line at a time, keeping state internally or stashing it in the {@link ItemList} as desired, and
+ * should interpret a {@code commit} call to indicate the end of input.
+ */
+public interface ILineParser {
+ /**
+ * parseList should take a line of input, do whatever is needed to parse it, and then either
+ * store the results in internal state or commit them to the passed {@link ItemList}.
+ * <p />
+ * The parser should robustly handle parse errors and make a best effort to return as much
+ * useful data as possible.
+ *
+ * @param line the line to parse
+ * @param itemlist the itemlist in which to store any permanent state
+ */
+ public void parseLine(String line, ItemList itemlist);
+
+ /**
+ * A signal that input is finished.
+ *
+ * @param itemlist the itemlist in which to store any permanent state
+ */
+ public void commit(ItemList itemlist);
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/IParser.java b/src/com/android/tradefed/util/brillopad/IParser.java
deleted file mode 100644
index 37e2f70..0000000
--- a/src/com/android/tradefed/util/brillopad/IParser.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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 java.io.BufferedReader;
-import java.io.IOException;
-
-/**
- * Interface for all parsers.
- *
- * <p>
- * Parsers are meant to be used only once and contain state. This means that an input can be split
- * up and parsed by the appropriate parse method without any affect on the parsing. For example,
- * a buffered reader containing 10 lines can be broken into 10 individual lines with
- * {@link IParser#parse(String)} being run 10 times and that will produce the same state as
- * {@link IParser#parse(BufferedReader)} being run once with the original buffered reader.
- * </p>
- */
-public interface IParser {
-
- /**
- * Parses an {@link java.io.BufferedReader} object.
- *
- * @param input The {@link java.io.BufferedReader} object
- * @throws IOException If there was a problem reading the Buffered reader
- */
- public void parse(BufferedReader input) throws IOException;
-
- /**
- * Parsers an array of {@link java.lang.String} objects.
- *
- * @param input The {@link java.lang.String} array.
- * @throws IOException If any of the strings are null.
- */
- public void parse(String[] input) throws IOException;
-
- /**
- * Parses a single {@link java.lang.String}
- *
- * @param line
- */
- public void parse(String line);
-}
diff --git a/src/com/android/tradefed/util/brillopad/ItemList.java b/src/com/android/tradefed/util/brillopad/ItemList.java
new file mode 100644
index 0000000..fcfd8e8
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/ItemList.java
@@ -0,0 +1,73 @@
+/*
+ * 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.IItem;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A class to represent the contents of an Android bugreport
+ */
+public class ItemList {
+
+ private List<IItem> mItems = new LinkedList<IItem>();
+
+ public void addItem(IItem item) {
+ if (item == null) {
+ throw new NullPointerException();
+ }
+ mItems.add(item);
+ }
+
+ public List<IItem> getItems() {
+ return mItems;
+ }
+
+ public List<IItem> getByType(String regex) {
+ return getByType(Pattern.compile(regex));
+ }
+
+ public List<IItem> getByType(Pattern filter) {
+ // FIXME: implement this in a way that takes sublinear time
+ List<IItem> results = new LinkedList<IItem>();
+
+ for (IItem item : mItems) {
+ String section = item.getType();
+ if (section == null) {
+ continue;
+ }
+ Matcher m = filter.matcher(section);
+ if (m.matches()) {
+ results.add(item);
+ }
+ }
+
+ return results;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ return mItems.toString();
+ }
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/item/AbstractItem.java b/src/com/android/tradefed/util/brillopad/item/AbstractItem.java
index dc0ea21..a3b1565 100644
--- a/src/com/android/tradefed/util/brillopad/item/AbstractItem.java
+++ b/src/com/android/tradefed/util/brillopad/item/AbstractItem.java
@@ -70,6 +70,14 @@
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getType() {
+ return null;
+ }
+
+ /**
* {@inhertiDoc}
*/
@Override
diff --git a/src/com/android/tradefed/util/brillopad/item/GenericMapItem.java b/src/com/android/tradefed/util/brillopad/item/GenericMapItem.java
new file mode 100644
index 0000000..8ad1008
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/item/GenericMapItem.java
@@ -0,0 +1,66 @@
+/*
+ * 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.HashMap;
+import java.util.Map;
+
+/**
+ * An IItem that just represents a simple key/value map
+ */
+public class GenericMapItem<K, V> extends HashMap<K,V> implements IItem {
+ private String mType = null;
+
+ /**
+ * No-op zero-arg constructor
+ */
+ public GenericMapItem() {}
+
+ /**
+ * Convenience constructor that sets the type
+ */
+ public GenericMapItem(String type) {
+ setType(type);
+ }
+
+ /**
+ * Set the self-reported type that this {@link GenericMapItem} represents.
+ */
+ public void setType(String type) {
+ mType = type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getType() {
+ return mType;
+ }
+
+ @Override
+ public IItem merge(IItem other) throws ConflictingItemException {
+ // FIXME
+ return (IItem)this;
+ }
+
+ @Override
+ public boolean isConsistent(IItem other) {
+ // FIXME
+ return true;
+ }
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/item/IItem.java b/src/com/android/tradefed/util/brillopad/item/IItem.java
index d64514c..47bd71a 100644
--- a/src/com/android/tradefed/util/brillopad/item/IItem.java
+++ b/src/com/android/tradefed/util/brillopad/item/IItem.java
@@ -17,15 +17,15 @@
/**
* Interface for all items that are created by any parser.
- *
- * <p>
- * Items may contain one or more items. For example, a Bugreport item will contain a Logcat item,
- * which itself might contain ANR, JavaCrash, and NativeCrash items.
- * </p>
*/
public interface IItem {
/**
+ * Determine what type this IItem represents. May return {@code null}
+ */
+ public String getType();
+
+ /**
* Merges the item and another into an item with the most complete information.
*
* <p>
diff --git a/src/com/android/tradefed/util/brillopad/item/NativeCrash.java b/src/com/android/tradefed/util/brillopad/item/NativeCrash.java
index d144133..dd823dc 100644
--- a/src/com/android/tradefed/util/brillopad/item/NativeCrash.java
+++ b/src/com/android/tradefed/util/brillopad/item/NativeCrash.java
@@ -21,7 +21,7 @@
* Item to hold information associated with native crashes.
*/
public final class NativeCrash extends AbstractLogcatItem {
- private static final String[] ALLOWED_ATTRIBUTES = {"stack"};
+ private static final String[] ALLOWED_ATTRIBUTES = {"stack", "fingerprint", "app"};
public NativeCrash() {
super(ALLOWED_ATTRIBUTES);
diff --git a/src/com/android/tradefed/util/brillopad/section/AbstractSectionParser.java b/src/com/android/tradefed/util/brillopad/section/AbstractSectionParser.java
new file mode 100644
index 0000000..54a538d
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/section/AbstractSectionParser.java
@@ -0,0 +1,125 @@
+/*
+ * 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.section;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.RegexTrie;
+import com.android.tradefed.util.brillopad.AbstractBlockParser;
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.IBlockParser;
+import com.android.tradefed.util.brillopad.ILineParser;
+import com.android.tradefed.util.brillopad.section.NoopSectionParser;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A line-oriented parser that splits an input file into discrete sections and passes each section
+ * to an {@link IBlockParser} to parse.
+ */
+public abstract class AbstractSectionParser extends AbstractBlockParser implements ILineParser {
+ private RegexTrie<IBlockParser> mSectionTrie = new RegexTrie<IBlockParser>();
+ private IBlockParser mCurrentParser;
+ private List<String> mParseBlock = new LinkedList<String>();
+
+ /**
+ * Default constructor: use {@link NoopSectionParser} as the initial section parser.
+ */
+ public AbstractSectionParser() {
+ this(new NoopSectionParser());
+ }
+
+ /**
+ * Use the passed {@link IBlockParser} as the initial section parser.
+ *
+ * @param defaultParser the IBlockParser to use
+ */
+ public AbstractSectionParser(IBlockParser defaultParser) {
+ mCurrentParser = defaultParser;
+
+ addDefaultSectionParsers(mSectionTrie);
+ }
+
+ /**
+ * A method to be overridden by subclasses that allows them to specify the parsers to use for
+ * the different sections of the file. It is assumed that a new section will start at a
+ * pattern that is recognizable with a single-line regular expression, and that a given section
+ * only ends when the subsequent section begins.
+ * <p />
+ * Parsers can skip sections by specifying that they be parsed by {@link NoopSectionParser}.
+ */
+ public abstract void addDefaultSectionParsers(RegexTrie<IBlockParser> sectionParsers);
+
+ /**
+ * A method to add a given section parser to the set of potential parsers to use.
+ * <p />
+ * @param parser The {@link IBlockParser} to add
+ * @param startPattern The regular expression to trigger this parser on
+ */
+ public void addSectionParser(IBlockParser parser, String startPattern) {
+ mSectionTrie.put(parser, startPattern);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parseLine(String line, ItemList itemlist) {
+ IBlockParser nextParser = mSectionTrie.retrieve(line);
+
+ if (nextParser == null) {
+ // no match, so buffer this for the current parser, if there is one
+ if (mCurrentParser != null) {
+ mParseBlock.add(line);
+ } else {
+ CLog.w("Line outside of parsed section: %s", line);
+ }
+ } else {
+ // Switching parsers. Send the parse block to the current parser and then rotate
+ if (mCurrentParser != null) {
+ mCurrentParser.parseBlock(mParseBlock, itemlist);
+ if (!(mCurrentParser instanceof NoopSectionParser)) {
+ CLog.v("Just ran the parser; itemlist is now: %s", itemlist);
+ }
+ }
+ mParseBlock.clear();
+
+ // This stanza is all just debug
+ String prev = null;
+ String next = nextParser.getClass().getSimpleName();
+ if (mCurrentParser != null) {
+ prev = mCurrentParser.getClass().getSimpleName();
+ }
+ CLog.v("Switching parsers from %s to %s for line %s", prev, next, line);
+ mCurrentParser = nextParser;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void commit(ItemList itemlist) {
+ CLog.w("commit!");
+ if (mCurrentParser != null) {
+ mCurrentParser.parseBlock(mParseBlock, itemlist);
+ if (!(mCurrentParser instanceof NoopSectionParser)) {
+ CLog.v("Just committed; itemlist is now: %s", itemlist);
+ }
+ }
+ }
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/section/MemInfoParser.java b/src/com/android/tradefed/util/brillopad/section/MemInfoParser.java
new file mode 100644
index 0000000..74a8225
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/section/MemInfoParser.java
@@ -0,0 +1,58 @@
+/*
+ * 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.section;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.IBlockParser;
+import com.android.tradefed.util.brillopad.item.GenericMapItem;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A {@link ILineParser} to handle the Memory Info section of the bugreport
+ */
+public class MemInfoParser implements IBlockParser {
+ public static final String SECTION_NAME = "MEMORY INFO";
+ public static final String SECTION_REGEX = "------ MEMORY INFO .*";
+
+ /** Match a single MemoryInfo line, such as "MemFree: 65420 kB" */
+ private static final Pattern INFO_LINE = Pattern.compile("^([^:]+):\\s+(\\d+) kB");
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parseBlock(List<String> block, ItemList itemlist) {
+ GenericMapItem<String, Integer> output =
+ new GenericMapItem<String, Integer>(SECTION_NAME);
+
+ for (String line : block) {
+ Matcher m = INFO_LINE.matcher(line);
+ if (m.matches()) {
+ String key = m.group(1);
+ Integer value = Integer.parseInt(m.group(2));
+ output.put(key, value);
+ } else {
+ CLog.w("Failed to parse line '%s'", line);
+ }
+ }
+ itemlist.addItem(output);
+ }
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/section/NoopSectionParser.java b/src/com/android/tradefed/util/brillopad/section/NoopSectionParser.java
new file mode 100644
index 0000000..e66f061
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/section/NoopSectionParser.java
@@ -0,0 +1,35 @@
+/*
+ * 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.section;
+
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.IBlockParser;
+
+import java.util.List;
+
+/**
+ * A Section Parser intended to consume unknown sections of the bugreport
+ */
+public class NoopSectionParser implements IBlockParser {
+ public static final String SECTION_NAME = "unknown";
+ public static final String SECTION_REGEX = "------ .*";
+
+ @Override
+ public void parseBlock(List<String> block, ItemList itemlist) {
+ // ignore
+ }
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/section/ProcRankParser.java b/src/com/android/tradefed/util/brillopad/section/ProcRankParser.java
new file mode 100644
index 0000000..ad88156
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/section/ProcRankParser.java
@@ -0,0 +1,99 @@
+/*
+ * 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.section;
+
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.IBlockParser;
+import com.android.tradefed.util.brillopad.item.GenericMapItem;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A {@link ILineParser} to handle the Procrank section of the bugreport. Memory values returned
+ * are in units of kiloBytes
+ */
+public class ProcRankParser implements IBlockParser {
+ public static final String SECTION_NAME = "PROCRANK";
+ public static final String SECTION_REGEX = "------ PROCRANK .*";
+
+ private int mNumFields = -1;
+ private String[] mFieldNames = null;
+
+ /** Match a memory amount, such as "12345K" */
+ private static final Pattern NUMBER_PAT = Pattern.compile("(\\d+)([BKMGbkmg])?");
+
+ /**
+ * A utility function to parse a memory amount, such as "12345K", and return the number of
+ * kiloBytes that the amount represents.
+ */
+ private static Integer parseMem(String val) {
+ Integer count = null;
+ Matcher m = NUMBER_PAT.matcher(val);
+ if (m.matches()) {
+ count = Integer.parseInt(m.group(1));
+ String suffix = m.group(2);
+ if (suffix == null) {
+ return count;
+ }
+ suffix = suffix.toLowerCase();
+ if ("b".equals(suffix)) {
+ count /= 1024;
+ } else if ("k".equals(suffix)) {
+ // nothing to do
+ } else if ("m".equals(suffix)) {
+ count *= 1024;
+ } else if ("g".equals(suffix)) {
+ count *= 1024 * 1024;
+ }
+ }
+ return count;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parseBlock(List<String> block, ItemList itemlist) {
+ GenericMapItem<String, Map<String, Integer>> output =
+ new GenericMapItem<String, Map<String, Integer>>(SECTION_NAME);
+
+ for (String line : block) {
+ // Trim leading whitespace so that split() works properly
+ line = line.replaceFirst("^\\s+", "");
+ if (mFieldNames == null) {
+ // try to parse a header
+ mFieldNames = line.split("\\s+");
+ mNumFields = mFieldNames.length;
+ continue;
+ }
+
+ String[] fields = line.split("\\s+", mNumFields);
+ String cmdline = fields[fields.length - 1];
+ Map<String, Integer> valueMap = new HashMap<String, Integer>();
+ for (int i = 0; i < mNumFields - 1 && i < fields.length; ++i) {
+ // FIXME: it's not correct to send PID through this, but in practice it works
+ valueMap.put(mFieldNames[i], parseMem(fields[i]));
+ }
+ output.put(cmdline, valueMap);
+ }
+ itemlist.addItem(output);
+ }
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/section/SystemLogParser.java b/src/com/android/tradefed/util/brillopad/section/SystemLogParser.java
new file mode 100644
index 0000000..03da047
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/section/SystemLogParser.java
@@ -0,0 +1,110 @@
+/*
+ * 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.section;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.IBlockParser;
+import com.android.tradefed.util.brillopad.section.syslog.AnrParser;
+import com.android.tradefed.util.brillopad.section.syslog.ISyslogParser;
+import com.android.tradefed.util.brillopad.section.syslog.JavaCrashParser;
+import com.android.tradefed.util.brillopad.section.syslog.NativeCrashParser;
+
+import java.util.List;
+import java.util.ListIterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * An {@link IBlockParser} to handle the System Log section of the bugreport
+ */
+public class SystemLogParser implements IBlockParser {
+ public static final String SECTION_NAME = "SYSTEM LOG";
+ public static final String SECTION_REGEX = "------ SYSTEM LOG .*";
+
+ private ISyslogParser mJava = new JavaCrashParser();
+ private ISyslogParser mNative = new NativeCrashParser();
+ private ISyslogParser mAnr = new AnrParser();
+
+ /**
+ * Match a single line of `logcat -v threadtime`, such as:
+ * 05-26 11:02:36.886 5689 5689 D AndroidRuntime: CheckJNI is OFF
+ */
+ private static final Pattern THREADTIME_LINE = Pattern.compile(
+ "^(\\d{2})-(\\d{2}) (\\d{2}:\\d{2}:\\d{2}.\\d{3})\\s+" + /* timestamp [1-3] */
+ "(\\d+)\\s+(\\d+)\\s+([A-Z])\\s+" + /* pid/tid and log level [4-6] */
+ "(.+?)\\s*: (.*)$" /* tag and message [7-8]*/);
+
+ /**
+ * Match a single line of `logcat -v time`, such as:
+ * 06-04 02:32:14.002 D/dalvikvm( 236): GC_CONCURRENT freed 580K, 51% free [...]
+ */
+ private static final Pattern TIME_LINE = Pattern.compile(
+ "^(\\d{2})-(\\d{2}) (\\d{2}:\\d{2}:\\d{2}.\\d{3})\\s+" + /* timestamp [1-3] */
+ "(\\w)/(.+?)\\(\\s*(\\d+)\\): (.*)$"); /* level, tag, pid, msg [4-7] */
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parseBlock(List<String> block, ItemList itemlist) {
+ ListIterator<String> iter = block.listIterator();
+
+ while (iter.hasNext()) {
+ String line = iter.next();
+ int pid = 0;
+ int tid = 0;
+ String level = null;
+ String tag = null;
+ String msg = null;
+
+ // FIXME: do something with timestamps
+ Matcher m = THREADTIME_LINE.matcher(line);
+ Matcher tm = TIME_LINE.matcher(line);
+ if (m.matches()) {
+ pid = Integer.parseInt(m.group(4));
+ tid = Integer.parseInt(m.group(5));
+ level = m.group(6);
+ tag = m.group(7);
+ msg = m.group(8);
+ } else if (tm.matches()) {
+ level = tm.group(4);
+ tag = tm.group(5);
+ pid = Integer.parseInt(tm.group(6));
+ msg = tm.group(7);
+ } else {
+ CLog.w("Failed to parse line '%s'", line);
+ continue;
+ }
+
+ Class nextParserClass = null;
+ if ("I".equals(level) && "DEBUG".equals(tag)) {
+ // Native crash
+ mNative.parseLine(tid, pid, msg, itemlist);
+ } else if ("E".equals(level) && "AndroidRuntime".equals(tag)) {
+ // Java crash
+ mJava.parseLine(tid, pid, msg, itemlist);
+ } else if ("ActivityManager".equals(tag)) {
+ // Message from ActivityManager; potentially an ANR
+ mAnr.parseLine(tid, pid, msg, itemlist);
+ }
+ }
+
+ mJava.commit(itemlist);
+ mNative.commit(itemlist);
+ mAnr.commit(itemlist);
+ }
+}
diff --git a/src/com/android/tradefed/util/brillopad/section/SystemPropParser.java b/src/com/android/tradefed/util/brillopad/section/SystemPropParser.java
new file mode 100644
index 0000000..d3d24a0
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/section/SystemPropParser.java
@@ -0,0 +1,56 @@
+/*
+ * 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.section;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.IBlockParser;
+import com.android.tradefed.util.brillopad.item.GenericMapItem;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A {@link ILineParser} to handle the System Properties section of the bugreport
+ */
+public class SystemPropParser implements IBlockParser {
+ public static final String SECTION_NAME = "SYSTEM PROPERTIES";
+ public static final String SECTION_REGEX = "------ SYSTEM PROPERTIES .*";
+
+ /** Match a single property line, such as "[gsm.sim.operator.numeric]: []" */
+ private static final Pattern PROP_LINE = Pattern.compile("^\\[(.*)\\]: \\[(.*)\\]$");
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parseBlock(List<String> block, ItemList itemlist) {
+ GenericMapItem<String, String> output =
+ new GenericMapItem<String, String>(SECTION_NAME);
+
+ for (String line : block) {
+ Matcher m = PROP_LINE.matcher(line);
+ if (m.matches()) {
+ output.put(m.group(1), m.group(2));
+ } else {
+ CLog.w("Failed to parse line '%s'", line);
+ }
+ }
+ itemlist.addItem(output);
+ }
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/section/syslog/AnrParser.java b/src/com/android/tradefed/util/brillopad/section/syslog/AnrParser.java
new file mode 100644
index 0000000..6109f17
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/section/syslog/AnrParser.java
@@ -0,0 +1,104 @@
+/*
+ * 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.section.syslog;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.item.GenericMapItem;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * An {@link ISyslogParser} to handle ANRs. Since ANRs are all reported by the ActivityManager,
+ * they are guaranteed to be fully serialized and thus to not be interleaved. Also for the same
+ * reason, the pid and tid fields are useless.
+ */
+public class AnrParser implements ISyslogParser {
+ public static final String SECTION_NAME = "ANR";
+
+ /**
+ * Matches: ANR (application not responding) in process: app
+ * Matches: ANR in app
+ * Matches: ANR in app (class/package)
+ */
+ private static final Pattern START = Pattern.compile(
+ "ANR (?:\\(application not responding\\) )?in (?:process: )?(\\S+).*");
+ private static final Pattern END = Pattern.compile(".*TOTAL: .*?\\d+% user + \\d+% kernel.*");
+ // FIXME: unit test: /TOTAL: .*/ vs. /TOTAL: .*?/
+
+ private GenericMapItem<String, String> mItem = null;
+ private int mPID = -1;
+ private int mTID = -1;
+
+ /**
+ * We store the stack messages separately so that we avoid creating a new stack String object
+ * for each new line. In this case, we just keep a single StringBuilder for the entire thing.
+ */
+ private StringBuilder mStack = new StringBuilder();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parseLine(int tid, int pid, String line, ItemList itemlist) {
+ Matcher m = START.matcher(line);
+ if (m.matches()) {
+ // start of new ANR. Out with the old, in with the new
+ CLog.v("Matched ANR start: %s", line);
+ commit(itemlist);
+ mItem = new GenericMapItem<String, String>(SECTION_NAME);
+ mItem.put("app", m.group(1));
+ mPID = pid;
+ mTID = tid;
+ } else if (mItem == null) {
+ //CLog.w("Ignoring unexpected line from pid %d, tid %d: %s", pid, tid, line);
+ return;
+ } else if (pid != mPID || tid != mTID) {
+ CLog.w("Expected pid %d, tid %d, but got line from pid %d, tid %d; committing...",
+ mPID, mTID, pid, tid);
+ commit(itemlist);
+ return;
+ }
+
+ mStack.append(line);
+ mStack.append("\n");
+
+ // FIXME: is there a way to guarantee that an ANR is completed?
+ m = END.matcher(line);
+ if (m.matches()) {
+ CLog.v("Matched ANR end: %s", line);
+ commit(itemlist);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void commit(ItemList itemlist) {
+ if (mItem != null) {
+ mItem.put("stack", mStack.toString());
+ itemlist.addItem(mItem);
+
+ mItem = null;
+ mStack = new StringBuilder();
+ mPID = -1;
+ mTID = -1;
+ }
+ }
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/section/syslog/ISyslogParser.java b/src/com/android/tradefed/util/brillopad/section/syslog/ISyslogParser.java
new file mode 100644
index 0000000..c2780fc
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/section/syslog/ISyslogParser.java
@@ -0,0 +1,37 @@
+/*
+ * 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.section.syslog;
+
+import com.android.tradefed.util.brillopad.ItemList;
+
+import java.util.List;
+
+/**
+ * Attempt to parse a line of the logcat
+ */
+public interface ISyslogParser {
+ // FIXME: ILogcatLineParser is a better name
+ /**
+ * Parse of line of logcat
+ */
+ public void parseLine(int tid, int pid, String msg, ItemList itemlist);
+
+ /**
+ * A signal that input is done for this parser
+ */
+ public void commit(ItemList itemlist);
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/section/syslog/JavaCrashParser.java b/src/com/android/tradefed/util/brillopad/section/syslog/JavaCrashParser.java
new file mode 100644
index 0000000..eeaa255
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/section/syslog/JavaCrashParser.java
@@ -0,0 +1,111 @@
+/*
+ * 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.section.syslog;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.item.GenericMapItem;
+
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * An {@link ISyslogParser} to handle Java crashes. We parse line-by-line since messages from
+ * different crashes may be interleaved -- the only guarantee we have is that a single line of
+ * logcat does not contain portions of some other line.
+ */
+public class JavaCrashParser implements ISyslogParser {
+ public static final String SECTION_NAME = "JAVA CRASH";
+
+ /**
+ * Matches: java.lang.Exception
+ * Matches: java.lang.Exception: reason
+ */
+ private static final Pattern EXCEPTION = Pattern.compile("^([^\\s:]+)(?:: (.*))$");
+
+ private Set<Integer> mKeys = new LinkedHashSet<Integer>();
+ private Map<Integer, GenericMapItem<String, String>> mMaps =
+ new HashMap<Integer, GenericMapItem<String, String>>();
+ /**
+ * We store the stack messages separately so that we avoid creating a new stack String object
+ * for each new line. In this case, we just keep a single StringBuilder for the entire thing.
+ */
+ private Map<Integer, StringBuilder> mStacks = new HashMap<Integer, StringBuilder>();
+
+ /**
+ * Hash the PID and TID to create an identifier that "should" be unique for a given logcat.
+ * In practice, we do use it as a unique identifier.
+ */
+ private static int encodePidTid(Integer pid, Integer tid) {
+ // Assume an unreachable max pid of 65536 == 16 bits
+ return (pid << 16) | tid;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parseLine(int tid, int pid, String line, ItemList itemlist) {
+ int key = encodePidTid(pid, tid);
+ //CLog.w("Got tid %d, pid %d, encoded key %d, line %s", tid, pid, key, line);
+
+ mKeys.add(key);
+
+ Matcher m = EXCEPTION.matcher(line);
+ if (m.matches()) {
+ GenericMapItem<String, String> item = new GenericMapItem<String, String>(SECTION_NAME);
+ item.put("exception", m.group(1));
+ String reason = m.group(2);
+ if (reason != null) {
+ item.put("reason", m.group(2));
+ }
+ mMaps.put(key, item);
+ }
+
+ StringBuilder stack;
+ if (mStacks.containsKey(key)) {
+ stack = mStacks.get(key);
+ } else {
+ stack = new StringBuilder();
+ mStacks.put(key, stack);
+ }
+ stack.append(line);
+ stack.append("\n");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void commit(ItemList itemlist) {
+ for (int key : mKeys) {
+ GenericMapItem<String, String> item = mMaps.get(key);
+ if (item == null) {
+ item = new GenericMapItem<String, String>(SECTION_NAME);
+ }
+
+ if (mStacks.containsKey(key)) {
+ item.put("stack", mStacks.get(key).toString());
+ }
+ itemlist.addItem(item);
+ }
+ }
+}
+
diff --git a/src/com/android/tradefed/util/brillopad/section/syslog/NativeCrashParser.java b/src/com/android/tradefed/util/brillopad/section/syslog/NativeCrashParser.java
new file mode 100644
index 0000000..fc324c5
--- /dev/null
+++ b/src/com/android/tradefed/util/brillopad/section/syslog/NativeCrashParser.java
@@ -0,0 +1,111 @@
+/*
+ * 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.section.syslog;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.item.GenericMapItem;
+
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * An {@link ISyslogParser} to handle Native crashes. We parse line-by-line since messages from
+ * different crashes may be interleaved -- the only guarantee we have is that a single line of
+ * logcat does not contain portions of some other line.
+ */
+public class NativeCrashParser implements ISyslogParser {
+ public static final String SECTION_NAME = "NATIVE CRASH";
+
+ /** Matches: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** */
+ private static final Pattern START = Pattern.compile("^(?:\\*\\*\\* ){15}\\*\\*\\*$");
+ /** Matches: Build fingerprint: 'fingerprint' */
+ private static final Pattern FINGERPRINT = Pattern.compile("^Build fingerprint: '(.*)'$");
+ /** Matches: pid: 957, tid: 963 >>> com.android.camera <<< */
+ private static final Pattern APP = Pattern.compile("^pid: \\d+, tid: \\d+ >>> (\\S+) <<<$");
+
+ private Set<Integer> mKeys = new LinkedHashSet<Integer>();
+ private Map<Integer, GenericMapItem<String, String>> mMaps =
+ new HashMap<Integer, GenericMapItem<String, String>>();
+ /**
+ * We store the stack messages separately so that we avoid creating a new stack String object
+ * for each new line. In this case, we just keep a single StringBuilder for the entire thing.
+ */
+ private Map<Integer, StringBuilder> mStacks = new HashMap<Integer, StringBuilder>();
+
+ /**
+ * Hash the PID and TID to create an identifier that "should" be unique for a given logcat.
+ * In practice, we do use it as a unique identifier.
+ */
+ private static int encodePidTid(Integer pid, Integer tid) {
+ // Assume an unreachable max pid of 65536 == 16 bits
+ return (pid << 16) | tid;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void parseLine(int tid, int pid, String line, ItemList itemlist) {
+ int key = encodePidTid(pid, tid);
+ //CLog.w("Got tid %d, pid %d, encoded key %d, line %s", tid, pid, key, line);
+
+ if (!mKeys.contains(key)) {
+ // potentially new entry. Ignore unless we see a start line.
+ Matcher m = START.matcher(line);
+ if (m.matches()) {
+ mKeys.add(key);
+ mMaps.put(key, new GenericMapItem<String, String>(SECTION_NAME));
+ mStacks.put(key, new StringBuilder());
+ } else {
+ CLog.w("Ignoring unexpected line from pid %d, tid %d: %s", pid, tid, line);
+ return;
+ }
+ }
+
+ // by virtue of getting this far, the entries in mMaps and mStacks should exist
+ GenericMapItem<String, String> item = mMaps.get(key);
+ Matcher m = FINGERPRINT.matcher(line);
+ if (m.matches()) {
+ item.put("fingerprint", m.group(1));
+ }
+ m = APP.matcher(line);
+ if (m.matches()) {
+ item.put("app", m.group(1));
+ }
+
+ StringBuilder stack = mStacks.get(key);
+ stack.append(line);
+ stack.append("\n");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void commit(ItemList itemlist) {
+ for (int key : mKeys) {
+ GenericMapItem<String, String> item = new GenericMapItem<String, String>(SECTION_NAME);
+ item.put("stack", mStacks.get(key).toString());
+ itemlist.addItem(item);
+ }
+ }
+}
+
diff --git a/tests/src/com/android/tradefed/util/brillopad/AbstractBlockParserTest.java b/tests/src/com/android/tradefed/util/brillopad/AbstractBlockParserTest.java
new file mode 100644
index 0000000..37e5e73
--- /dev/null
+++ b/tests/src/com/android/tradefed/util/brillopad/AbstractBlockParserTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.ItemList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link AbstractBlockParser}
+ */
+public class AbstractBlockParserTest extends TestCase {
+ private static final String COMMIT_MSG = "COMMITTERS! COMMITTERS! COMMITTERS!";
+
+ private List<String> mExpectedLines = new ArrayList<String>();
+ private ItemList mItemList = null;
+
+ private class BlockParser extends AbstractBlockParser {
+ @Override
+ public void parseLine(String line, ItemList itemlist) {
+ String expectedLine = mExpectedLines.remove(0);
+ assertEquals(expectedLine, line);
+ assertEquals(mItemList, itemlist);
+ }
+
+ @Override
+ public void commit(ItemList itemlist) {
+ String expectedLine = mExpectedLines.remove(0);
+ assertEquals(COMMIT_MSG, expectedLine);
+ assertEquals(mItemList, itemlist);
+ }
+ }
+
+ /**
+ * Simply verify that the AbstractBlockParser turns a single parseBlock call into the
+ * appropriate sequence of parseLine calls, followed by a commit() call
+ */
+ public void testSimple() {
+ List<String> lines = list("alpha", "beta", "gamma", "delta");
+ mItemList = new ItemList();
+ BlockParser parser = new BlockParser();
+ mExpectedLines.addAll(lines);
+ mExpectedLines.add(COMMIT_MSG);
+
+ parser.parseBlock(lines, mItemList);
+ assertEquals(0, mExpectedLines.size());
+ }
+
+ private static List<String> list(String... strings) {
+ List<String> retList = new ArrayList<String>(strings.length);
+ for (String str : strings) {
+ retList.add(str);
+ }
+ return retList;
+ }
+}
+
diff --git a/tests/src/com/android/tradefed/util/brillopad/BugreportParserFuncTest.java b/tests/src/com/android/tradefed/util/brillopad/BugreportParserFuncTest.java
new file mode 100644
index 0000000..9ab6d1e
--- /dev/null
+++ b/tests/src/com/android/tradefed/util/brillopad/BugreportParserFuncTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.IItem;
+
+import org.easymock.EasyMock;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Functional tests for {@link BugreportParser}
+ */
+public class BugreportParserFuncTest extends TestCase {
+ private static final File BR_FILE = new File("/tmp/bugreport.txt");
+
+/* FIXME: flesh this out with known bugreports
+ public void disable_testFullMockedParse() throws Exception {
+ BugreportParser parser = new BugreportParser();
+ ISectionParser mockSP = EasyMock.createStrictMock(ISectionParser.class);
+ parser.addSectionParser(mockSP, "------ MEMORY INFO .*");
+
+ // Set up expectations
+ EasyMock.expect(mockSP.parseBlock((List<String>)EasyMock.anyObject())).andReturn(null);
+ EasyMock.replay(mockSP);
+
+ if (!BR_FILE.exists()) {
+ fail(String.format("Full Parse test requires sample bugreport at %s", BR_FILE));
+ }
+
+ BufferedReader reader = new BufferedReader(new FileReader(BR_FILE));
+ parser.parse(reader);
+ EasyMock.verify(mockSP);
+ }
+*/
+
+ /**
+ * A "test" that is intended to force Brillopad to parse a bugreport found at {@code BR_FILE}.
+ * The purpose of this is to assist a developer in checking why a given bugreport file might not
+ * be parsed correctly by Brillopad.
+ * <p />
+ * Because the name doesn't start with "test", this method won't be picked up automatically by a
+ * JUnit test runner, and must be run by hand. This is as intended.
+ */
+ public void manualTestFullParse() throws Exception {
+ BugreportParser parser = new BugreportParser();
+
+ if (!BR_FILE.exists()) {
+ fail(String.format("Full Parse test requires sample bugreport at %s", BR_FILE));
+ }
+
+ BufferedReader reader = new BufferedReader(new FileReader(BR_FILE));
+ ItemList bugreport = new ItemList();
+ parser.parse(reader, bugreport);
+
+ List<IItem> jc = bugreport.getByType("JAVA CRASH");
+ List<IItem> nc = bugreport.getByType("NATIVE CRASH");
+ List<IItem> anr = bugreport.getByType("ANR");
+ System.err.format("Got %d items for JAVA CRASH, %d for NATIVE CRASH, and %d for ANR\n",
+ jc.size(), nc.size(), anr.size());
+ for (IItem item : bugreport.getItems()) {
+ System.err.format("Got item with type %s\n", item.getType());
+ }
+ }
+}
+
diff --git a/tests/src/com/android/tradefed/util/brillopad/BugreportParserTest.java b/tests/src/com/android/tradefed/util/brillopad/BugreportParserTest.java
new file mode 100644
index 0000000..c764d5a
--- /dev/null
+++ b/tests/src/com/android/tradefed/util/brillopad/BugreportParserTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.ItemList;
+import com.android.tradefed.util.brillopad.item.GenericMapItem;
+import com.android.tradefed.util.brillopad.item.IItem;
+import com.android.tradefed.util.brillopad.section.MemInfoParser;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link BugreportParser}
+ */
+public class BugreportParserTest extends TestCase {
+ public void testMemInfoParser() {
+ List<String> inputBlock = list(
+ "MemTotal: 353332 kB",
+ "MemFree: 65420 kB",
+ "Buffers: 20800 kB",
+ "Cached: 86204 kB",
+ "SwapCached: 0 kB");
+ MemInfoParser parser = new MemInfoParser();
+ ItemList br = new ItemList();
+
+ parser.parseBlock(inputBlock, br);
+ List<IItem> items = br.getItems();
+ assertNotNull(items);
+ assertEquals(1, items.size());
+ assertTrue("Expected item of type GenericMapItem!", items.get(0) instanceof GenericMapItem);
+ assertEquals(MemInfoParser.SECTION_NAME, items.get(0).getType());
+
+ Map<String, Integer> output = (GenericMapItem<String, Integer>)items.get(0);
+ assertEquals(5, output.size());
+ assertEquals((Integer)353332, output.get("MemTotal"));
+ assertEquals((Integer)65420, output.get("MemFree"));
+ assertEquals((Integer)20800, output.get("Buffers"));
+ assertEquals((Integer)86204, output.get("Cached"));
+ assertEquals((Integer)0, output.get("SwapCached"));
+ }
+
+ private static List<String> list(String... strings) {
+ List<String> retList = new ArrayList<String>(strings.length);
+ for (String str : strings) {
+ retList.add(str);
+ }
+ return retList;
+ }
+
+ // FIXME: testcase: log tag with embedded colon
+ // FIXME: '05-25 15:04:46.900 2793 2793 I DataGauge:DataUsageService: Custom datagauge boot service destroyed'
+}
+
diff --git a/tests/src/com/android/tradefed/util/brillopad/ItemListTest.java b/tests/src/com/android/tradefed/util/brillopad/ItemListTest.java
new file mode 100644
index 0000000..2295157
--- /dev/null
+++ b/tests/src/com/android/tradefed/util/brillopad/ItemListTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.IItem;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link ItemList}
+ */
+public class ItemListTest extends TestCase {
+ private ItemList mList = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mList = new ItemList();
+ }
+
+ private static class FakeItem implements IItem {
+ private String mType = null;
+ public FakeItem(String type) {
+ mType = type;
+ }
+ @Override
+ public String getType() {
+ return mType;
+ }
+ @Override
+ public IItem merge(IItem other) {
+ return this;
+ }
+ @Override
+ public boolean isConsistent(IItem other) {
+ return true;
+ }
+ }
+
+ /**
+ * Verify that if you add some {@link IItem}s, you can get them back out as well.
+ */
+ public void testAdd() {
+ List<IItem> iitems = new ArrayList<IItem>(5);
+ for (Integer i = 0; i < 5; ++i) {
+ IItem item = new FakeItem(i.toString());
+ iitems.add(item);
+ mList.addItem(item);
+ }
+
+ assertEquals("Wrong number of iitems!", iitems.size(), mList.getItems().size());
+ }
+
+ /**
+ * Verify that searching works properly
+ */
+ public void testSearch() {
+ final String namePat = "fake %d";
+ List<IItem> iitems = new ArrayList<IItem>(5);
+ for (Integer i = 0; i < 5; ++i) {
+ IItem item = new FakeItem(String.format("fake %d", i));
+ iitems.add(item);
+ mList.addItem(item);
+ }
+
+ for (Integer i = 0; i < 5; ++i) {
+ List<IItem> searchList = mList.getByType(String.format(".*%d.*", i));
+ assertEquals(1, searchList.size());
+ assertEquals(String.format(namePat, i), searchList.get(0).getType());
+ }
+ }
+}
+
diff --git a/tests/src/com/android/tradefed/util/brillopad/section/AbstractSectionParserTest.java b/tests/src/com/android/tradefed/util/brillopad/section/AbstractSectionParserTest.java
new file mode 100644
index 0000000..b57ee27
--- /dev/null
+++ b/tests/src/com/android/tradefed/util/brillopad/section/AbstractSectionParserTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.section;
+
+import com.android.tradefed.util.RegexTrie;
+import com.android.tradefed.util.brillopad.IBlockParser;
+import com.android.tradefed.util.brillopad.ItemList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link AbstractSectionParser}
+ */
+public class AbstractSectionParserTest extends TestCase {
+ AbstractSectionParser mParser = null;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mParser = new AbstractSectionParser() {
+ @Override
+ public void addDefaultSectionParsers(RegexTrie<IBlockParser> parsers) {
+ // ignore
+ }
+ };
+ }
+
+ private static class FakeBlockParser implements IBlockParser {
+ private String mExpected = null;
+ private int mCalls = 0;
+
+ public FakeBlockParser(String expected) {
+ mExpected = expected;
+ }
+
+ public int getCalls() {
+ return mCalls;
+ }
+
+ @Override
+ public void parseBlock(List<String> input, ItemList itemlist) {
+ assertEquals(1, input.size());
+ assertEquals("parseBlock() got unexpected input!", mExpected, input.get(0));
+ mCalls += 1;
+ }
+ }
+
+ /**
+ * Verifies that {@link AbstractSectionParser} switches between parsers as expected
+ */
+ public void testSwitchParsers() {
+ final String lineFormat = "howdy, parser %d!";
+ final String linePattern = "I spy %d candles";
+ final int nParsers = 4;
+ FakeBlockParser[] parsers = new FakeBlockParser[nParsers];
+ final List<String> lines = new ArrayList<String>(2*nParsers);
+
+ for (int i = 0; i < nParsers; ++i) {
+ String line = String.format(lineFormat, i);
+ FakeBlockParser parser = new FakeBlockParser(line);
+ mParser.addSectionParser(parser, String.format(linePattern, i));
+ parsers[i] = parser;
+
+ // add the parser trigger
+ lines.add(String.format(linePattern, i));
+ // and then add the line that the parser is expecting
+ lines.add(String.format(lineFormat, i));
+ }
+
+ mParser.parseBlock(lines, null);
+
+ // Verify that all the parsers were run
+ for (int i = 0; i < nParsers; ++i) {
+ assertEquals(String.format("Parser %d has wrong call count!", i),
+ 1, parsers[i].getCalls());
+ }
+ }
+
+ private static List<String> list(String... strings) {
+ List<String> retList = new ArrayList<String>(strings.length);
+ for (String str : strings) {
+ retList.add(str);
+ }
+ return retList;
+ }
+}
+
diff --git a/tests/src/com/android/tradefed/util/brillopad/section/MemInfoParserTest.java b/tests/src/com/android/tradefed/util/brillopad/section/MemInfoParserTest.java
new file mode 100644
index 0000000..1890899
--- /dev/null
+++ b/tests/src/com/android/tradefed/util/brillopad/section/MemInfoParserTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.section;
+
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.item.GenericMapItem;
+import com.android.tradefed.util.brillopad.item.IItem;
+import com.android.tradefed.util.brillopad.section.MemInfoParser;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link MemInfoParser}
+ */
+public class MemInfoParserTest extends TestCase {
+ public void testMemInfoParser() {
+ List<String> inputBlock = list(
+ "MemTotal: 353332 kB",
+ "MemFree: 65420 kB",
+ "Buffers: 20800 kB",
+ "Cached: 86204 kB",
+ "SwapCached: 0 kB");
+ MemInfoParser parser = new MemInfoParser();
+ ItemList br = new ItemList();
+
+ parser.parseBlock(inputBlock, br);
+ List<IItem> items = br.getItems();
+ assertNotNull(items);
+ assertEquals(1, items.size());
+ assertTrue("Expected item of type GenericMapItem!", items.get(0) instanceof GenericMapItem);
+ assertEquals(MemInfoParser.SECTION_NAME, items.get(0).getType());
+
+ Map<String, Integer> output = (GenericMapItem<String, Integer>)items.get(0);
+ assertEquals(5, output.size());
+ assertEquals((Integer)353332, output.get("MemTotal"));
+ assertEquals((Integer)65420, output.get("MemFree"));
+ assertEquals((Integer)20800, output.get("Buffers"));
+ assertEquals((Integer)86204, output.get("Cached"));
+ assertEquals((Integer)0, output.get("SwapCached"));
+ }
+
+ private static List<String> list(String... strings) {
+ List<String> retList = new ArrayList<String>(strings.length);
+ for (String str : strings) {
+ retList.add(str);
+ }
+ return retList;
+ }
+}
+
diff --git a/tests/src/com/android/tradefed/util/brillopad/section/ProcRankParserTest.java b/tests/src/com/android/tradefed/util/brillopad/section/ProcRankParserTest.java
new file mode 100644
index 0000000..7024ae7
--- /dev/null
+++ b/tests/src/com/android/tradefed/util/brillopad/section/ProcRankParserTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.section;
+
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.item.GenericMapItem;
+import com.android.tradefed.util.brillopad.item.IItem;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link ProcRankParser}
+ */
+public class ProcRankParserTest extends TestCase {
+ public void testProcRankParser() {
+ List<String> inputBlock = list(
+ " PID Vss Rss Pss Uss cmdline",
+ " 178 87136K 81684K 52829K 50012K system_server",
+ " 1313 78128K 77996K 48603K 45812K com.google.android.apps.maps",
+ " 3247 61652K 61492K 33122K 30972K com.android.browser",
+ " 334 55740K 55572K 29629K 28360K com.android.launcher",
+ " 2072 51348K 51172K 24263K 22812K android.process.acore",
+ " 1236 51440K 51312K 22911K 20608K com.android.settings");
+ ProcRankParser parser = new ProcRankParser();
+ ItemList br = new ItemList();
+
+ parser.parseBlock(inputBlock, br);
+ List<IItem> items = br.getItems();
+ assertNotNull(items);
+ assertEquals(1, items.size());
+ assertTrue("Expected item of type GenericMapItem!", items.get(0) instanceof GenericMapItem);
+ assertEquals(ProcRankParser.SECTION_NAME, items.get(0).getType());
+
+ Map<String, Integer> map;
+ Map<String, Map<String, Integer>> output =
+ (GenericMapItem<String, Map<String, Integer>>)items.get(0);
+ assertEquals(6, output.size());
+ // Make sure all expected rows are present, and do a diagonal check of values
+ map = output.get("system_server");
+ assertNotNull(map);
+ assertEquals((Integer)178, map.get("PID"));
+
+ map = output.get("com.google.android.apps.maps");
+ assertNotNull(map);
+ assertEquals((Integer)78128, map.get("Vss"));
+
+ map = output.get("com.android.browser");
+ assertNotNull(map);
+ assertEquals((Integer)61492, map.get("Rss"));
+
+ map = output.get("com.android.launcher");
+ assertNotNull(map);
+ assertEquals((Integer)29629, map.get("Pss"));
+
+ map = output.get("android.process.acore");
+ assertNotNull(map);
+ assertEquals((Integer)22812, map.get("Uss"));
+
+ map = output.get("com.android.settings");
+ assertNotNull(map);
+ assertEquals((Integer)1236, map.get("PID"));
+ }
+
+ private static List<String> list(String... strings) {
+ List<String> retList = new ArrayList<String>(strings.length);
+ for (String str : strings) {
+ retList.add(str);
+ }
+ return retList;
+ }
+}
+
diff --git a/tests/src/com/android/tradefed/util/brillopad/section/SystemPropParserTest.java b/tests/src/com/android/tradefed/util/brillopad/section/SystemPropParserTest.java
new file mode 100644
index 0000000..d5f6e2f
--- /dev/null
+++ b/tests/src/com/android/tradefed/util/brillopad/section/SystemPropParserTest.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.section;
+
+import com.android.tradefed.util.brillopad.ItemList;
+import com.android.tradefed.util.brillopad.item.GenericMapItem;
+import com.android.tradefed.util.brillopad.item.IItem;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link SystemPropParser}
+ */
+public class SystemPropParserTest extends TestCase {
+ public void testSimpleParse() {
+ List<String> inputBlock = list(
+ "[dalvik.vm.dexopt-flags]: [m=y]",
+ "[dalvik.vm.heapgrowthlimit]: [48m]",
+ "[dalvik.vm.heapsize]: [256m]",
+ "[gsm.version.ril-impl]: [android moto-ril-multimode 1.0]");
+ SystemPropParser parser = new SystemPropParser();
+ ItemList br = new ItemList();
+
+ parser.parseBlock(inputBlock, br);
+ List<IItem> items = br.getItems();
+ assertNotNull(items);
+ assertEquals(1, items.size());
+ assertTrue("Expected item of type GenericMapItem!", items.get(0) instanceof GenericMapItem);
+ assertEquals(SystemPropParser.SECTION_NAME, items.get(0).getType());
+
+ Map<String, String> map = (Map<String, String>)items.get(0);
+ assertEquals(4, map.size());
+ assertEquals("m=y", map.get("dalvik.vm.dexopt-flags"));
+ assertEquals("48m", map.get("dalvik.vm.heapgrowthlimit"));
+ assertEquals("256m", map.get("dalvik.vm.heapsize"));
+ assertEquals("android moto-ril-multimode 1.0", map.get("gsm.version.ril-impl"));
+ }
+
+ /**
+ * Make sure that a parse error on one line doesn't prevent the rest of the lines from being
+ * parsed
+ */
+ public void testParseError() {
+ List<String> inputBlock = list(
+ "[dalvik.vm.dexopt-flags]: [m=y]",
+ "[ends with newline]: [yup",
+ "]",
+ "[dalvik.vm.heapsize]: [256m]");
+ SystemPropParser parser = new SystemPropParser();
+ ItemList br = new ItemList();
+
+ parser.parseBlock(inputBlock, br);
+ List<IItem> items = br.getItems();
+ assertNotNull(items);
+ assertEquals(1, items.size());
+ assertTrue("Expected item of type GenericMapItem!", items.get(0) instanceof GenericMapItem);
+ assertEquals(SystemPropParser.SECTION_NAME, items.get(0).getType());
+
+ Map<String, String> map = (Map<String, String>)items.get(0);
+ assertEquals(2, map.size());
+ assertEquals("m=y", map.get("dalvik.vm.dexopt-flags"));
+ assertEquals("256m", map.get("dalvik.vm.heapsize"));
+ }
+
+ private static List<String> list(String... strings) {
+ List<String> retList = new ArrayList<String>(strings.length);
+ for (String str : strings) {
+ retList.add(str);
+ }
+ return retList;
+ }
+}
+