summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Rowe <erowe@google.com>2013-04-18 17:24:37 -0700
committerEric Rowe <erowe@google.com>2013-05-02 10:52:55 -0700
commit8bed524a70e5466c6cbdc2726c40324d279c6eaa (patch)
treefacae4364bacd2c875b59e40f6489a1a62cee834
parent857055399e6bc90e0afd5565d7d82dc751e01978 (diff)
downloadloganalysis-8bed524a70e5466c6cbdc2726c40324d279c6eaa.tar.gz
Add heuristic interface and basic heuristics.
Change-Id: Ie355c45e28d1315698e1971235b4a4804e9089cc
-rw-r--r--src/com/android/loganalysis/heuristic/AbstractHeuristic.java106
-rw-r--r--src/com/android/loganalysis/heuristic/AnrHeuristic.java125
-rw-r--r--src/com/android/loganalysis/heuristic/CpuUsageHeuristic.java153
-rw-r--r--src/com/android/loganalysis/heuristic/IHeuristic.java135
-rw-r--r--src/com/android/loganalysis/heuristic/JavaCrashHeuristic.java121
-rw-r--r--src/com/android/loganalysis/heuristic/KernelResetHeuristic.java118
-rw-r--r--src/com/android/loganalysis/heuristic/MemoryUsageHeuristic.java154
-rw-r--r--src/com/android/loganalysis/heuristic/NativeCrashHeuristic.java121
-rw-r--r--src/com/android/loganalysis/heuristic/PowerUsageHeuristic.java233
-rw-r--r--src/com/android/loganalysis/heuristic/RuntimeRestartHeuristic.java241
-rw-r--r--src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java4
-rw-r--r--src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java4
-rw-r--r--tests/src/com/android/loganalysis/UnitTests.java18
-rw-r--r--tests/src/com/android/loganalysis/heuristic/AnrHeuristicTest.java50
-rw-r--r--tests/src/com/android/loganalysis/heuristic/CpuUsageHeuristicTest.java72
-rw-r--r--tests/src/com/android/loganalysis/heuristic/JavaCrashHeuristicTest.java52
-rw-r--r--tests/src/com/android/loganalysis/heuristic/KernelResetHeuristicTest.java55
-rw-r--r--tests/src/com/android/loganalysis/heuristic/MemoryUsageHeuristicTest.java70
-rw-r--r--tests/src/com/android/loganalysis/heuristic/NativeCrashHeuristicTest.java52
-rw-r--r--tests/src/com/android/loganalysis/heuristic/PowerUsageHeuristicTest.java75
-rw-r--r--tests/src/com/android/loganalysis/heuristic/RuntimeRestartHeuristicTest.java128
-rw-r--r--tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java24
-rw-r--r--tests/src/com/android/loganalysis/parser/DumpsysParserTest.java4
23 files changed, 2097 insertions, 18 deletions
diff --git a/src/com/android/loganalysis/heuristic/AbstractHeuristic.java b/src/com/android/loganalysis/heuristic/AbstractHeuristic.java
new file mode 100644
index 0000000..d74ab4a
--- /dev/null
+++ b/src/com/android/loganalysis/heuristic/AbstractHeuristic.java
@@ -0,0 +1,106 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.BugreportItem;
+import com.android.loganalysis.item.DumpsysItem;
+import com.android.loganalysis.item.KernelLogItem;
+import com.android.loganalysis.item.LogcatItem;
+import com.android.loganalysis.item.MemInfoItem;
+import com.android.loganalysis.item.ProcrankItem;
+import com.android.loganalysis.item.TopItem;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Date;
+
+/**
+ * An abstract {@link IHeuristic} which implements empty methods for all the add methods.
+ */
+public abstract class AbstractHeuristic implements IHeuristic {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addBugreport(Date timestamp, BugreportItem bugreport) {
+ // Ignore
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addLogcat(Date timestamp, LogcatItem logcat) {
+ // Ignore
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addKernelLog(Date timestamp, KernelLogItem kernelLog) {
+ // Ignore
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addMemInfo(Date timestamp, MemInfoItem meminfo) {
+ // Ignore
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addProcrank(Date timestamp, ProcrankItem procrank) {
+ // Ignore
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addTop(Date timestamp, TopItem top) {
+ // Ignore
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addDumpsys(Date timestamp, DumpsysItem dumpsys) {
+ // Ignore
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JSONObject toJson() {
+ JSONObject object = new JSONObject();
+ try {
+ object.put(TYPE, getType());
+ object.put(NAME, getName());
+ object.put(STATUS, failed() ? FAILED : PASSED);
+ } catch (JSONException e) {
+ // Ignore
+ }
+ return object;
+ }
+} \ No newline at end of file
diff --git a/src/com/android/loganalysis/heuristic/AnrHeuristic.java b/src/com/android/loganalysis/heuristic/AnrHeuristic.java
new file mode 100644
index 0000000..3ad6ec3
--- /dev/null
+++ b/src/com/android/loganalysis/heuristic/AnrHeuristic.java
@@ -0,0 +1,125 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.AnrItem;
+import com.android.loganalysis.item.LogcatItem;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Date;
+
+/**
+ * A {@link IHeuristic} to detect if there are any ARNs.
+ */
+public class AnrHeuristic extends AbstractHeuristic {
+
+ /** Constant for JSON output */
+ public static final String ANRS = "ANRS";
+
+ private static final String HEURISTIC_NAME = "ANR";
+ private static final String HEURISTIC_TYPE = "ANR_HEURISTIC";
+
+ private LogcatItem mLogcat = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addLogcat(Date timestamp, LogcatItem logcat) {
+ mLogcat = logcat;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean failed() {
+ if (mLogcat == null) {
+ return false;
+ }
+
+ return (mLogcat.getAnrs().size() > 0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getType() {
+ return HEURISTIC_TYPE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return HEURISTIC_NAME;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getSummary() {
+ if (!failed()) {
+ return null;
+ }
+
+ return String.format("Found %d ANR%s", mLogcat.getAnrs().size(),
+ mLogcat.getAnrs().size() == 1 ? "" : "s");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDetails() {
+ if (!failed()) {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (AnrItem anr : mLogcat.getAnrs()) {
+ sb.append(anr.getStack());
+ sb.append("\n\nLast lines of logcat\n");
+ sb.append(anr.getLastPreamble());
+ sb.append(String.format("\n\nLast lines of logcat for PID %d\n", anr.getPid()));
+ sb.append(anr.getProcessPreamble());
+ sb.append("\n\n");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject output = super.toJson();
+ try {
+ JSONArray anrs = new JSONArray();
+ for (AnrItem anr : mLogcat.getAnrs()) {
+ anrs.put(anr.toJson());
+ }
+ output.put(ANRS, anrs);
+ } catch (JSONException e) {
+ // Ignore
+ }
+ return output;
+ }
+}
diff --git a/src/com/android/loganalysis/heuristic/CpuUsageHeuristic.java b/src/com/android/loganalysis/heuristic/CpuUsageHeuristic.java
new file mode 100644
index 0000000..da2f161
--- /dev/null
+++ b/src/com/android/loganalysis/heuristic/CpuUsageHeuristic.java
@@ -0,0 +1,153 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.LogcatItem;
+import com.android.loganalysis.item.TopItem;
+import com.android.loganalysis.parser.LogcatParser;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Date;
+
+/**
+ * A {@link IHeuristic} to detect if the CPU usage is abnormally high.
+ */
+public class CpuUsageHeuristic extends AbstractHeuristic {
+
+ /** Constant for JSON output */
+ public static final String CUTOFF = "CUTOFF";
+ /** Constant for JSON output */
+ public static final String USAGE = "USAGE";
+ /** Constant for JSON output */
+ public static final String TOP = "TOP";
+
+ private static final String HEURISTIC_NAME = "CPU usage";
+ private static final String HEURISTIC_TYPE = "CPU_USAGE_HEURISTIC";
+
+ // TODO: Make this value configurable.
+ private static final double TOP_USAGE_CUTOFF = 0.8;
+
+ private LogcatItem mLogcat = null;
+ private TopItem mTop = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addLogcat(Date timestamp, LogcatItem logcat) {
+ mLogcat = logcat;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addTop(Date timestamp, TopItem top) {
+ mTop = top;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean failed() {
+ if (mTop != null && getUsage() > TOP_USAGE_CUTOFF) {
+ return true;
+ }
+
+ if (mLogcat != null && mLogcat.getMiscEvents(LogcatParser.HIGH_CPU_USAGE).size() > 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getType() {
+ return HEURISTIC_TYPE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return HEURISTIC_NAME;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getSummary() {
+ if (!failed()) {
+ return null;
+ }
+
+ return String.format("CPU usage at %.0f%% (over %.0f%%)", 100.0 * getUsage(),
+ 100.0 * TOP_USAGE_CUTOFF);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDetails() {
+ // TODO: List the top cpu using processes
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject output = super.toJson();
+ try {
+ output.put(CUTOFF, TOP_USAGE_CUTOFF);
+ if (mTop != null) {
+ output.put(USAGE, getUsage());
+ output.put(TOP, mTop.toJson());
+ }
+ } catch (JSONException e) {
+ // Ignore
+ }
+ return output;
+ }
+
+ /**
+ * Get the usage as a fraction between 0 and 1.
+ */
+ private double getUsage() {
+ if (mTop == null) {
+ return 0.0;
+ }
+
+ return ((double) (mTop.getTotal() - mTop.getIdle())) / mTop.getTotal();
+ }
+
+ /**
+ * Get the CPU usage threshold.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ double getCutoff() {
+ return TOP_USAGE_CUTOFF;
+ }
+}
diff --git a/src/com/android/loganalysis/heuristic/IHeuristic.java b/src/com/android/loganalysis/heuristic/IHeuristic.java
new file mode 100644
index 0000000..4bdfab5
--- /dev/null
+++ b/src/com/android/loganalysis/heuristic/IHeuristic.java
@@ -0,0 +1,135 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.BugreportItem;
+import com.android.loganalysis.item.DumpsysItem;
+import com.android.loganalysis.item.IItem;
+import com.android.loganalysis.item.KernelLogItem;
+import com.android.loganalysis.item.LogcatItem;
+import com.android.loganalysis.item.MemInfoItem;
+import com.android.loganalysis.item.ProcrankItem;
+import com.android.loganalysis.item.TopItem;
+
+import org.json.JSONObject;
+
+import java.util.Date;
+
+/**
+ * Interface for all heuristics used to determine if there are any problems in the logs.
+ * <p>
+ * Certain heuristics will care about different {@link IItem}s. For example, a heuristic looking
+ * for runtime restarts will look at the logcat and procrank but ignore the rest. It is recommended
+ * to use {@link AbstractHeuristic} and implement the methods to only add the {@link IItem}s that
+ * the heuristic cares about.
+ * </p><p>
+ * The {@code add()} methods are used to add {@link IItem}s which the heuristic cares about. When
+ * {@link #failed()} is called, it will only evaluate the {@link IItem}s which have been
+ * added. The same is true for {{@link #getSummary()} and {{@link #getDetails()}.
+ * </p>
+ */
+public interface IHeuristic {
+
+ /** Constant for JSON output */
+ public static final String TYPE = "TYPE";
+ /** Constant for JSON output */
+ public static final String NAME = "NAME";
+ /** Constant for JSON output */
+ public static final String STATUS = "STATUS";
+ /** Constant for JSON output */
+ public static final String PASSED = "PASSED";
+ /** Constant for JSON output */
+ public static final String FAILED = "FAILED";
+
+ /**
+ * Add a bugreport item to be checked.
+ */
+ public void addBugreport(Date timestamp, BugreportItem bugreport);
+
+ /**
+ * Add a logcat item to be checked.
+ */
+ public void addLogcat(Date timestamp, LogcatItem logcat);
+
+ /**
+ * Add a kernel log item to be checked.
+ */
+ public void addKernelLog(Date timestamp, KernelLogItem kernelLog);
+
+ /**
+ * Add a memory info item to be checked.
+ */
+ public void addMemInfo(Date timestamp, MemInfoItem meminfo);
+
+ /**
+ * Add a procrank item to be checked.
+ */
+ public void addProcrank(Date timestamp, ProcrankItem procrank);
+
+ /**
+ * Add a top item to be checked.
+ */
+ public void addTop(Date timestamp, TopItem top);
+
+ /**
+ * Add a dumpsys item to be checked.
+ */
+ public void addDumpsys(Date timestamp, DumpsysItem dumpsys);
+
+ /**
+ * Checks to see if there are any problems.
+ *
+ * @return {@code true} if there is a problem, {@code false} if there is not.
+ */
+ public boolean failed();
+
+ /**
+ * Get the type of the heuristic.
+ *
+ * @return The type.
+ */
+ public String getType();
+
+ /**
+ * Get the name of the heuristic.
+ *
+ * @return The name of the heuristic, to be used for the output.
+ */
+ public String getName();
+
+ /**
+ * Get the summary of the problem.
+ *
+ * @return The summary as a string or {@code null} if there is no problem.
+ */
+ public String getSummary();
+
+ /**
+ * Get the problem details.
+ *
+ * @return The details as a string or {@code null} if there is no problem.
+ */
+ public String getDetails();
+
+ /**
+ * Get the JSON representation of the problem.
+ *
+ * @return The {@link JSONObject} representing the heuristic. JSON object must contain at least
+ * the key {@link #STATUS} with either the value {@link #PASSED} or {@link #FAILED}, as well as
+ * any additional information about the status.
+ */
+ public JSONObject toJson();
+}
diff --git a/src/com/android/loganalysis/heuristic/JavaCrashHeuristic.java b/src/com/android/loganalysis/heuristic/JavaCrashHeuristic.java
new file mode 100644
index 0000000..4c32c4d
--- /dev/null
+++ b/src/com/android/loganalysis/heuristic/JavaCrashHeuristic.java
@@ -0,0 +1,121 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.JavaCrashItem;
+import com.android.loganalysis.item.LogcatItem;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Date;
+
+/**
+ * A {@link IHeuristic} to detect if there are any Java crashes.
+ */
+public class JavaCrashHeuristic extends AbstractHeuristic {
+
+ /** Constant for JSON output */
+ public static final String JAVA_CRASHES = "JAVA_CRASHES";
+
+ private static final String HEURISTIC_NAME = "Java crash";
+ private static final String HEURISTIC_TYPE = "JAVA_CRASH_HEURISTIC";
+
+ private LogcatItem mLogcat = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addLogcat(Date timestamp, LogcatItem logcat) {
+ mLogcat = logcat;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean failed() {
+ if (mLogcat == null) {
+ return false;
+ }
+
+ return (mLogcat.getJavaCrashes().size() > 0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getType() {
+ return HEURISTIC_TYPE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return HEURISTIC_NAME;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getSummary() {
+ if (!failed()) {
+ return null;
+ }
+
+ return String.format("Found %d Java crash%s", mLogcat.getJavaCrashes().size(),
+ mLogcat.getJavaCrashes().size() == 1 ? "" : "es");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDetails() {
+ if (!failed()) {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (JavaCrashItem crash : mLogcat.getJavaCrashes()) {
+ sb.append(crash.getStack());
+ sb.append("\n\n");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject output = super.toJson();
+ try {
+ JSONArray javaCrashes = new JSONArray();
+ for (JavaCrashItem jc : mLogcat.getJavaCrashes()) {
+ javaCrashes.put(jc.toJson());
+ }
+ output.put(JAVA_CRASHES, javaCrashes);
+ } catch (JSONException e) {
+ // Ignore
+ }
+ return output;
+ }
+}
diff --git a/src/com/android/loganalysis/heuristic/KernelResetHeuristic.java b/src/com/android/loganalysis/heuristic/KernelResetHeuristic.java
new file mode 100644
index 0000000..ba6a08c
--- /dev/null
+++ b/src/com/android/loganalysis/heuristic/KernelResetHeuristic.java
@@ -0,0 +1,118 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.KernelLogItem;
+import com.android.loganalysis.item.MiscKernelLogItem;
+import com.android.loganalysis.parser.KernelLogParser;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Date;
+
+/**
+ * A {@link IHeuristic} to detect if there are any kernel resets.
+ */
+public class KernelResetHeuristic extends AbstractHeuristic {
+
+ /** Constant for JSON output */
+ public static final String KERNEL_RESETS = "KERNEL_RESETS";
+
+ private static final String HEURISTIC_NAME = "Kernel reset";
+ private static final String HEURISTIC_TYPE = "KERNEL_RESET_HEURISTIC";
+
+ private KernelLogItem mKernelLog = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addKernelLog(Date timestamp, KernelLogItem kernelLog) {
+ mKernelLog = kernelLog;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean failed() {
+ return (mKernelLog != null &&
+ mKernelLog.getMiscEvents(KernelLogParser.KERNEL_RESET).size() > 0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getType() {
+ return HEURISTIC_TYPE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return HEURISTIC_NAME;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getSummary() {
+ if (!failed()) {
+ return null;
+ }
+
+ return "Found a kernel reset";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDetails() {
+ if (!failed()) {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (MiscKernelLogItem item : mKernelLog.getMiscEvents(KernelLogParser.KERNEL_RESET)) {
+ sb.append(String.format("Reason: %s, Time: %.6f\nPreamble:\n%s\n\n", item.getMessage(),
+ item.getEventTime(), item.getPreamble()));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject output = super.toJson();
+ try {
+ JSONArray kernelResets = new JSONArray();
+ for (MiscKernelLogItem item : mKernelLog.getMiscEvents(KernelLogParser.KERNEL_RESET)) {
+ kernelResets.put(item.toJson());
+ }
+ output.put(KERNEL_RESETS, kernelResets);
+ } catch (JSONException e) {
+ // Ignore
+ }
+ return output;
+ }
+}
diff --git a/src/com/android/loganalysis/heuristic/MemoryUsageHeuristic.java b/src/com/android/loganalysis/heuristic/MemoryUsageHeuristic.java
new file mode 100644
index 0000000..1355390
--- /dev/null
+++ b/src/com/android/loganalysis/heuristic/MemoryUsageHeuristic.java
@@ -0,0 +1,154 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.LogcatItem;
+import com.android.loganalysis.item.MemInfoItem;
+import com.android.loganalysis.parser.LogcatParser;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Date;
+
+/**
+ * A {@link IHeuristic} to detect if the memory usage is abnormally high.
+ */
+public class MemoryUsageHeuristic extends AbstractHeuristic {
+
+ /** Constant for JSON output */
+ public static final String CUTOFF = "CUTOFF";
+ /** Constant for JSON output */
+ public static final String USAGE = "USAGE";
+ /** Constant for JSON output */
+ public static final String MEM_INFO = "MEM_INFO";
+
+ private static final String HEURISTIC_NAME = "Memory usage";
+ private static final String HEURISTIC_TYPE = "MEMORY_USAGE_HEURISTIC";
+
+ // TODO: Make this value configurable.
+ private static final double MEMORY_USAGE_CUTOFF = 0.99;
+
+ private LogcatItem mLogcat = null;
+ private MemInfoItem mMemInfo = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addLogcat(Date timestamp, LogcatItem logcat) {
+ mLogcat = logcat;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addMemInfo(Date timestamp, MemInfoItem top) {
+ mMemInfo = top;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean failed() {
+ if (mMemInfo != null && getUsage() > MEMORY_USAGE_CUTOFF) {
+ return true;
+ }
+
+ if (mLogcat != null && mLogcat.getMiscEvents(LogcatParser.HIGH_MEMORY_USAGE).size() > 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getType() {
+ return HEURISTIC_TYPE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return HEURISTIC_NAME;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getSummary() {
+ if (!failed()) {
+ return null;
+ }
+
+ return String.format("Memory usage at %.0f%% (over %.0f%%)", 100.0 * getUsage(),
+ 100.0 * MEMORY_USAGE_CUTOFF);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDetails() {
+ // TODO: List the top memory using processes.
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject output = super.toJson();
+ try {
+ output.put(CUTOFF, MEMORY_USAGE_CUTOFF);
+ if (mMemInfo != null) {
+ output.put(USAGE, getUsage());
+ output.put(MEM_INFO, mMemInfo.toJson());
+ }
+ } catch (JSONException e) {
+ // Ignore
+ }
+ return output;
+ }
+
+ /**
+ * Get the memory usage as a fraction between 0 and 1.
+ */
+ private double getUsage() {
+ if (mMemInfo == null) {
+ return 0.0;
+ }
+
+ return ((double) (mMemInfo.get("MemTotal") - mMemInfo.get("MemFree"))) /
+ mMemInfo.get("MemTotal");
+ }
+
+ /**
+ * Get the memory usage threshold.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ double getCutoff() {
+ return MEMORY_USAGE_CUTOFF;
+ }
+}
diff --git a/src/com/android/loganalysis/heuristic/NativeCrashHeuristic.java b/src/com/android/loganalysis/heuristic/NativeCrashHeuristic.java
new file mode 100644
index 0000000..a10982c
--- /dev/null
+++ b/src/com/android/loganalysis/heuristic/NativeCrashHeuristic.java
@@ -0,0 +1,121 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.LogcatItem;
+import com.android.loganalysis.item.NativeCrashItem;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Date;
+
+/**
+ * A {@link IHeuristic} to detect if there are any native crashes.
+ */
+public class NativeCrashHeuristic extends AbstractHeuristic {
+
+ /** Constant for JSON output */
+ public static final String NATIVE_CRASHES = "NATIVE_CRASHES";
+
+ private static final String HEURISTIC_NAME = "Native crash";
+ private static final String HEURISTIC_TYPE = "NATIVE_CRASH_HEURISTIC";
+
+ private LogcatItem mLogcat = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addLogcat(Date timestamp, LogcatItem logcat) {
+ mLogcat = logcat;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean failed() {
+ if (mLogcat == null) {
+ return false;
+ }
+
+ return (mLogcat.getNativeCrashes().size() > 0);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getType() {
+ return HEURISTIC_TYPE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return HEURISTIC_NAME;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getSummary() {
+ if (!failed()) {
+ return null;
+ }
+
+ return String.format("Found %d native crash%s", mLogcat.getNativeCrashes().size(),
+ mLogcat.getNativeCrashes().size() == 1 ? "" : "es");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDetails() {
+ if (!failed()) {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (NativeCrashItem crash : mLogcat.getNativeCrashes()) {
+ sb.append(crash.getStack());
+ sb.append("\n\n");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject output = super.toJson();
+ try {
+ JSONArray nativeCrashes = new JSONArray();
+ for (NativeCrashItem nc : mLogcat.getNativeCrashes()) {
+ nativeCrashes.put(nc.toJson());
+ }
+ output.put(NATIVE_CRASHES, nativeCrashes);
+ } catch (JSONException e) {
+ // Ignore
+ }
+ return output;
+ }
+}
diff --git a/src/com/android/loganalysis/heuristic/PowerUsageHeuristic.java b/src/com/android/loganalysis/heuristic/PowerUsageHeuristic.java
new file mode 100644
index 0000000..a9b27b3
--- /dev/null
+++ b/src/com/android/loganalysis/heuristic/PowerUsageHeuristic.java
@@ -0,0 +1,233 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.DumpsysBatteryInfoItem;
+import com.android.loganalysis.item.DumpsysBatteryInfoItem.WakeLock;
+import com.android.loganalysis.item.DumpsysItem;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Date;
+
+/**
+ * A {@link IHeuristic} to detect if there are any power issues such as held wake locks.
+ */
+public class PowerUsageHeuristic extends AbstractHeuristic {
+
+ /** Constant for JSON output */
+ public static final String CUTOFF = "CUTOFF";
+ /** Constant for JSON output */
+ public static final String KERNEL_WAKE_LOCKS = "KERNEL_WAKE_LOCKS";
+ /** Constant for JSON output */
+ public static final String WAKE_LOCKS = "WAKE_LOCKS";
+
+ private static final String HEURISTIC_NAME = "Power usage";
+ private static final String HEURISTIC_TYPE = "POWER_USAGE_HEURISTIC";
+
+ // TODO: Make this value configurable.
+ private static final long WAKE_LOCK_TIME_CUTOFF = 30 * 60 * 1000; // 30 minutes
+
+ private DumpsysBatteryInfoItem mBatteryInfo = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ public void addDumpsys(Date timestamp, DumpsysItem dumpsys) {
+ mBatteryInfo = dumpsys.getBatteryInfo();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean failed() {
+ if (mBatteryInfo == null) {
+ return false;
+ }
+
+ for (WakeLock wakeLock : mBatteryInfo.getLastUnpluggedWakeLocks()) {
+ if (wakeLock.getHeldTime() > WAKE_LOCK_TIME_CUTOFF) {
+ return true;
+ }
+ }
+
+ for (WakeLock wakeLock :
+ mBatteryInfo.getLastUnpluggedKernelWakeLocks()) {
+ if (wakeLock.getHeldTime() > WAKE_LOCK_TIME_CUTOFF) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getType() {
+ return HEURISTIC_TYPE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return HEURISTIC_NAME;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getSummary() {
+ if (!failed()) {
+ return null;
+ }
+
+ int wakeLockCounter = 0;
+ for (WakeLock wakeLock : mBatteryInfo.getLastUnpluggedWakeLocks()) {
+ if (wakeLock.getHeldTime() > WAKE_LOCK_TIME_CUTOFF) {
+ wakeLockCounter++;
+ }
+ }
+ int kernelWakeLockCounter = 0;
+ for (WakeLock wakeLock : mBatteryInfo.getLastUnpluggedKernelWakeLocks()) {
+ if (wakeLock.getHeldTime() > WAKE_LOCK_TIME_CUTOFF) {
+ kernelWakeLockCounter++;
+ }
+ }
+
+ StringBuilder sb = new StringBuilder();
+ if (wakeLockCounter > 0) {
+ sb.append(String.format("%d wake lock%s helder longer than %s", wakeLockCounter,
+ wakeLockCounter == 1 ? "" : "s", formatTime(WAKE_LOCK_TIME_CUTOFF)));
+ }
+ if (wakeLockCounter >= 0 && kernelWakeLockCounter >= 0) {
+ sb.append(", ");
+ }
+ if (kernelWakeLockCounter > 0) {
+ sb.append(String.format("%d kernel wake lock%s helder longer than %s",
+ kernelWakeLockCounter, kernelWakeLockCounter == 1 ? "" : "s",
+ formatTime(WAKE_LOCK_TIME_CUTOFF)));
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDetails() {
+ if (!failed()) {
+ return null;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (WakeLock wakeLock : mBatteryInfo.getLastUnpluggedWakeLocks()) {
+ if (wakeLock.getHeldTime() > WAKE_LOCK_TIME_CUTOFF) {
+ sb.append(String.format("Wake lock \"%s\" #%d held for %s (%s times)\n",
+ wakeLock.getName(), wakeLock.getNumber(),
+ formatTime(wakeLock.getHeldTime()), wakeLock.getLockedCount()));
+ }
+ }
+ for (WakeLock wakeLock : mBatteryInfo.getLastUnpluggedKernelWakeLocks()) {
+ if (wakeLock.getHeldTime() > WAKE_LOCK_TIME_CUTOFF) {
+ sb.append(String.format("Kernel wake lock \"%s\" held for %s (%s times)\n",
+ wakeLock.getName(), formatTime(wakeLock.getHeldTime()),
+ wakeLock.getLockedCount()));
+ }
+ }
+ sb.append("\n");
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject output = super.toJson();
+ try {
+ output.put(CUTOFF, WAKE_LOCK_TIME_CUTOFF);
+
+ JSONArray kernelWakeLocks = new JSONArray();
+ for (WakeLock wakeLock : mBatteryInfo.getLastUnpluggedKernelWakeLocks()) {
+ kernelWakeLocks.put(wakeLock.toJson());
+ }
+ output.put(KERNEL_WAKE_LOCKS, kernelWakeLocks);
+
+ JSONArray wakeLocks = new JSONArray();
+ for (WakeLock wakeLock : mBatteryInfo.getLastUnpluggedWakeLocks()) {
+ wakeLocks.put(wakeLock.toJson());
+ }
+ output.put(WAKE_LOCKS, wakeLocks);
+ } catch (JSONException e) {
+ // Ignore
+ }
+ return output;
+ }
+
+ /**
+ * Convert the time in milliseconds into a readable string broken into d/h/m/s/ms.
+ */
+ private static String formatTime(long time) {
+ long msecs = time % 1000;
+ time /= 1000; // Convert to secs
+ long secs = time % 60;
+ time /= 60; // Convert to mins
+ long mins = time % 60;
+ time /= 60; // Convert to hours
+ long hours = time % 24;
+ time /= 24; // Convert to days
+
+ StringBuilder sb = new StringBuilder();
+ if (time != 0) {
+ sb.append(time);
+ sb.append("d ");
+ }
+ if (hours != 0) {
+ sb.append(hours);
+ sb.append("h ");
+ }
+ if (mins != 0) {
+ sb.append(mins);
+ sb.append("m ");
+ }
+ if (secs != 0) {
+ sb.append(secs);
+ sb.append("s ");
+ }
+ if (msecs != 0) {
+ sb.append(msecs);
+ sb.append("ms ");
+ }
+ return sb.toString().trim();
+ }
+
+ /**
+ * Get the wake lock held time threshold.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ long getCutoff() {
+ return WAKE_LOCK_TIME_CUTOFF;
+ }
+}
diff --git a/src/com/android/loganalysis/heuristic/RuntimeRestartHeuristic.java b/src/com/android/loganalysis/heuristic/RuntimeRestartHeuristic.java
new file mode 100644
index 0000000..3d77267
--- /dev/null
+++ b/src/com/android/loganalysis/heuristic/RuntimeRestartHeuristic.java
@@ -0,0 +1,241 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.LogcatItem;
+import com.android.loganalysis.item.MiscLogcatItem;
+import com.android.loganalysis.item.ProcrankItem;
+import com.android.loganalysis.parser.LogcatParser;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Date;
+
+/**
+ * A {@link IHeuristic} to detect if there are any runtime restarts.
+ */
+public class RuntimeRestartHeuristic extends AbstractHeuristic {
+
+ /** Constant for JSON output */
+ public static final String RUNTIME_RESTARTS = "RUNTIME_RESTARTS";
+ /** Constant for JSON output */
+ public static final String PROCRANK = "PROCRANK";
+ /** Constant for JSON output */
+ public static final String PROCRANK_SUMMARY = "PROCRANK_SUMMARY";
+
+ private static final String HEURISTIC_NAME = "Runtime restart";
+ private static final String HEURISTIC_TYPE = "RUNTIME_RESTART_HEURISTIC";
+
+ private static final int MAX_SYSTEM_SERVER_PID = 1000;
+ private static final String SYSTEM_SERVER_PROCESS_NAME = "system_server";
+ private static final String BOOT_ANIMATION_PROCESS_NAME = "/system/bin/bootanimation";
+
+ private LogcatItem mLogcat = null;
+ private ProcrankItem mProcrank = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addLogcat(Date timestamp, LogcatItem logcat) {
+ mLogcat = logcat;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addProcrank(Date timestamp, ProcrankItem procrank) {
+ mProcrank = procrank;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean failed() {
+ if (mLogcat != null && mLogcat.getMiscEvents(LogcatParser.RUNTIME_RESTART).size() > 0) {
+ return true;
+ }
+
+ if (mProcrank != null && mProcrank.getPids().size() > 0) {
+ Integer systemServerPid = getPid(SYSTEM_SERVER_PROCESS_NAME);
+ Integer bootAnimationPid = getPid(BOOT_ANIMATION_PROCESS_NAME);
+
+ if (systemServerPid == null || systemServerPid > MAX_SYSTEM_SERVER_PID) {
+ return true;
+ }
+
+ if (bootAnimationPid != null && bootAnimationPid > MAX_SYSTEM_SERVER_PID) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getType() {
+ return HEURISTIC_TYPE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return HEURISTIC_NAME;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getSummary() {
+ if (!failed()) {
+ return null;
+ }
+
+ return "Found a runtime restart";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDetails() {
+ if (!failed()) {
+ return null;
+ }
+
+ Integer systemServerPid = getPid(SYSTEM_SERVER_PROCESS_NAME);
+ Integer bootAnimtationPid = getPid(BOOT_ANIMATION_PROCESS_NAME);
+
+ StringBuilder sb = new StringBuilder();
+ if (mLogcat != null) {
+ for (MiscLogcatItem item : mLogcat.getMiscEvents(LogcatParser.RUNTIME_RESTART)) {
+ sb.append(String.format("Message: %s, Time: %s\n\nLast lines of logcat:\n%s\n\n" +
+ "Process lines for pid %d:\n%s\n\n", item.getMessage(), item.getEventTime(),
+ item.getLastPreamble(), item.getPid(), item.getProcessPreamble()));
+ }
+ }
+
+ if (systemServerPid == null) {
+ sb.append("Suspected runtime restart detected because system_server is missing from " +
+ "procrank");
+ }
+ if (systemServerPid != null && systemServerPid > MAX_SYSTEM_SERVER_PID) {
+ sb.append(String.format("Suspected runtime restart detected because system_server " +
+ "has a PID of %d (greater than %d)\n", systemServerPid, MAX_SYSTEM_SERVER_PID));
+ }
+ if (bootAnimtationPid != null && bootAnimtationPid > MAX_SYSTEM_SERVER_PID) {
+ sb.append(String.format("Suspected runtime restart detected because " +
+ "/system/bin/bootanimation is present in procrank with a PID of %d (greater " +
+ "than %d)\n", bootAnimtationPid, MAX_SYSTEM_SERVER_PID));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public JSONObject toJson() {
+ JSONObject output = super.toJson();
+ try {
+ Integer systemServerPid = getPid(SYSTEM_SERVER_PROCESS_NAME);
+ Integer bootAnimtationPid = getPid(BOOT_ANIMATION_PROCESS_NAME);
+
+ if (mLogcat != null) {
+ JSONArray runtimeRestarts = new JSONArray();
+ for (MiscLogcatItem item : mLogcat.getMiscEvents(LogcatParser.RUNTIME_RESTART)) {
+ runtimeRestarts.put(item.toJson());
+ }
+ output.put(RUNTIME_RESTARTS, runtimeRestarts);
+ }
+
+ if (mProcrank != null) {
+ output.put(PROCRANK, mProcrank.toJson());
+ JSONArray summary = new JSONArray();
+ if (systemServerPid == null) {
+ summary.put(String.format("%s is absent from the procrank",
+ SYSTEM_SERVER_PROCESS_NAME));
+ }
+ if (systemServerPid != null && systemServerPid > MAX_SYSTEM_SERVER_PID) {
+ summary.put(String.format("%s is present in the procrank with a PID greater " +
+ "than %d", SYSTEM_SERVER_PROCESS_NAME, MAX_SYSTEM_SERVER_PID));
+ }
+ if (bootAnimtationPid != null && bootAnimtationPid > MAX_SYSTEM_SERVER_PID) {
+ summary.put(String.format("%s is present in the procrank with a PID greater " +
+ "than %d", BOOT_ANIMATION_PROCESS_NAME, MAX_SYSTEM_SERVER_PID));
+ }
+ output.put(PROCRANK, mProcrank.toJson());
+ output.put(PROCRANK_SUMMARY, summary);
+ }
+ } catch (JSONException e) {
+ // Ignore
+ }
+ return output;
+ }
+
+ /**
+ * Get the PID from a process name
+ */
+ private Integer getPid(String processName) {
+ if (mProcrank == null || processName == null) {
+ return null;
+ }
+ for (Integer pid : mProcrank.getPids()) {
+ if (processName.equals(mProcrank.getProcessName(pid))) {
+ return pid;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the system server PID threshold.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ int getCutoff() {
+ return MAX_SYSTEM_SERVER_PID;
+ }
+
+ /**
+ * Get the system server name.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ String getSystemServerName() {
+ return SYSTEM_SERVER_PROCESS_NAME;
+ }
+
+ /**
+ * Get the bootanimation name.
+ * <p>
+ * Exposed for unit testing.
+ * </p>
+ */
+ String getBootAnimationName() {
+ return BOOT_ANIMATION_PROCESS_NAME;
+ }
+}
diff --git a/src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java b/src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java
index 325347e..0b0c2d3 100644
--- a/src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java
+++ b/src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java
@@ -129,14 +129,14 @@ public class DumpsysBatteryInfoItem implements IItem {
/**
* Get the list of kernel wake locks from the last unplugged section of the battery info.
*/
- public List<WakeLock> getLastUnpluggedKernelWakeLock() {
+ public List<WakeLock> getLastUnpluggedKernelWakeLocks() {
return mLastUnpluggedKernelWakeLocks;
}
/**
* Get the list of wake locks from the last unplugged section of the battery info.
*/
- public List<WakeLock> getLastUnpluggedWakeLock() {
+ public List<WakeLock> getLastUnpluggedWakeLocks() {
return mLastUnpluggedWakeLocks;
}
diff --git a/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java b/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java
index 5109966..fce4009 100644
--- a/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java
+++ b/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java
@@ -165,8 +165,8 @@ public class DumpsysBatteryInfoParser implements IParser {
* 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
+ * @return the long represented by the argument in decimal, or 0 if the string is {@code null}.
+ * @throws NumberFormatException if the string is not {@code null} or does not contain a
* parsable long.
*/
private long parseLongOrZero(String s) {
diff --git a/tests/src/com/android/loganalysis/UnitTests.java b/tests/src/com/android/loganalysis/UnitTests.java
index 83feb64..caac918 100644
--- a/tests/src/com/android/loganalysis/UnitTests.java
+++ b/tests/src/com/android/loganalysis/UnitTests.java
@@ -16,6 +16,14 @@
package com.android.loganalysis;
+import com.android.loganalysis.heuristic.AnrHeuristicTest;
+import com.android.loganalysis.heuristic.CpuUsageHeuristicTest;
+import com.android.loganalysis.heuristic.JavaCrashHeuristicTest;
+import com.android.loganalysis.heuristic.KernelResetHeuristicTest;
+import com.android.loganalysis.heuristic.MemoryUsageHeuristicTest;
+import com.android.loganalysis.heuristic.NativeCrashHeuristicTest;
+import com.android.loganalysis.heuristic.PowerUsageHeuristicTest;
+import com.android.loganalysis.heuristic.RuntimeRestartHeuristicTest;
import com.android.loganalysis.item.DumpsysBatteryInfoItemTest;
import com.android.loganalysis.item.GenericItemTest;
import com.android.loganalysis.item.MonkeyLogItemTest;
@@ -54,6 +62,16 @@ public class UnitTests extends TestSuite {
public UnitTests() {
super();
+ // heuristic
+ addTestSuite(AnrHeuristicTest.class);
+ addTestSuite(CpuUsageHeuristicTest.class);
+ addTestSuite(JavaCrashHeuristicTest.class);
+ addTestSuite(KernelResetHeuristicTest.class);
+ addTestSuite(MemoryUsageHeuristicTest.class);
+ addTestSuite(NativeCrashHeuristicTest.class);
+ addTestSuite(PowerUsageHeuristicTest.class);
+ addTestSuite(RuntimeRestartHeuristicTest.class);
+
// item
addTestSuite(DumpsysBatteryInfoItemTest.class);
addTestSuite(GenericItemTest.class);
diff --git a/tests/src/com/android/loganalysis/heuristic/AnrHeuristicTest.java b/tests/src/com/android/loganalysis/heuristic/AnrHeuristicTest.java
new file mode 100644
index 0000000..a76f6fd
--- /dev/null
+++ b/tests/src/com/android/loganalysis/heuristic/AnrHeuristicTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.AnrItem;
+import com.android.loganalysis.item.LogcatItem;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test for {@link AnrHeuristic}.
+ */
+public class AnrHeuristicTest extends TestCase {
+
+ /**
+ * Test that {@link AnrHeuristic#failed()} returns true in the presence of ANRs.
+ */
+ public void testCheckHeuristic_anr() {
+ AnrHeuristic heuristic = new AnrHeuristic();
+ LogcatItem logcat = new LogcatItem();
+ logcat.addEvent(new AnrItem());
+ heuristic.addLogcat(null, logcat);
+
+ assertTrue(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link AnrHeuristic#failed()} returns false in the absence of ANRs.
+ */
+ public void testCheckHeuristic_no_anr() {
+ AnrHeuristic heuristic = new AnrHeuristic();
+ LogcatItem logcat = new LogcatItem();
+ heuristic.addLogcat(null, logcat);
+
+ assertFalse(heuristic.failed());
+ }
+}
diff --git a/tests/src/com/android/loganalysis/heuristic/CpuUsageHeuristicTest.java b/tests/src/com/android/loganalysis/heuristic/CpuUsageHeuristicTest.java
new file mode 100644
index 0000000..6010e8e
--- /dev/null
+++ b/tests/src/com/android/loganalysis/heuristic/CpuUsageHeuristicTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.LogcatItem;
+import com.android.loganalysis.item.MiscLogcatItem;
+import com.android.loganalysis.item.TopItem;
+import com.android.loganalysis.parser.LogcatParser;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test for {@link CpuUsageHeuristic}.
+ */
+public class CpuUsageHeuristicTest extends TestCase {
+
+ /**
+ * Test that {@link CpuUsageHeuristic#failed()} returns true if CPU usage is high.
+ */
+ public void testCheckHeuristic_high_cpu() {
+ CpuUsageHeuristic heuristic = new CpuUsageHeuristic();
+ TopItem top = new TopItem();
+ top.setTotal(1000);
+ top.setUser((int) (heuristic.getCutoff() * top.getTotal()) + 1);
+ top.setIdle(top.getTotal() - top.getUser());
+ heuristic.addTop(null, top);
+
+ assertTrue(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link CpuUsageHeuristic#failed()} returns false if CPU usage is low.
+ */
+ public void testCheckHeuristic_low_cpu() {
+ CpuUsageHeuristic heuristic = new CpuUsageHeuristic();
+ TopItem top = new TopItem();
+ top.setTotal(1000);
+ top.setUser((int) (heuristic.getCutoff() * top.getTotal()) - 1);
+ top.setIdle(top.getTotal() - top.getUser());
+ heuristic.addTop(null, top);
+
+ assertFalse(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link CpuUsageHeuristic#failed()} returns true if a high cpu usage
+ * indicator is found in the logcat.
+ */
+ public void testCheckHeuristic_logcat() {
+ CpuUsageHeuristic heuristic = new CpuUsageHeuristic();
+ LogcatItem logcat = new LogcatItem();
+ MiscLogcatItem item = new MiscLogcatItem();
+ item.setCategory(LogcatParser.HIGH_CPU_USAGE);
+ logcat.addEvent(item);
+ heuristic.addLogcat(null, logcat);
+
+ assertTrue(heuristic.failed());
+ }
+}
diff --git a/tests/src/com/android/loganalysis/heuristic/JavaCrashHeuristicTest.java b/tests/src/com/android/loganalysis/heuristic/JavaCrashHeuristicTest.java
new file mode 100644
index 0000000..a3aa707
--- /dev/null
+++ b/tests/src/com/android/loganalysis/heuristic/JavaCrashHeuristicTest.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.heuristic;
+
+import com.android.loganalysis.item.JavaCrashItem;
+import com.android.loganalysis.item.LogcatItem;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test for {@link JavaCrashHeuristic}.
+ */
+public class JavaCrashHeuristicTest extends TestCase {
+
+ /**
+ * Test that {@link JavaCrashHeuristic#failed()} returns true in the presence of
+ * Java crashes.
+ */
+ public void testCheckHeuristic_crash() {
+ JavaCrashHeuristic heuristic = new JavaCrashHeuristic();
+ LogcatItem logcat = new LogcatItem();
+ logcat.addEvent(new JavaCrashItem());
+ heuristic.addLogcat(null, logcat);
+
+ assertTrue(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link JavaCrashHeuristic#failed()} returns false in the absence of
+ * Java crashes.
+ */
+ public void testCheckHeuristic_no_java_crash() {
+ JavaCrashHeuristic heuristic = new JavaCrashHeuristic();
+ LogcatItem logcat = new LogcatItem();
+ heuristic.addLogcat(null, logcat);
+
+ assertFalse(heuristic.failed());
+ }
+}
diff --git a/tests/src/com/android/loganalysis/heuristic/KernelResetHeuristicTest.java b/tests/src/com/android/loganalysis/heuristic/KernelResetHeuristicTest.java
new file mode 100644
index 0000000..53d3aff
--- /dev/null
+++ b/tests/src/com/android/loganalysis/heuristic/KernelResetHeuristicTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.KernelLogItem;
+import com.android.loganalysis.item.MiscKernelLogItem;
+import com.android.loganalysis.parser.KernelLogParser;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test for {@link KernelResetHeuristic}.
+ */
+public class KernelResetHeuristicTest extends TestCase {
+
+ /**
+ * Test that {@link RuntimeRestartHeuristic#failed()} returns true if a reset is found
+ * in the kernel log.
+ */
+ public void testCheckHeuristic_reset() {
+ KernelResetHeuristic heuristic = new KernelResetHeuristic();
+ KernelLogItem kernelLog = new KernelLogItem();
+ MiscKernelLogItem item = new MiscKernelLogItem();
+ item.setCategory(KernelLogParser.KERNEL_RESET);
+ kernelLog.addEvent(item);
+ heuristic.addKernelLog(null, kernelLog);
+
+ assertTrue(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link RuntimeRestartHeuristic#failed()} returns false if no resets are
+ * found in the kernel log.
+ */
+ public void testCheckHeuristic_no_reset() {
+ KernelResetHeuristic heuristic = new KernelResetHeuristic();
+ KernelLogItem kernelLog = new KernelLogItem();
+ heuristic.addKernelLog(null, kernelLog);
+
+ assertFalse(heuristic.failed());
+ }
+}
diff --git a/tests/src/com/android/loganalysis/heuristic/MemoryUsageHeuristicTest.java b/tests/src/com/android/loganalysis/heuristic/MemoryUsageHeuristicTest.java
new file mode 100644
index 0000000..c185bad
--- /dev/null
+++ b/tests/src/com/android/loganalysis/heuristic/MemoryUsageHeuristicTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.LogcatItem;
+import com.android.loganalysis.item.MemInfoItem;
+import com.android.loganalysis.item.MiscLogcatItem;
+import com.android.loganalysis.parser.LogcatParser;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test for {@link MemoryUsageHeuristic}.
+ */
+public class MemoryUsageHeuristicTest extends TestCase {
+
+ /**
+ * Test that {@link MemoryUsageHeuristic#failed()} returns true if memory usage is high.
+ */
+ public void testCheckHeuristic_high_memory() {
+ MemoryUsageHeuristic heuristic = new MemoryUsageHeuristic();
+ MemInfoItem memInfo = new MemInfoItem();
+ memInfo.put("MemTotal", 1000000);
+ memInfo.put("MemFree", (int) ((1.0 - heuristic.getCutoff()) * memInfo.get("MemTotal")) - 1);
+ heuristic.addMemInfo(null, memInfo);
+
+ assertTrue(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link MemoryUsageHeuristic#failed()} returns false if memory usage is low.
+ */
+ public void testCheckHeuristic_low_memory() {
+ MemoryUsageHeuristic heuristic = new MemoryUsageHeuristic();
+ MemInfoItem memInfo = new MemInfoItem();
+ memInfo.put("MemTotal", 1000000);
+ memInfo.put("MemFree", (int) ((1.0 - heuristic.getCutoff()) * memInfo.get("MemTotal")) + 1);
+ heuristic.addMemInfo(null, memInfo);
+
+ assertFalse(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link CpuUsageHeuristic#failed()} returns true if a high memory usage
+ * indicator is found in the logcat.
+ */
+ public void testCheckHeuristic_logcat() {
+ MemoryUsageHeuristic heuristic = new MemoryUsageHeuristic();
+ LogcatItem logcat = new LogcatItem();
+ MiscLogcatItem item = new MiscLogcatItem();
+ item.setCategory(LogcatParser.HIGH_MEMORY_USAGE);
+ logcat.addEvent(item);
+ heuristic.addLogcat(null, logcat);
+
+ assertTrue(heuristic.failed());
+ }
+}
diff --git a/tests/src/com/android/loganalysis/heuristic/NativeCrashHeuristicTest.java b/tests/src/com/android/loganalysis/heuristic/NativeCrashHeuristicTest.java
new file mode 100644
index 0000000..7f1c1af
--- /dev/null
+++ b/tests/src/com/android/loganalysis/heuristic/NativeCrashHeuristicTest.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.heuristic;
+
+import com.android.loganalysis.item.LogcatItem;
+import com.android.loganalysis.item.NativeCrashItem;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test for {@link NativeCrashHeuristic}.
+ */
+public class NativeCrashHeuristicTest extends TestCase {
+
+ /**
+ * Test that {@link NativeCrashHeuristic#failed()} returns true in the presence of
+ * native crashes.
+ */
+ public void testCheckHeuristic_crash() {
+ NativeCrashHeuristic heuristic = new NativeCrashHeuristic();
+ LogcatItem logcat = new LogcatItem();
+ logcat.addEvent(new NativeCrashItem());
+ heuristic.addLogcat(null, logcat);
+
+ assertTrue(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link NativeCrashHeuristic#failed()} returns false in the absence of
+ * native crashes.
+ */
+ public void testCheckHeuristic_no_java_crash() {
+ NativeCrashHeuristic heuristic = new NativeCrashHeuristic();
+ LogcatItem logcat = new LogcatItem();
+ heuristic.addLogcat(null, logcat);
+
+ assertFalse(heuristic.failed());
+ }
+}
diff --git a/tests/src/com/android/loganalysis/heuristic/PowerUsageHeuristicTest.java b/tests/src/com/android/loganalysis/heuristic/PowerUsageHeuristicTest.java
new file mode 100644
index 0000000..7db3d25
--- /dev/null
+++ b/tests/src/com/android/loganalysis/heuristic/PowerUsageHeuristicTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.DumpsysBatteryInfoItem;
+import com.android.loganalysis.item.DumpsysItem;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test for {@link PowerUsageHeuristic}.
+ */
+public class PowerUsageHeuristicTest extends TestCase {
+
+ /**
+ * Test that {@link PowerUsageHeuristic#failed()} returns true if a wake lock is held
+ * longer than the threshold.
+ */
+ public void testCheckHeuristic_wake_lock() {
+ PowerUsageHeuristic heuristic = new PowerUsageHeuristic();
+ DumpsysItem dumpsys = new DumpsysItem();
+ DumpsysBatteryInfoItem batteryInfo = new DumpsysBatteryInfoItem();
+ batteryInfo.addLastUnpluggedWakeLock("wakelock", 0, heuristic.getCutoff() + 1, 0);
+ batteryInfo.addLastUnpluggedKernelWakeLock("kernelwakelock", heuristic.getCutoff() - 1, 0);
+ dumpsys.setBatteryInfo(batteryInfo);
+ heuristic.addDumpsys(null, dumpsys);
+
+ assertTrue(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link PowerUsageHeuristic#failed()} returns true if a kernel wake lock is
+ * held longer than the threshold.
+ */
+ public void testCheckHeuristic_kernel_wake_lock() {
+ PowerUsageHeuristic heuristic = new PowerUsageHeuristic();
+ DumpsysItem dumpsys = new DumpsysItem();
+ DumpsysBatteryInfoItem batteryInfo = new DumpsysBatteryInfoItem();
+ batteryInfo.addLastUnpluggedWakeLock("wakelock", 0, heuristic.getCutoff() - 1, 0);
+ batteryInfo.addLastUnpluggedKernelWakeLock("kernelwakelock", heuristic.getCutoff() + 1, 0);
+ dumpsys.setBatteryInfo(batteryInfo);
+ heuristic.addDumpsys(null, dumpsys);
+
+ assertTrue(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link PowerUsageHeuristic#failed()} returns false if no wake locks are
+ * held longer than the threshold.
+ */
+ public void testCheckHeuristic_no_wake_lock() {
+ PowerUsageHeuristic heuristic = new PowerUsageHeuristic();
+ DumpsysItem dumpsys = new DumpsysItem();
+ DumpsysBatteryInfoItem batteryInfo = new DumpsysBatteryInfoItem();
+ batteryInfo.addLastUnpluggedWakeLock("wakelock", 0, heuristic.getCutoff() - 1, 0);
+ batteryInfo.addLastUnpluggedKernelWakeLock("kernelwakelock", heuristic.getCutoff() - 1, 0);
+ dumpsys.setBatteryInfo(batteryInfo);
+ heuristic.addDumpsys(null, dumpsys);
+
+ assertFalse(heuristic.failed());
+ }
+}
diff --git a/tests/src/com/android/loganalysis/heuristic/RuntimeRestartHeuristicTest.java b/tests/src/com/android/loganalysis/heuristic/RuntimeRestartHeuristicTest.java
new file mode 100644
index 0000000..8c5d920
--- /dev/null
+++ b/tests/src/com/android/loganalysis/heuristic/RuntimeRestartHeuristicTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.heuristic;
+
+import com.android.loganalysis.item.LogcatItem;
+import com.android.loganalysis.item.MiscLogcatItem;
+import com.android.loganalysis.item.ProcrankItem;
+import com.android.loganalysis.parser.LogcatParser;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test for {@link RuntimeRestartHeuristic}.
+ */
+public class RuntimeRestartHeuristicTest extends TestCase {
+
+ /**
+ * Test that {@link RuntimeRestartHeuristic#failed()} returns false if the procrank is empty.
+ */
+ public void testCheckHeuristic_empty_procrank() {
+ RuntimeRestartHeuristic heuristic = new RuntimeRestartHeuristic();
+ ProcrankItem procrank = new ProcrankItem();
+ heuristic.addProcrank(null, procrank);
+
+ assertFalse(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link RuntimeRestartHeuristic#failed()} returns true if the system server
+ * is process is absent.
+ */
+ public void testCheckHeuristic_no_system_server() {
+ RuntimeRestartHeuristic heuristic = new RuntimeRestartHeuristic();
+ ProcrankItem procrank = new ProcrankItem();
+ procrank.addProcrankLine(0, "foo", 0, 0, 0, 0);
+ heuristic.addProcrank(null, procrank);
+
+ assertTrue(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link RuntimeRestartHeuristic#failed()} returns true if the system server
+ * is process is present with a high PID.
+ */
+ public void testCheckHeuristic_high_system_server() {
+ RuntimeRestartHeuristic heuristic = new RuntimeRestartHeuristic();
+ ProcrankItem procrank = new ProcrankItem();
+ procrank.addProcrankLine(heuristic.getCutoff() + 1, heuristic.getSystemServerName(), 0, 0,
+ 0, 0);
+ heuristic.addProcrank(null, procrank);
+
+ assertTrue(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link RuntimeRestartHeuristic#failed()} returns true if the bootanimation
+ * process is present with a high PID.
+ */
+ public void testCheckHeuristic_high_bootanimation() {
+ RuntimeRestartHeuristic heuristic = new RuntimeRestartHeuristic();
+ ProcrankItem procrank = new ProcrankItem();
+ procrank.addProcrankLine(heuristic.getCutoff() - 1, heuristic.getSystemServerName(), 0, 0,
+ 0, 0);
+ procrank.addProcrankLine(heuristic.getCutoff() + 1, heuristic.getBootAnimationName(), 0, 0,
+ 0, 0);
+ heuristic.addProcrank(null, procrank);
+
+ assertTrue(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link RuntimeRestartHeuristic#failed()} returns false if the system server
+ * process is present with a low PID.
+ */
+ public void testCheckHeuristic_low_system_server() {
+ RuntimeRestartHeuristic heuristic = new RuntimeRestartHeuristic();
+ ProcrankItem procrank = new ProcrankItem();
+ procrank.addProcrankLine(heuristic.getCutoff() - 1, heuristic.getSystemServerName(), 0, 0,
+ 0, 0);
+ heuristic.addProcrank(null, procrank);
+
+ assertFalse(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link RuntimeRestartHeuristic#failed()} returns false if the bootanimation
+ * and system server processes are present with low PIDs.
+ */
+ public void testCheckHeuristic_low_bootanimation() {
+ RuntimeRestartHeuristic heuristic = new RuntimeRestartHeuristic();
+ ProcrankItem procrank = new ProcrankItem();
+ procrank.addProcrankLine(heuristic.getCutoff() - 1, heuristic.getSystemServerName(), 0, 0,
+ 0, 0);
+ procrank.addProcrankLine(heuristic.getCutoff() - 2, heuristic.getBootAnimationName(), 0, 0,
+ 0, 0);
+ heuristic.addProcrank(null, procrank);
+
+ assertFalse(heuristic.failed());
+ }
+
+ /**
+ * Test that {@link RuntimeRestartHeuristic#failed()} returns true if a runtime restart
+ * is found in the logcat.
+ */
+ public void testCheckHeuristic_logcat() {
+ RuntimeRestartHeuristic heuristic = new RuntimeRestartHeuristic();
+ LogcatItem logcat = new LogcatItem();
+ MiscLogcatItem item = new MiscLogcatItem();
+ item.setCategory(LogcatParser.RUNTIME_RESTART);
+ logcat.addEvent(item);
+ heuristic.addLogcat(null, logcat);
+
+ assertTrue(heuristic.failed());
+ }
+}
diff --git a/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java b/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java
index dec82fa..55af599 100644
--- a/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java
+++ b/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java
@@ -96,13 +96,13 @@ public class DumpsysBatteryInfoParserTest extends TestCase {
DumpsysBatteryInfoParser parser = new DumpsysBatteryInfoParser();
DumpsysBatteryInfoItem item = parser.parse(inputBlock);
- assertEquals(5, item.getLastUnpluggedWakeLock().size());
- assertEquals(3, item.getLastUnpluggedKernelWakeLock().size());
+ assertEquals(5, item.getLastUnpluggedWakeLocks().size());
+ assertEquals(3, item.getLastUnpluggedKernelWakeLocks().size());
assertEquals("partialWakelock",
- item.getLastUnpluggedWakeLock().get(0).getName());
+ item.getLastUnpluggedWakeLocks().get(0).getName());
assertEquals("PowerManagerService.WakeLocks",
- item.getLastUnpluggedKernelWakeLock().get(0).getName());
+ item.getLastUnpluggedKernelWakeLocks().get(0).getName());
}
/**
@@ -115,8 +115,8 @@ public class DumpsysBatteryInfoParserTest extends TestCase {
parser.parseLastUnpluggedKernelWakeLock(inputLine);
DumpsysBatteryInfoItem item = parser.getItem();
- assertEquals(1, item.getLastUnpluggedKernelWakeLock().size());
- WakeLock wakeLock = item.getLastUnpluggedKernelWakeLock().get(0);
+ assertEquals(1, item.getLastUnpluggedKernelWakeLocks().size());
+ WakeLock wakeLock = item.getLastUnpluggedKernelWakeLocks().get(0);
assertEquals("Process", wakeLock.getName());
assertNull(wakeLock.getNumber());
assertEquals(DumpsysBatteryInfoParser.getMs(1, 2, 3, 4, 5), wakeLock.getHeldTime());
@@ -128,8 +128,8 @@ public class DumpsysBatteryInfoParserTest extends TestCase {
parser.parseLastUnpluggedKernelWakeLock(inputLine);
item = parser.getItem();
- assertEquals(1, item.getLastUnpluggedKernelWakeLock().size());
- wakeLock = item.getLastUnpluggedKernelWakeLock().get(0);
+ assertEquals(1, item.getLastUnpluggedKernelWakeLocks().size());
+ wakeLock = item.getLastUnpluggedKernelWakeLocks().get(0);
assertEquals("Process", wakeLock.getName());
assertNull(wakeLock.getNumber());
assertEquals(5 * 60 * 1000 + 7, wakeLock.getHeldTime());
@@ -146,8 +146,8 @@ public class DumpsysBatteryInfoParserTest extends TestCase {
parser.parseLastUnpluggedWakeLock(inputLine);
DumpsysBatteryInfoItem item = parser.getItem();
- assertEquals(1, item.getLastUnpluggedWakeLock().size());
- WakeLock wakeLock = item.getLastUnpluggedWakeLock().get(0);
+ assertEquals(1, item.getLastUnpluggedWakeLocks().size());
+ WakeLock wakeLock = item.getLastUnpluggedWakeLocks().get(0);
assertEquals("Process", wakeLock.getName());
assertEquals((Integer) 1234, wakeLock.getNumber());
assertEquals(DumpsysBatteryInfoParser.getMs(1, 2, 3, 4, 5), wakeLock.getHeldTime());
@@ -159,8 +159,8 @@ public class DumpsysBatteryInfoParserTest extends TestCase {
parser.parseLastUnpluggedWakeLock(inputLine);
item = parser.getItem();
- assertEquals(1, item.getLastUnpluggedWakeLock().size());
- wakeLock = item.getLastUnpluggedWakeLock().get(0);
+ assertEquals(1, item.getLastUnpluggedWakeLocks().size());
+ wakeLock = item.getLastUnpluggedWakeLocks().get(0);
assertEquals("Process:with:colons", wakeLock.getName());
assertEquals((Integer) 1234, wakeLock.getNumber());
assertEquals(5 * 60 * 1000 + 7, wakeLock.getHeldTime());
diff --git a/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java b/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java
index 9561b7b..60b9fd7 100644
--- a/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java
+++ b/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java
@@ -52,7 +52,7 @@ public class DumpsysParserTest extends TestCase {
DumpsysItem item = parser.parse(inputBlock);
assertNotNull(item.getBatteryInfo());
- assertEquals(2, item.getBatteryInfo().getLastUnpluggedWakeLock().size());
- assertEquals(2, item.getBatteryInfo().getLastUnpluggedKernelWakeLock().size());
+ assertEquals(2, item.getBatteryInfo().getLastUnpluggedWakeLocks().size());
+ assertEquals(2, item.getBatteryInfo().getLastUnpluggedKernelWakeLocks().size());
}
}