diff options
author | Eric Rowe <erowe@google.com> | 2013-04-18 17:24:37 -0700 |
---|---|---|
committer | Eric Rowe <erowe@google.com> | 2013-05-02 10:52:55 -0700 |
commit | 8bed524a70e5466c6cbdc2726c40324d279c6eaa (patch) | |
tree | facae4364bacd2c875b59e40f6489a1a62cee834 | |
parent | 857055399e6bc90e0afd5565d7d82dc751e01978 (diff) | |
download | loganalysis-8bed524a70e5466c6cbdc2726c40324d279c6eaa.tar.gz |
Add heuristic interface and basic heuristics.
Change-Id: Ie355c45e28d1315698e1971235b4a4804e9089cc
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()); } } |