Merge "SmartMonkey log parser and unit tests"
diff --git a/src/com/android/loganalysis/item/BugreportItem.java b/src/com/android/loganalysis/item/BugreportItem.java
index 11a41fe..95b36a8 100644
--- a/src/com/android/loganalysis/item/BugreportItem.java
+++ b/src/com/android/loganalysis/item/BugreportItem.java
@@ -29,11 +29,13 @@
private static final String TIME = "TIME";
private static final String MEM_INFO = "MEM_INFO";
private static final String PROCRANK = "PROCRANK";
+ private static final String TOP = "TOP";
private static final String SYSTEM_LOG = "SYSTEM_LOG";
private static final String SYSTEM_PROPS = "SYSTEM_PROPS";
+ private static final String DUMPSYS = "DUMPSYS";
private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
- TIME, MEM_INFO, PROCRANK, SYSTEM_LOG, SYSTEM_PROPS));
+ TIME, MEM_INFO, PROCRANK, TOP, SYSTEM_LOG, SYSTEM_PROPS, DUMPSYS));
/**
* The constructor for {@link BugreportItem}.
@@ -85,6 +87,20 @@
}
/**
+ * Get the {@link TopItem} of the bugreport.
+ */
+ public TopItem getTop() {
+ return (TopItem) getAttribute(TOP);
+ }
+
+ /**
+ * Set the {@link TopItem} of the bugreport.
+ */
+ public void setTop(TopItem top) {
+ setAttribute(TOP, top);
+ }
+
+ /**
* Get the {@link LogcatItem} of the bugreport.
*/
public LogcatItem getSystemLog() {
@@ -111,4 +127,18 @@
public void setSystemProps(SystemPropsItem systemProps) {
setAttribute(SYSTEM_PROPS, systemProps);
}
+
+ /**
+ * Get the {@link DumpsysItem} of the bugreport.
+ */
+ public DumpsysItem getDumpsys() {
+ return (DumpsysItem) getAttribute(DUMPSYS);
+ }
+
+ /**
+ * Set the {@link DumpsysItem} of the bugreport.
+ */
+ public void setDumpsys(DumpsysItem dumpsys) {
+ setAttribute(DUMPSYS, dumpsys);
+ }
}
diff --git a/src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java b/src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java
new file mode 100644
index 0000000..196932d
--- /dev/null
+++ b/src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java
@@ -0,0 +1,146 @@
+/*
+ * 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.LinkedList;
+import java.util.List;
+
+/**
+ * An {@link IItem} used to store the battery info part of the dumpsys output.
+ */
+public class DumpsysBatteryInfoItem implements IItem {
+ public static final String TYPE = "DUMPSYS_BATTERY_INFO";
+
+ /**
+ * A class designed to store information related to wake locks and kernel wake locks.
+ */
+ public class WakeLock {
+ private String mName;
+ private Integer mNumber;
+ private long mHeldTime;
+ private int mLockedCount;
+
+ /**
+ * The constructor for {@link WakeLock}
+ *
+ * @param name The name of the wake lock
+ * @param heldTime The amount of time held in milliseconds
+ * @param lockedCount The number of times the wake lock was locked
+ */
+ public WakeLock(String name, long heldTime, int lockedCount) {
+ this(name, null, heldTime, lockedCount);
+ }
+
+ /**
+ * The constructor for {@link WakeLock}
+ *
+ * @param name The name of the wake lock
+ * @param number The number of the wake lock
+ * @param heldTime The amount of time held in milliseconds
+ * @param lockedCount The number of times the wake lock was locked
+ */
+ public WakeLock(String name, Integer number, long heldTime, int lockedCount) {
+ mName = name;
+ mNumber = number;
+ mHeldTime = heldTime;
+ mLockedCount = lockedCount;
+ }
+
+ /**
+ * Get the name of the wake lock.
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Get the number of the wake lock.
+ */
+ public Integer getNumber() {
+ return mNumber;
+ }
+
+ /**
+ * Get the time the wake lock was held in milliseconds.
+ */
+ public long getHeldTime() {
+ return mHeldTime;
+ }
+
+ /**
+ * Get the number of times the wake lock was locked.
+ */
+ public int getLockedCount() {
+ return mLockedCount;
+ }
+ }
+
+ private List<WakeLock> mLastUnpluggedKernelWakeLocks = new LinkedList<WakeLock>();
+ private List<WakeLock> mLastUnpluggedWakeLocks = new LinkedList<WakeLock>();
+
+ /**
+ * Add a kernel wake lock from the last unplugged section of the battery info.
+ */
+ public void addLastUnpluggedKernelWakeLock(String name, long heldTime, int timesCalled) {
+ mLastUnpluggedKernelWakeLocks.add(new WakeLock(name, heldTime, timesCalled));
+ }
+
+ /**
+ * Add a wake lock from the last unplugged section of the battery info.
+ */
+ public void addLastUnpluggedWakeLock(String name, int number, long heldTime, int timesCalled) {
+ mLastUnpluggedWakeLocks.add(new WakeLock(name, number, heldTime, timesCalled));
+ }
+
+ /**
+ * Get the list of kernel wake locks from the last unplugged section of the battery info.
+ */
+ public List<WakeLock> getLastUnpluggedKernelWakeLock() {
+ return mLastUnpluggedKernelWakeLocks;
+ }
+
+ /**
+ * Get the list of wake locks from the last unplugged section of the battery info.
+ */
+ public List<WakeLock> getLastUnpluggedWakeLock() {
+ return mLastUnpluggedWakeLocks;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getType() {
+ return TYPE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IItem merge(IItem other) throws ConflictingItemException {
+ throw new ConflictingItemException("Dumpsys battery info items cannot be merged");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isConsistent(IItem other) {
+ return false;
+ }
+
+}
diff --git a/src/com/android/loganalysis/item/DumpsysItem.java b/src/com/android/loganalysis/item/DumpsysItem.java
new file mode 100644
index 0000000..ed13b9c
--- /dev/null
+++ b/src/com/android/loganalysis/item/DumpsysItem.java
@@ -0,0 +1,52 @@
+/*
+ * 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 the output of the dumpsys section of the bugreport.
+ */
+public class DumpsysItem extends GenericItem {
+ public static final String TYPE = "DUMPSYS";
+
+ private static final String BATTERY_INFO = "BATTERY_INFO";
+
+ private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(BATTERY_INFO));
+
+ /**
+ * The constructor for {@link BugreportItem}.
+ */
+ public DumpsysItem() {
+ super(TYPE, ATTRIBUTES);
+ }
+
+ /**
+ * Get the battery info section of the dumpsys.
+ */
+ public DumpsysBatteryInfoItem getBatteryInfo() {
+ return (DumpsysBatteryInfoItem) getAttribute(BATTERY_INFO);
+ }
+
+ /**
+ * Set the battery info section of the dumpsys.
+ */
+ public void setBatteryInfo(DumpsysBatteryInfoItem batteryInfo) {
+ setAttribute(BATTERY_INFO, batteryInfo);
+ }
+}
diff --git a/src/com/android/loganalysis/item/ProcrankItem.java b/src/com/android/loganalysis/item/ProcrankItem.java
index a2d7e74..32d4ad1 100644
--- a/src/com/android/loganalysis/item/ProcrankItem.java
+++ b/src/com/android/loganalysis/item/ProcrankItem.java
@@ -27,7 +27,7 @@
public static final String TYPE = "PROCRANK";
private class ProcrankValue {
- public String mProcessName = null;
+ public String mProcessName;
public int mVss;
public int mRss;
public int mPss;
diff --git a/src/com/android/loganalysis/item/TopItem.java b/src/com/android/loganalysis/item/TopItem.java
new file mode 100644
index 0000000..bf15f7e
--- /dev/null
+++ b/src/com/android/loganalysis/item/TopItem.java
@@ -0,0 +1,162 @@
+/*
+ * 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 the output of the top command.
+ */
+public class TopItem extends GenericItem {
+ public static final String TYPE = "TOP";
+
+ private static final String USER = "USER";
+ private static final String NICE = "NICE";
+ private static final String SYSTEM = "SYSTEM";
+ private static final String IDLE = "IDLE";
+ private static final String IOW = "IOW";
+ private static final String IRQ = "IRQ";
+ private static final String SIRQ = "SIRQ";
+ private static final String TOTAL = "TOTAL";
+
+ private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(
+ USER, NICE, SYSTEM, IDLE, IOW, IRQ, SIRQ, TOTAL));
+
+ /**
+ * The constructor for {@link TopItem}.
+ */
+ public TopItem() {
+ super(TYPE, ATTRIBUTES);
+
+ for (String attribute : ATTRIBUTES) {
+ setAttribute(attribute, 0);
+ }
+ }
+
+ /**
+ * Get the number of user ticks.
+ */
+ public int getUser() {
+ return (Integer) getAttribute(USER);
+ }
+
+ /**
+ * Set the number of user ticks.
+ */
+ public void setUser(int user) {
+ setAttribute(USER, user);
+ }
+
+ /**
+ * Get the number of nice ticks.
+ */
+ public int getNice() {
+ return (Integer) getAttribute(NICE);
+ }
+
+ /**
+ * Set the number of nice ticks.
+ */
+ public void setNice(int nice) {
+ setAttribute(NICE, nice);
+ }
+
+ /**
+ * Get the number of system ticks.
+ */
+ public int getSystem() {
+ return (Integer) getAttribute(SYSTEM);
+ }
+
+ /**
+ * Set the number of system ticks.
+ */
+ public void setSystem(int system) {
+ setAttribute(SYSTEM, system);
+ }
+
+ /**
+ * Get the number of idle ticks.
+ */
+ public int getIdle() {
+ return (Integer) getAttribute(IDLE);
+ }
+
+ /**
+ * Set the number of idle ticks.
+ */
+ public void setIdle(int idle) {
+ setAttribute(IDLE, idle);
+ }
+
+ /**
+ * Get the number of IOW ticks.
+ */
+ public int getIow() {
+ return (Integer) getAttribute(IOW);
+ }
+
+ /**
+ * Set the number of IOW ticks.
+ */
+ public void setIow(int iow) {
+ setAttribute(IOW, iow);
+ }
+
+ /**
+ * Get the number of IRQ ticks.
+ */
+ public int getIrq() {
+ return (Integer) getAttribute(IRQ);
+ }
+
+ /**
+ * Set the number of IRQ ticks.
+ */
+ public void setIrq(int irq) {
+ setAttribute(IRQ, irq);
+ }
+
+ /**
+ * Get the number of SIRQ ticks.
+ */
+ public int getSirq() {
+ return (Integer) getAttribute(SIRQ);
+ }
+
+ /**
+ * Set the number of SIRQ ticks.
+ */
+ public void setSirq(int sirq) {
+ setAttribute(SIRQ, sirq);
+ }
+
+ /**
+ * Get the number of total ticks.
+ */
+ public int getTotal() {
+ return (Integer) getAttribute(TOTAL);
+ }
+
+ /**
+ * Set the number of total ticks.
+ */
+ public void setTotal(int total) {
+ setAttribute(TOTAL, total);
+ }
+}
diff --git a/src/com/android/loganalysis/parser/BugreportParser.java b/src/com/android/loganalysis/parser/BugreportParser.java
index 748f751..aed156c 100644
--- a/src/com/android/loganalysis/parser/BugreportParser.java
+++ b/src/com/android/loganalysis/parser/BugreportParser.java
@@ -17,12 +17,14 @@
import com.android.loganalysis.item.AnrItem;
import com.android.loganalysis.item.BugreportItem;
+import com.android.loganalysis.item.DumpsysItem;
import com.android.loganalysis.item.GenericLogcatItem;
import com.android.loganalysis.item.IItem;
import com.android.loganalysis.item.LogcatItem;
import com.android.loganalysis.item.MemInfoItem;
import com.android.loganalysis.item.ProcrankItem;
import com.android.loganalysis.item.SystemPropsItem;
+import com.android.loganalysis.item.TopItem;
import com.android.loganalysis.item.TracesItem;
import java.io.BufferedReader;
@@ -42,10 +44,12 @@
public class BugreportParser extends AbstractSectionParser {
private static final String MEM_INFO_SECTION_REGEX = "------ MEMORY INFO .*";
private static final String PROCRANK_SECTION_REGEX = "------ PROCRANK .*";
+ private static final String TOP_SECTION_REGEX = "------ CPU INFO .*";
private static final String SYSTEM_PROP_SECTION_REGEX = "------ SYSTEM PROPERTIES .*";
private static final String SYSTEM_LOG_SECTION_REGEX =
"------ (SYSTEM|MAIN|MAIN AND SYSTEM) LOG .*";
private static final String ANR_TRACES_SECTION_REGEX = "------ VM TRACES AT LAST ANR .*";
+ private static final String DUMPSYS_SECTION_REGEX = "------ DUMPSYS .*";
private static final String NOOP_SECTION_REGEX = "------ .*";
/**
@@ -113,9 +117,11 @@
});
addSectionParser(new MemInfoParser(), MEM_INFO_SECTION_REGEX);
addSectionParser(new ProcrankParser(), PROCRANK_SECTION_REGEX);
+ addSectionParser(new TopParser(), TOP_SECTION_REGEX);
addSectionParser(new SystemPropsParser(), SYSTEM_PROP_SECTION_REGEX);
addSectionParser(new TracesParser(), ANR_TRACES_SECTION_REGEX);
addSectionParser(mLogcatParser, SYSTEM_LOG_SECTION_REGEX);
+ addSectionParser(new DumpsysParser(), DUMPSYS_SECTION_REGEX);
addSectionParser(new NoopParser(), NOOP_SECTION_REGEX);
}
@@ -130,8 +136,10 @@
if (mBugreport != null) {
mBugreport.setMemInfo((MemInfoItem) getSection(MemInfoItem.TYPE));
mBugreport.setProcrank((ProcrankItem) getSection(ProcrankItem.TYPE));
+ mBugreport.setTop((TopItem) getSection(TopItem.TYPE));
mBugreport.setSystemLog((LogcatItem) getSection(LogcatItem.TYPE));
mBugreport.setSystemProps((SystemPropsItem) getSection(SystemPropsItem.TYPE));
+ mBugreport.setDumpsys((DumpsysItem) getSection(DumpsysItem.TYPE));
if (mBugreport.getSystemLog() != null && mBugreport.getProcrank() != null) {
for (IItem item : mBugreport.getSystemLog().getEvents()) {
diff --git a/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java b/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java
new file mode 100644
index 0000000..5109966
--- /dev/null
+++ b/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java
@@ -0,0 +1,178 @@
+/*
+ * 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.parser;
+
+import com.android.loganalysis.item.DumpsysBatteryInfoItem;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A {@link IParser} to handle the "dumpsys batteryinfo" command output.
+ */
+public class DumpsysBatteryInfoParser implements IParser {
+ private static final Pattern LAST_UNPLUGGED_START_PAT = Pattern.compile(
+ "^Statistics since last unplugged:$");
+ private static final Pattern WAKE_LOCK_START_PAT = Pattern.compile(
+ "^ All partial wake locks:$");
+
+ private static final String WAKE_LOCK_PAT_SUFFIX =
+ "((\\d+)d )?((\\d+)h )?((\\d+)m )?((\\d+)s )?((\\d+)ms )?\\((\\d+) times\\) realtime";
+
+ /**
+ * Match a valid line such as:
+ * " Kernel Wake lock \"Process\": 1d 2h 3m 4s 5ms (6 times) realtime";
+ */
+ private static final Pattern KERNEL_WAKE_LOCK_PAT = Pattern.compile(
+ "^ Kernel Wake lock \"([^\"]+)\": " + WAKE_LOCK_PAT_SUFFIX);
+ /**
+ * Match a valid line such as:
+ * " Wake lock #1234 Process: 1d 2h 3m 4s 5ms (6 times) realtime";
+ */
+ private static final Pattern WAKE_LOCK_PAT = Pattern.compile(
+ "^ Wake lock #(\\d+) (.+): " + WAKE_LOCK_PAT_SUFFIX);
+
+ private DumpsysBatteryInfoItem mItem = new DumpsysBatteryInfoItem();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DumpsysBatteryInfoItem parse(List<String> lines) {
+ boolean inLastUnplugged = false;
+ boolean inKernelWakeLock = false;
+ boolean inWakeLock = false;
+
+ // Look for the section for last unplugged statistics. Kernel wakelocks are in the lines
+ // immediately following, until a blank line. Partial wake locks are in their own block,
+ // until a blank line. Return immediately after since there is nothing left to parse.
+ for (String line : lines) {
+ if (!inLastUnplugged) {
+ Matcher m = LAST_UNPLUGGED_START_PAT.matcher(line);
+ if (m.matches()) {
+ inLastUnplugged = true;
+ inKernelWakeLock = true;
+ }
+ } else {
+ if (inKernelWakeLock) {
+ if ("".equals(line.trim())) {
+ inKernelWakeLock = false;
+ } else {
+ parseLastUnpluggedKernelWakeLock(line);
+ }
+ } else if (inWakeLock) {
+ if ("".equals(line.trim())) {
+ inWakeLock = false;
+ return mItem;
+ } else {
+ parseLastUnpluggedWakeLock(line);
+ }
+ } else {
+ Matcher m = WAKE_LOCK_START_PAT.matcher(line);
+ if (m.matches()) {
+ inWakeLock = true;
+ }
+ }
+ }
+ }
+ return mItem;
+ }
+
+ /**
+ * Parse a line of output and add it to the last unplugged kernel wake lock section.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ void parseLastUnpluggedKernelWakeLock(String line) {
+ Matcher m = KERNEL_WAKE_LOCK_PAT.matcher(line);
+ if (!m.matches()) {
+ return;
+ }
+
+ final String name = m.group(1);
+ final long days = parseLongOrZero(m.group(3));
+ final long hours = parseLongOrZero(m.group(5));
+ final long mins = parseLongOrZero(m.group(7));
+ final long secs = parseLongOrZero(m.group(9));
+ final long msecs = parseLongOrZero(m.group(11));
+ final int timesCalled = Integer.parseInt(m.group(12));
+
+ mItem.addLastUnpluggedKernelWakeLock(name, getMs(days, hours, mins, secs, msecs),
+ timesCalled);
+ }
+
+ /**
+ * Parse a line of output and add it to the last unplugged wake lock section.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ void parseLastUnpluggedWakeLock(String line) {
+ Matcher m = WAKE_LOCK_PAT.matcher(line);
+ if (!m.matches()) {
+ return;
+ }
+
+ final int number = Integer.parseInt(m.group(1));
+ final String name = m.group(2);
+ final long days = parseLongOrZero(m.group(4));
+ final long hours = parseLongOrZero(m.group(6));
+ final long mins = parseLongOrZero(m.group(8));
+ final long secs = parseLongOrZero(m.group(10));
+ final long msecs = parseLongOrZero(m.group(12));
+ final int timesCalled = Integer.parseInt(m.group(13));
+
+ mItem.addLastUnpluggedWakeLock(name, number, getMs(days, hours, mins, secs, msecs),
+ timesCalled);
+ }
+
+ /**
+ * Get the {@link DumpsysBatteryInfoItem}.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ DumpsysBatteryInfoItem getItem() {
+ return mItem;
+ }
+
+ /**
+ * Convert days/hours/mins/secs/msecs into milliseconds.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ static long getMs(long days, long hours, long mins, long secs, long msecs) {
+ return (((24 * days + hours) * 60 + mins) * 60 + secs) * 1000 + msecs;
+ }
+
+ /**
+ * Parses a string into a long, or returns 0 if the string is null.
+ *
+ * @param s a {@link String} containing the long representation to be parsed
+ * @return the long represented by the argument in decimal, or 0 if the string is {@link null}.
+ * @throws NumberFormatException if the string is not {@link null} or does not contain a
+ * parsable long.
+ */
+ private long parseLongOrZero(String s) {
+ if (s == null) {
+ return 0;
+ }
+ return Long.parseLong(s);
+ }
+}
diff --git a/src/com/android/loganalysis/parser/DumpsysParser.java b/src/com/android/loganalysis/parser/DumpsysParser.java
new file mode 100644
index 0000000..151db33
--- /dev/null
+++ b/src/com/android/loganalysis/parser/DumpsysParser.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.parser;
+
+import com.android.loganalysis.item.DumpsysBatteryInfoItem;
+import com.android.loganalysis.item.DumpsysItem;
+
+import java.util.List;
+
+/**
+ * A {@link IParser} to handle the output of the dumpsys section of the bugreport.
+ */
+public class DumpsysParser extends AbstractSectionParser {
+ private static final String BATTERY_INFO_SECTION_REGEX = "DUMP OF SERVICE batteryinfo:";
+ private static final String NOOP_SECTION_REGEX = "DUMP OF SERVICE .*";
+
+ private DumpsysItem mDumpsys = new DumpsysItem();
+
+ /**
+ * {@inheritDoc}
+ *
+ * @return The {@link DumpsysItem}
+ */
+ public DumpsysItem parse(List<String> lines) {
+ setup();
+ for (String line : lines) {
+ parseLine(line);
+ }
+ commit();
+
+ return mDumpsys;
+ }
+
+ /**
+ * Sets up the parser by adding the section parsers.
+ */
+ protected void setup() {
+ addSectionParser(new DumpsysBatteryInfoParser(), BATTERY_INFO_SECTION_REGEX);
+ addSectionParser(new NoopParser(), NOOP_SECTION_REGEX);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void commit() {
+ // signal EOF
+ super.commit();
+
+ if (mDumpsys != null) {
+ mDumpsys.setBatteryInfo(
+ (DumpsysBatteryInfoItem) getSection(DumpsysBatteryInfoItem.TYPE));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/loganalysis/parser/TopParser.java b/src/com/android/loganalysis/parser/TopParser.java
new file mode 100644
index 0000000..e5022c3
--- /dev/null
+++ b/src/com/android/loganalysis/parser/TopParser.java
@@ -0,0 +1,63 @@
+/*
+ * 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.parser;
+
+import com.android.loganalysis.item.TopItem;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A {@link IParser} to handle the output of the top command output.
+ * <p>
+ * The parser only record the last top entry if multiple entries are printed by the top command. It
+ * only parses the total cpu usage.
+ * </p>
+ */
+public class TopParser implements IParser {
+
+ /**
+ * Match a valid cpu ticks line, such as:
+ * "User 5 + Nice 0 + Sys 14 + Idle 207 + IOW 0 + IRQ 0 + SIRQ 0 = 226"
+ */
+ private static final Pattern TICKS_PAT = Pattern.compile(
+ "User (\\d+) \\+ Nice (\\d+) \\+ Sys (\\d+) \\+ Idle (\\d+) \\+ IOW (\\d+) \\+ " +
+ "IRQ (\\d+) \\+ SIRQ (\\d+) = (\\d+)");
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public TopItem parse(List<String> lines) {
+ TopItem item = new TopItem();
+
+ for (String line : lines) {
+ Matcher m = TICKS_PAT.matcher(line);
+ if (m.matches()) {
+ item.setUser(Integer.parseInt(m.group(1)));
+ item.setNice(Integer.parseInt(m.group(2)));
+ item.setSystem(Integer.parseInt(m.group(3)));
+ item.setIdle(Integer.parseInt(m.group(4)));
+ item.setIow(Integer.parseInt(m.group(5)));
+ item.setIrq(Integer.parseInt(m.group(6)));
+ item.setSirq(Integer.parseInt(m.group(7)));
+ item.setTotal(Integer.parseInt(m.group(8)));
+ }
+ }
+ return item;
+ }
+}
diff --git a/tests/src/com/android/loganalysis/UnitTests.java b/tests/src/com/android/loganalysis/UnitTests.java
index 083d571..09a42d9 100644
--- a/tests/src/com/android/loganalysis/UnitTests.java
+++ b/tests/src/com/android/loganalysis/UnitTests.java
@@ -20,6 +20,8 @@
import com.android.loganalysis.parser.AbstractSectionParserTest;
import com.android.loganalysis.parser.AnrParserTest;
import com.android.loganalysis.parser.BugreportParserTest;
+import com.android.loganalysis.parser.DumpsysBatteryInfoParserTest;
+import com.android.loganalysis.parser.DumpsysParserTest;
import com.android.loganalysis.parser.JavaCrashParserTest;
import com.android.loganalysis.parser.LogcatParserTest;
import com.android.loganalysis.parser.MemInfoParserTest;
@@ -27,6 +29,7 @@
import com.android.loganalysis.parser.NativeCrashParserTest;
import com.android.loganalysis.parser.ProcrankParserTest;
import com.android.loganalysis.parser.SystemPropsParserTest;
+import com.android.loganalysis.parser.TopParserTest;
import com.android.loganalysis.parser.TracesParserTest;
import com.android.loganalysis.util.ArrayUtilTest;
import com.android.loganalysis.util.RegexTrieTest;
@@ -51,6 +54,8 @@
addTestSuite(AbstractSectionParserTest.class);
addTestSuite(AnrParserTest.class);
addTestSuite(BugreportParserTest.class);
+ addTestSuite(DumpsysParserTest.class);
+ addTestSuite(DumpsysBatteryInfoParserTest.class);
addTestSuite(JavaCrashParserTest.class);
addTestSuite(LogcatParserTest.class);
addTestSuite(MemInfoParserTest.class);
@@ -58,6 +63,7 @@
addTestSuite(NativeCrashParserTest.class);
addTestSuite(ProcrankParserTest.class);
addTestSuite(SystemPropsParserTest.class);
+ addTestSuite(TopParserTest.class);
addTestSuite(TracesParserTest.class);
// util
diff --git a/tests/src/com/android/loganalysis/parser/BugreportParserTest.java b/tests/src/com/android/loganalysis/parser/BugreportParserTest.java
index b5f973e..5edf61a 100644
--- a/tests/src/com/android/loganalysis/parser/BugreportParserTest.java
+++ b/tests/src/com/android/loganalysis/parser/BugreportParserTest.java
@@ -101,6 +101,16 @@
"----- end 2887 -----",
"",
"------ SECTION ------",
+ "",
+ "------ DUMPSYS (dumpsys) ------",
+ "DUMP OF SERVICE batteryinfo:",
+ "Statistics since last unplugged:",
+ " Kernel Wake lock \"PowerManagerService.WakeLocks\": 5m 10s 61ms (2 times) realtime",
+ " Kernel Wake lock \"pm8921_eoc\": 9s 660ms (0 times) realtime",
+ "",
+ " All partial wake locks:",
+ " Wake lock #0 partialWakelock: 5m 9s 260ms (1 times) realtime",
+ " Wake lock #1000 AlarmManager: 422ms (7 times) realtime",
"");
BugreportItem bugreport = new BugreportParser().parse(lines);
@@ -122,6 +132,9 @@
assertNotNull(bugreport.getSystemProps());
assertEquals(4, bugreport.getSystemProps().size());
+
+ assertNotNull(bugreport.getDumpsys());
+ assertNotNull(bugreport.getDumpsys().getBatteryInfo());
}
/**
diff --git a/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java b/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java
new file mode 100644
index 0000000..dec82fa
--- /dev/null
+++ b/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.parser;
+
+import com.android.loganalysis.item.DumpsysBatteryInfoItem;
+import com.android.loganalysis.item.DumpsysBatteryInfoItem.WakeLock;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link DumpsysBatteryInfoParser}
+ */
+public class DumpsysBatteryInfoParserTest extends TestCase {
+
+ /**
+ * Test that complete battery info dumpsys is parsed.
+ */
+ public void testParse() {
+ List<String> inputBlock = Arrays.asList(
+ "Battery History:",
+ " -15m07s754ms START",
+ " -15m05s119ms 088 20080000 status=charging health=good plug=usb temp=269 volt=4358 +plugged +sensor",
+ "",
+ "Per-PID Stats:",
+ " PID 4242 wake time: +5m10s24ms",
+ " PID 543 wake time: +3s585ms",
+ "",
+ "Statistics since last charge:",
+ " System starts: 2, currently on battery: false",
+ " Time on battery: 8m 20s 142ms (55.1%) realtime, 5m 17s 5ms (34.9%) uptime",
+ " Kernel Wake lock \"PowerManagerService.WakeLocks\": 5m 10s 61ms (2 times) realtime",
+ " Kernel Wake lock \"pm8921_eoc\": 9s 660ms (0 times) realtime",
+ " ",
+ " All partial wake locks:",
+ " Wake lock #0 partialWakelock: 5m 9s 260ms (1 times) realtime",
+ " Wake lock #1000 AlarmManager: 422ms (7 times) realtime",
+ "",
+ "Statistics since last unplugged:",
+ " Time on battery: 8m 20s 142ms (92.6%) realtime, 5m 17s 5ms (58.7%) uptime",
+ " Total run time: 8m 59s 968ms realtime, 5m 56s 831ms uptime, ",
+ " Screen on: 0ms (0.0%), Input events: 0, Active phone call: 0ms (0.0%)",
+ " Screen brightnesses: No activity",
+ " Kernel Wake lock \"PowerManagerService.WakeLocks\": 5m 10s 61ms (2 times) realtime",
+ " Kernel Wake lock \"pm8921_eoc\": 9s 660ms (0 times) realtime",
+ " Kernel Wake lock \"main\": 7s 323ms (0 times) realtime",
+ " Total received: 0B, Total sent: 0B",
+ " Total full wakelock time: 0ms , Total partial wakelock time: 5m 10s 60ms ",
+ " Signal levels: No activity",
+ " Signal scanning time: 0ms ",
+ " Radio types: none 8m 20s 142ms (100.0%) 0x",
+ " Radio data uptime when unplugged: 0 ms",
+ " Wifi on: 0ms (0.0%), Wifi running: 0ms (0.0%), Bluetooth on: 0ms (0.0%)",
+ " ",
+ " Device is currently plugged into power",
+ " Last discharge cycle start level: 87",
+ " Last discharge cycle end level: 87",
+ " Amount discharged while screen on: 0",
+ " Amount discharged while screen off: 0",
+ " ",
+ " All partial wake locks:",
+ " Wake lock #0 partialWakelock: 5m 9s 260ms (1 times) realtime",
+ " Wake lock #1000 AlarmManager: 422ms (7 times) realtime",
+ " Wake lock #1000 show keyguard: 277ms (1 times) realtime",
+ " Wake lock #1000 ActivityManager-Sleep: 72ms (1 times) realtime",
+ " Wake lock #10015 AlarmManager: 16ms (1 times) realtime",
+ "",
+ " #0:",
+ " Wake lock partialWakelock: 5m 9s 260ms partial (1 times) realtime",
+ " Proc /init:",
+ " CPU: 10ms usr + 0ms krn",
+ " Proc flush-179:0:",
+ " CPU: 0ms usr + 10ms krn",
+ " Proc vold:",
+ " CPU: 20ms usr + 10ms krn",
+ " #1000:",
+ " User activity: 3 other, 1 button",
+ " Wake lock show keyguard: 277ms partial (1 times) realtime",
+ " Wake lock AlarmManager: 422ms partial (7 times) realtime");
+
+ DumpsysBatteryInfoParser parser = new DumpsysBatteryInfoParser();
+ DumpsysBatteryInfoItem item = parser.parse(inputBlock);
+
+ assertEquals(5, item.getLastUnpluggedWakeLock().size());
+ assertEquals(3, item.getLastUnpluggedKernelWakeLock().size());
+
+ assertEquals("partialWakelock",
+ item.getLastUnpluggedWakeLock().get(0).getName());
+ assertEquals("PowerManagerService.WakeLocks",
+ item.getLastUnpluggedKernelWakeLock().get(0).getName());
+ }
+
+ /**
+ * Test that kernel wakelocks are parsed.
+ */
+ public void testParseKernelWakeLock() {
+ String inputLine = " Kernel Wake lock \"Process\": 1d 2h 3m 4s 5ms (6 times) realtime";
+
+ DumpsysBatteryInfoParser parser = new DumpsysBatteryInfoParser();
+ parser.parseLastUnpluggedKernelWakeLock(inputLine);
+ DumpsysBatteryInfoItem item = parser.getItem();
+
+ assertEquals(1, item.getLastUnpluggedKernelWakeLock().size());
+ WakeLock wakeLock = item.getLastUnpluggedKernelWakeLock().get(0);
+ assertEquals("Process", wakeLock.getName());
+ assertNull(wakeLock.getNumber());
+ assertEquals(DumpsysBatteryInfoParser.getMs(1, 2, 3, 4, 5), wakeLock.getHeldTime());
+ assertEquals(6, wakeLock.getLockedCount());
+
+ inputLine = " Kernel Wake lock \"Process\": 5m 7ms (2 times) realtime";
+
+ parser = new DumpsysBatteryInfoParser();
+ parser.parseLastUnpluggedKernelWakeLock(inputLine);
+ item = parser.getItem();
+
+ assertEquals(1, item.getLastUnpluggedKernelWakeLock().size());
+ wakeLock = item.getLastUnpluggedKernelWakeLock().get(0);
+ assertEquals("Process", wakeLock.getName());
+ assertNull(wakeLock.getNumber());
+ assertEquals(5 * 60 * 1000 + 7, wakeLock.getHeldTime());
+ assertEquals(2, wakeLock.getLockedCount());
+ }
+
+ /**
+ * Test that wake locks are parsed.
+ */
+ public void testParseWakeLock() {
+ String inputLine = " Wake lock #1234 Process: 1d 2h 3m 4s 5ms (6 times) realtime";
+
+ DumpsysBatteryInfoParser parser = new DumpsysBatteryInfoParser();
+ parser.parseLastUnpluggedWakeLock(inputLine);
+ DumpsysBatteryInfoItem item = parser.getItem();
+
+ assertEquals(1, item.getLastUnpluggedWakeLock().size());
+ WakeLock wakeLock = item.getLastUnpluggedWakeLock().get(0);
+ assertEquals("Process", wakeLock.getName());
+ assertEquals((Integer) 1234, wakeLock.getNumber());
+ assertEquals(DumpsysBatteryInfoParser.getMs(1, 2, 3, 4, 5), wakeLock.getHeldTime());
+ assertEquals(6, wakeLock.getLockedCount());
+
+ inputLine = " Wake lock #1234 Process:with:colons: 5m 7ms (2 times) realtime";
+
+ parser = new DumpsysBatteryInfoParser();
+ parser.parseLastUnpluggedWakeLock(inputLine);
+ item = parser.getItem();
+
+ assertEquals(1, item.getLastUnpluggedWakeLock().size());
+ wakeLock = item.getLastUnpluggedWakeLock().get(0);
+ assertEquals("Process:with:colons", wakeLock.getName());
+ assertEquals((Integer) 1234, wakeLock.getNumber());
+ assertEquals(5 * 60 * 1000 + 7, wakeLock.getHeldTime());
+ assertEquals(2, wakeLock.getLockedCount());
+ }
+
+ /**
+ * Test the helper function to covert time to ms.
+ */
+ public void testGetMs() {
+ assertEquals(1, DumpsysBatteryInfoParser.getMs(0, 0, 0, 0, 1));
+ assertEquals(1000, DumpsysBatteryInfoParser.getMs(0, 0, 0, 1, 0));
+ assertEquals(60 * 1000, DumpsysBatteryInfoParser.getMs(0, 0, 1, 0, 0));
+ assertEquals(60 * 60 * 1000, DumpsysBatteryInfoParser.getMs(0, 1, 0, 0, 0));
+ assertEquals(24 * 60 * 60 * 1000, DumpsysBatteryInfoParser.getMs(1, 0, 0, 0, 0));
+ }
+}
+
diff --git a/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java b/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java
new file mode 100644
index 0000000..9561b7b
--- /dev/null
+++ b/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.parser;
+
+import com.android.loganalysis.item.DumpsysItem;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link DumpsysParser}
+ */
+public class DumpsysParserTest extends TestCase {
+
+ /**
+ * Test that the dumpsys section of the bugreport is parsed.
+ */
+ public void testParse() {
+ List<String> inputBlock = Arrays.asList(
+ "-------------------------------------------------------------------------------",
+ "DUMP OF SERVICE process1:",
+ "-------------------------------------------------------------------------------",
+ "DUMP OF SERVICE batteryinfo:",
+ "Statistics since last unplugged:",
+ " Kernel Wake lock \"PowerManagerService.WakeLocks\": 5m 10s 61ms (2 times) realtime",
+ " Kernel Wake lock \"pm8921_eoc\": 9s 660ms (0 times) realtime",
+ "",
+ " All partial wake locks:",
+ " Wake lock #0 partialWakelock: 5m 9s 260ms (1 times) realtime",
+ " Wake lock #1000 AlarmManager: 422ms (7 times) realtime",
+ "",
+ "-------------------------------------------------------------------------------",
+ "DUMP OF SERVICE process2:",
+ "-------------------------------------------------------------------------------");
+
+ DumpsysParser parser = new DumpsysParser();
+ DumpsysItem item = parser.parse(inputBlock);
+
+ assertNotNull(item.getBatteryInfo());
+ assertEquals(2, item.getBatteryInfo().getLastUnpluggedWakeLock().size());
+ assertEquals(2, item.getBatteryInfo().getLastUnpluggedKernelWakeLock().size());
+ }
+}
diff --git a/tests/src/com/android/loganalysis/parser/TopParserTest.java b/tests/src/com/android/loganalysis/parser/TopParserTest.java
new file mode 100644
index 0000000..0f67849
--- /dev/null
+++ b/tests/src/com/android/loganalysis/parser/TopParserTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.parser;
+
+import com.android.loganalysis.item.TopItem;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Unit tests for {@link ProcrankParser}
+ */
+public class TopParserTest extends TestCase {
+
+ /**
+ * Test that the output of the top command is parsed.
+ */
+ public void testTopParser() {
+ List<String> inputBlock = Arrays.asList(
+ "User 20%, System 20%, IOW 5%, IRQ 3%",
+ "User 150 + Nice 50 + Sys 200 + Idle 510 + IOW 60 + IRQ 5 + SIRQ 25 = 1000",
+ "",
+ " PID TID PR CPU% S VSS RSS PCY UID Thread Proc",
+ " 4474 4474 0 2% R 1420K 768K shell top top");
+ TopParser parser = new TopParser();
+ TopItem item = parser.parse(inputBlock);
+
+ assertEquals(150, item.getUser());
+ assertEquals(50, item.getNice());
+ assertEquals(200, item.getSystem());
+ assertEquals(510, item.getIdle());
+ assertEquals(60, item.getIow());
+ assertEquals(5, item.getIrq());
+ assertEquals(25, item.getSirq());
+ assertEquals(1000, item.getTotal());
+ }
+
+ /**
+ * Test that the last output is stored.
+ */
+ public void testLastTop() {
+ List<String> inputBlock = Arrays.asList(
+ "User 0 + Nice 0 + Sys 0 + Idle 1000 + IOW 0 + IRQ 0 + SIRQ 0 = 1000",
+ "User 0 + Nice 0 + Sys 0 + Idle 1000 + IOW 0 + IRQ 0 + SIRQ 0 = 1000",
+ "User 150 + Nice 50 + Sys 200 + Idle 510 + IOW 60 + IRQ 5 + SIRQ 25 = 1000");
+
+ TopParser parser = new TopParser();
+ TopItem item = parser.parse(inputBlock);
+
+ assertEquals(150, item.getUser());
+ assertEquals(50, item.getNice());
+ assertEquals(200, item.getSystem());
+ assertEquals(510, item.getIdle());
+ assertEquals(60, item.getIow());
+ assertEquals(5, item.getIrq());
+ assertEquals(25, item.getSirq());
+ assertEquals(1000, item.getTotal());
+ }
+}