diff options
40 files changed, 3123 insertions, 554 deletions
diff --git a/src/com/android/loganalysis/LogAnalyzer.java b/src/com/android/loganalysis/LogAnalyzer.java index 787e58a..a0a49a1 100644 --- a/src/com/android/loganalysis/LogAnalyzer.java +++ b/src/com/android/loganalysis/LogAnalyzer.java @@ -26,10 +26,14 @@ import com.android.loganalysis.parser.KernelLogParser; import com.android.loganalysis.parser.LogcatParser; import com.android.loganalysis.parser.MemoryHealthParser; import com.android.loganalysis.parser.MonkeyLogParser; +import com.android.loganalysis.rule.RuleEngine; +import com.android.loganalysis.rule.RuleEngine.RuleType; import com.android.loganalysis.util.config.ArgsOptionParser; import com.android.loganalysis.util.config.ConfigurationException; import com.android.loganalysis.util.config.Option; +import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; @@ -39,6 +43,9 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + /** * A command line tool to parse a bugreport, logcat, or kernel log file and return the output. */ @@ -49,6 +56,10 @@ public class LogAnalyzer { JSON; } + private enum ResultType { + RAW, ANALYSIS; + } + @Option(name="bugreport", description="The path to the bugreport") private String mBugreportPath = null; @@ -67,6 +78,17 @@ public class LogAnalyzer { @Option(name="output", description="The output format, currently only JSON") private OutputFormat mOutputFormat = OutputFormat.JSON; + @Option(name="rule-type", description="The type of rules to be applied") + private RuleType mRuleType = RuleType.ALL; + + @Option(name="print", description="Print the result type") + private List<ResultType> mResultType = new ArrayList<ResultType>(); + + /** Constant for JSON output */ + private static final String RAW_DATA = "RAW"; + /** Constant for JSON output */ + private static final String ANALYSIS_DATA = "ANALYSIS"; + /** * Run the command line tool */ @@ -140,9 +162,61 @@ public class LogAnalyzer { */ private void printBugreport(BugreportItem bugreport) { if (OutputFormat.JSON.equals(mOutputFormat)) { - printJson(bugreport); + if (mResultType.size() == 0) { + printJson(bugreport); + } else if (mResultType.size() == 1) { + switch (mResultType.get(0)) { + case RAW: + printJson(bugreport); + break; + case ANALYSIS: + printBugreportAnalysis(getBugreportAnalysis(bugreport)); + break; + default: + // should not get here + return; + } + } else { + JSONObject result = new JSONObject(); + try { + for (ResultType resultType : mResultType) { + switch (resultType) { + case RAW: + result.put(RAW_DATA, bugreport.toJson()); + break; + case ANALYSIS: + result.put(ANALYSIS_DATA, getBugreportAnalysis(bugreport)); + break; + default: + // should not get here + break; + } + } + } catch (JSONException e) { + // Ignore + } + printJson(result); + } + } + } + + private JSONArray getBugreportAnalysis(BugreportItem bugreport) { + RuleEngine ruleEngine = new RuleEngine(bugreport); + ruleEngine.registerRules(mRuleType); + ruleEngine.executeRules(); + if (ruleEngine.getAnalysis() != null) { + return ruleEngine.getAnalysis(); + } else { + return new JSONArray(); + } + } + + private void printBugreportAnalysis(JSONArray analysis) { + if (analysis != null && analysis.length() > 0) { + System.out.println(analysis.toString()); + } else { + System.out.println(new JSONObject().toString()); } - // TODO: Print bugreport in human readable form. } /** @@ -180,7 +254,18 @@ public class LogAnalyzer { */ private void printJson(IItem item) { if (item != null && item.toJson() != null) { - System.out.println(item.toJson().toString()); + printJson(item.toJson()); + } else { + printJson(new JSONObject()); + } + } + + /** + * Print an {@link JSONObject} to stdout + */ + private void printJson(JSONObject json) { + if (json != null) { + System.out.println(json.toString()); } else { System.out.println(new JSONObject().toString()); } diff --git a/src/com/android/loganalysis/item/BatteryDischargeItem.java b/src/com/android/loganalysis/item/BatteryDischargeItem.java new file mode 100644 index 0000000..cdfcbcc --- /dev/null +++ b/src/com/android/loganalysis/item/BatteryDischargeItem.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.item; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; + +/** + * An {@link IItem} used to store information related to Battery discharge + */ +public class BatteryDischargeItem implements IItem { + + /** Constant for JSON output */ + public static final String BATTERY_DISCHARGE = "BATTERY_DISCHARGE"; + + private Collection<BatteryDischargeInfoItem> mBatteryDischargeInfo = + new LinkedList<BatteryDischargeInfoItem>(); + + public static class BatteryDischargeInfoItem extends GenericItem { + /** Constant for JSON output */ + public static final String CLOCK_TIME_OF_DISCHARGE = "CLOCK_TIME_OF_DISCHARGE"; + /** Constant for JSON output */ + public static final String DISCHARGE_ELAPSED_TIME = "DISCHARGE_ELAPSED_TIME"; + /** Constant for JSON output */ + public static final String BATTERY_LEVEL = "BATTERY_LEVEL"; + + private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList( + CLOCK_TIME_OF_DISCHARGE, DISCHARGE_ELAPSED_TIME, BATTERY_LEVEL)); + + /** + * The constructor for {@link BatteryDischargeInfoItem} + * + * @param clockTime Clock time when the battery discharge happened + * @param elapsedTime Time it took to discharge to the current battery level + * @param batteryLevel Current battery level + */ + public BatteryDischargeInfoItem(Calendar clockTime, long elapsedTime, int batteryLevel) { + super(ATTRIBUTES); + + setAttribute(CLOCK_TIME_OF_DISCHARGE, clockTime); + setAttribute(DISCHARGE_ELAPSED_TIME, elapsedTime); + setAttribute(BATTERY_LEVEL, batteryLevel); + } + + /** + * Get the clock time when the battery level dropped + */ + public Calendar getClockTime() { + return (Calendar) getAttribute(CLOCK_TIME_OF_DISCHARGE); + } + + /** + * Get the time elapsed to discharge to the current battery level + */ + public long getElapsedTime() { + return (long) getAttribute(DISCHARGE_ELAPSED_TIME); + } + + /** + * Get the current battery level + */ + public int getBatteryLevel() { + return (int) getAttribute(BATTERY_LEVEL); + } + } + + /** + * Add a battery discharge step from battery stats + * + * @param clockTime Clock time when the battery discharge happened + * @param elapsedTime Time it took to discharge to the current battery level + * @param batteryLevel Current battery level + */ + public void addBatteryDischargeInfo(Calendar clockTime, long elapsedTime, int batteryLevel) { + mBatteryDischargeInfo.add(new BatteryDischargeInfoItem(clockTime, + elapsedTime, batteryLevel)); + } + + public Collection<BatteryDischargeInfoItem> getDischargeStepsInfo() { + return mBatteryDischargeInfo; + } + + /** + * {@inheritDoc} + */ + @Override + public IItem merge(IItem other) throws ConflictingItemException { + throw new ConflictingItemException("Wakelock items cannot be merged"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isConsistent(IItem other) { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public JSONObject toJson() { + JSONObject object = new JSONObject(); + try { + JSONArray batteryDischargeSteps = new JSONArray(); + for (BatteryDischargeInfoItem batteryDischargeStep : mBatteryDischargeInfo) { + batteryDischargeSteps.put(batteryDischargeStep.toJson()); + } + object.put(BATTERY_DISCHARGE, batteryDischargeSteps); + } catch (JSONException e) { + // Ignore + } + return object; + } +} diff --git a/src/com/android/loganalysis/item/BatteryStatsDetailedInfoItem.java b/src/com/android/loganalysis/item/BatteryStatsDetailedInfoItem.java new file mode 100644 index 0000000..a1ac0b0 --- /dev/null +++ b/src/com/android/loganalysis/item/BatteryStatsDetailedInfoItem.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.item; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * An {@link IItem} used to store BatteryStats Info + */ +public class BatteryStatsDetailedInfoItem implements IItem { + + /** Constant for JSON output */ + public static final String TIME_ON_BATTERY = "TIME_ON_BATTERY"; + /** Constant for JSON output */ + public static final String SCREEN_ON_TIME = "SCREEN_ON_TIME"; + /** Constant for JSON output */ + public static final String WAKELOCKS = "WAKELOCKS"; + /** Constant for JSON output */ + public static final String INTERRUPTS = "INTERRUPTS"; + /** Constant for JSON output */ + public static final String PROCESS_USAGE = "PROCESS_USAGE"; + + private long mTimeOnBattery = 0; + private long mScreenOnTime = 0; + private WakelockItem mWakelockItem = null; + private InterruptItem mInterruptItem = null; + private ProcessUsageItem mprocessUsageItem = null; + + /** + * Set the time on battery + */ + public void setTimeOnBattery(long timeOnBattery) { + mTimeOnBattery = timeOnBattery; + } + + /** + * Set the time on battery + */ + public void setScreenOnTime(long screenOnTime) { + mScreenOnTime = screenOnTime; + } + + /** + * Set the wakelock summary {@link WakelockItem} + */ + public void setWakelockItem(WakelockItem wakelockItem) { + mWakelockItem = wakelockItem; + } + + /** + * Set the interrupt summary {@link InterruptItem} + */ + public void setInterruptItem(InterruptItem interruptItem) { + mInterruptItem = interruptItem; + } + + /** + * Set the process usage {@link ProcessUsageItem} + */ + public void setProcessUsageItem(ProcessUsageItem processUsageItem) { + mprocessUsageItem = processUsageItem; + } + + /** + * Get the time on battery + */ + public long getTimeOnBattery() { + return mTimeOnBattery; + } + + /** + * Get the screen on time + */ + public long getScreenOnTime() { + return mScreenOnTime; + } + + /** + * Get the wakelock summary {@link WakelockItem} + */ + public WakelockItem getWakelockItem() { + return mWakelockItem; + } + + /** + * Get the interrupt summary {@link InterruptItem} + */ + public InterruptItem getInterruptItem() { + return mInterruptItem; + } + + /** + * Get the process usage summary {@link ProcessUsageItem} + */ + public ProcessUsageItem getProcessUsageItem() { + return mprocessUsageItem; + } + + /** + * {@inheritDoc} + */ + @Override + public IItem merge(IItem other) throws ConflictingItemException { + throw new ConflictingItemException("Dumpsys battery info items cannot be merged"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isConsistent(IItem other) { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public JSONObject toJson() { + JSONObject batteryStatsComponent = new JSONObject(); + try { + if (mTimeOnBattery > 0) { + batteryStatsComponent.put(TIME_ON_BATTERY, getTimeOnBattery()); + } + if (mScreenOnTime > 0) { + batteryStatsComponent.put(SCREEN_ON_TIME, getScreenOnTime()); + } + if (mWakelockItem != null) { + batteryStatsComponent.put(WAKELOCKS, mWakelockItem.toJson()); + } + if (mInterruptItem != null) { + batteryStatsComponent.put(INTERRUPTS, mInterruptItem.toJson()); + } + if (mprocessUsageItem != null) { + batteryStatsComponent.put(PROCESS_USAGE, mprocessUsageItem.toJson()); + } + } catch (JSONException e) { + // ignore + } + return batteryStatsComponent; + } +} diff --git a/src/com/android/loganalysis/item/BatteryStatsSummaryInfoItem.java b/src/com/android/loganalysis/item/BatteryStatsSummaryInfoItem.java new file mode 100644 index 0000000..6b07905 --- /dev/null +++ b/src/com/android/loganalysis/item/BatteryStatsSummaryInfoItem.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.item; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * An {@link GenericItem} used to store the power analysis summary + */ +public class BatteryStatsSummaryInfoItem extends GenericItem { + + /** Constant for JSON output */ + public static final String DISCHARGE_RATE = "DISCHARGE_RATE"; + /** Constant for JSON output */ + public static final String PEAK_DISCHARGE_TIME = "PEAK_DISCHARGE_TIME"; + + private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList( + DISCHARGE_RATE, PEAK_DISCHARGE_TIME)); + + /** + * The constructor for {@link BatteryStatsSummaryInfoItem}. + */ + public BatteryStatsSummaryInfoItem() { + super(ATTRIBUTES); + } + + /** + * Get the battery discharge rate + */ + public String getBatteryDischargeRate() { + return (String) getAttribute(DISCHARGE_RATE); + } + + /** + * Set the battery discharge rate + */ + public void setBatteryDischargeRate(String dischargeRate) { + setAttribute(DISCHARGE_RATE, dischargeRate); + } + + /** + * Get the peak discharge time + */ + public String getPeakDischargeTime() { + return (String) getAttribute(PEAK_DISCHARGE_TIME); + } + + /** + * Set the peak discharge time + */ + public void setPeakDischargeTime(String peakDischargeTime) { + setAttribute(PEAK_DISCHARGE_TIME, peakDischargeTime); + } +} diff --git a/src/com/android/loganalysis/item/DumpsysBatteryStatsItem.java b/src/com/android/loganalysis/item/DumpsysBatteryStatsItem.java new file mode 100644 index 0000000..1039324 --- /dev/null +++ b/src/com/android/loganalysis/item/DumpsysBatteryStatsItem.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.item; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * An {@link IItem} used to store BatteryStats Info + */ +public class DumpsysBatteryStatsItem implements IItem { + + /** Constant for JSON output */ + public static final String SUMMARY = "SUMMARY"; + /** Constant for JSON output */ + public static final String DETAILED_STATS = "DETAILED_STATS"; + + private BatteryStatsSummaryInfoItem mBatteryStatsSummaryItem; + private BatteryStatsDetailedInfoItem mDetailedBatteryStatsItem; + + /** + * Set the battery stats summary {@link BatteryStatsSummaryInfoItem} + */ + public void setBatteryStatsSummarytem(BatteryStatsSummaryInfoItem summaryItem) { + mBatteryStatsSummaryItem = summaryItem; + } + + /** + * Set the detailed battery stats item {@link BatteryStatsDetailedInfoItem} + */ + public void setDetailedBatteryStatsItem(BatteryStatsDetailedInfoItem detailedItem) { + mDetailedBatteryStatsItem = detailedItem; + } + + /** + * Get the battery stats summary {@link BatteryStatsSummaryInfoItem} + */ + public BatteryStatsSummaryInfoItem getBatteryStatsSummaryItem() { + return mBatteryStatsSummaryItem; + } + + /** + * Get the detailed battery stats item {@link BatteryStatsDetailedInfoItem} + */ + public BatteryStatsDetailedInfoItem getDetailedBatteryStatsItem() { + return mDetailedBatteryStatsItem; + } + + + /** + * {@inheritDoc} + */ + @Override + public IItem merge(IItem other) throws ConflictingItemException { + throw new ConflictingItemException("Dumpsys battery info items cannot be merged"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isConsistent(IItem other) { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public JSONObject toJson() { + JSONObject batteryStatsComponent = new JSONObject(); + try { + if (mBatteryStatsSummaryItem != null) { + batteryStatsComponent.put(SUMMARY, mBatteryStatsSummaryItem.toJson()); + } + if (mDetailedBatteryStatsItem != null) { + batteryStatsComponent.put(DETAILED_STATS, mDetailedBatteryStatsItem.toJson()); + } + } catch (JSONException e) { + // ignore + } + return batteryStatsComponent; + } +} diff --git a/src/com/android/loganalysis/item/DumpsysItem.java b/src/com/android/loganalysis/item/DumpsysItem.java index 866c56f..5bd82e2 100644 --- a/src/com/android/loganalysis/item/DumpsysItem.java +++ b/src/com/android/loganalysis/item/DumpsysItem.java @@ -25,28 +25,45 @@ import java.util.Set; public class DumpsysItem extends GenericItem { /** Constant for JSON output */ - private static final String BATTERY_INFO = "BATTERY_INFO"; + private static final String BATTERY_STATS = "BATTERY_STATS"; + /** Constant for JSON output */ + private static final String PROC_STATS = "PROC_STATS"; - private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList(BATTERY_INFO)); + private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList( + BATTERY_STATS, PROC_STATS)); /** - * The constructor for {@link BugreportItem}. + * The constructor for {@link DumpsysItem}. */ public DumpsysItem() { super(ATTRIBUTES); } /** - * Get the battery info section of the dumpsys. + * Set the {@link DumpsysBatteryStatsItem} of the bugreport. + */ + public void setBatteryInfo(DumpsysBatteryStatsItem batteryStats) { + setAttribute(BATTERY_STATS, batteryStats); + } + + /** + * Set the {@link DumpsysProcStatsItem} of the bugreport. + */ + public void setProcStats(DumpsysProcStatsItem procStats) { + setAttribute(PROC_STATS, procStats); + } + + /** + * Get the {@link DumpsysBatteryStatsItem} of the bugreport. */ - public DumpsysBatteryInfoItem getBatteryInfo() { - return (DumpsysBatteryInfoItem) getAttribute(BATTERY_INFO); + public DumpsysBatteryStatsItem getBatteryStats() { + return (DumpsysBatteryStatsItem) getAttribute(BATTERY_STATS); } /** - * Set the battery info section of the dumpsys. + * Get the {@link DumpsysProcStatsItem} of the bugreport. */ - public void setBatteryInfo(DumpsysBatteryInfoItem batteryInfo) { - setAttribute(BATTERY_INFO, batteryInfo); + public DumpsysProcStatsItem getProcStats() { + return (DumpsysProcStatsItem) getAttribute(PROC_STATS); } } diff --git a/src/com/android/loganalysis/item/DumpsysProcStatsItem.java b/src/com/android/loganalysis/item/DumpsysProcStatsItem.java new file mode 100644 index 0000000..846f70a --- /dev/null +++ b/src/com/android/loganalysis/item/DumpsysProcStatsItem.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.item; + +/** + * An {@link IItem} used to store uid and processname map. It is going to be + * used a helper item and not going to be marshalled to JSON + */ +public class DumpsysProcStatsItem extends GenericMapItem<String> { + +} diff --git a/src/com/android/loganalysis/item/InterruptItem.java b/src/com/android/loganalysis/item/InterruptItem.java new file mode 100644 index 0000000..1169004 --- /dev/null +++ b/src/com/android/loganalysis/item/InterruptItem.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.item; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** + * An {@link IItem} used to store information related to interrupts + */ +public class InterruptItem implements IItem { + /** Constant for JSON output */ + public static final String INTERRUPTS = "INTERRUPT_INFO"; + + private Collection<InterruptInfoItem> mInterrupts = new LinkedList<InterruptInfoItem>(); + + /** + * Enum for describing the type of interrupt + */ + public enum InterruptCategory { + WIFI_INTERRUPT, + MODEM_INTERRUPT, + ALARM_INTERRUPT, + ADSP_INTERRUPT, + UNKNOWN_INTERRUPT, + } + + public static class InterruptInfoItem extends GenericItem { + /** Constant for JSON output */ + public static final String NAME = "NAME"; + /** Constant for JSON output */ + public static final String CATEGORY = "CATEGORY"; + /** Constant for JSON output */ + public static final String INTERRUPT_COUNT = "INTERRUPT_COUNT"; + + private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList( + NAME, INTERRUPT_COUNT, CATEGORY)); + + /** + * The constructor for {@link InterruptItem} + * + * @param name The name of the wake lock + * @param interruptCount The number of times the interrupt woke up the AP + * @param category The {@link InterruptCategory} of the interrupt + */ + public InterruptInfoItem(String name, int interruptCount, + InterruptCategory category) { + super(ATTRIBUTES); + + setAttribute(NAME, name); + setAttribute(INTERRUPT_COUNT, interruptCount); + setAttribute(CATEGORY, category); + } + + /** + * Get the name of the interrupt + */ + public String getName() { + return (String) getAttribute(NAME); + } + + /** + * Get the interrupt count. + */ + public int getInterruptCount() { + return (Integer) getAttribute(INTERRUPT_COUNT); + } + + /** + * Get the {@link InterruptCategory} of the wake lock. + */ + public InterruptCategory getCategory() { + return (InterruptCategory) getAttribute(CATEGORY); + } + } + + /** + * Add an interrupt from the battery info section. + * + * @param name The name of the interrupt + * @param interruptCount Number of interrupts + * @param category The {@link InterruptCategory} of the interrupt. + */ + public void addInterrupt(String name, int interruptCount, + InterruptCategory category) { + mInterrupts.add(new InterruptInfoItem(name, interruptCount, category)); + } + + /** + * Get a list of {@link InterruptInfoItem} objects matching a given {@link InterruptCategory}. + */ + public List<InterruptInfoItem> getInterrupts(InterruptCategory category) { + LinkedList<InterruptInfoItem> interrupts = new LinkedList<InterruptInfoItem>(); + if (category == null) { + return interrupts; + } + + for (InterruptInfoItem interrupt : mInterrupts) { + if (category.equals(interrupt.getCategory())) { + interrupts.add(interrupt); + } + } + return interrupts; + } + + /** + * {@inheritDoc} + */ + @Override + public IItem merge(IItem other) throws ConflictingItemException { + throw new ConflictingItemException("Wakelock items cannot be merged"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isConsistent(IItem other) { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public JSONObject toJson() { + JSONObject object = new JSONObject(); + if (mInterrupts != null) { + try { + JSONArray interrupts = new JSONArray(); + for (InterruptInfoItem interrupt : mInterrupts) { + interrupts.put(interrupt.toJson()); + } + object.put(INTERRUPTS, interrupts); + } catch (JSONException e) { + // Ignore + } + } + return object; + } +} diff --git a/src/com/android/loganalysis/item/ProcessUsageItem.java b/src/com/android/loganalysis/item/ProcessUsageItem.java new file mode 100644 index 0000000..e2feddb --- /dev/null +++ b/src/com/android/loganalysis/item/ProcessUsageItem.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.item; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; + +/** + * An {@link IItem} used to store information related to network bandwidth, sensor usage, + * alarm usage by each processes + */ +public class ProcessUsageItem implements IItem { + + /** Constant for JSON output */ + public static final String PROCESS_USAGE = "PROCESS_USAGE"; + + private Collection<ProcessUsageInfoItem> mProcessUsage = + new LinkedList<ProcessUsageInfoItem>(); + + public static class SensorInfoItem extends GenericItem { + /** Constant for JSON output */ + public static final String SENSOR_NAME = "SENSOR_NAME"; + /** Constant for JSON output */ + public static final String USAGE_DURATION = "USAGE_DURATION"; + + private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList( + SENSOR_NAME, USAGE_DURATION)); + + /** + * The constructor for {@link SensorInfoItem} + * + * @param name The name of the sensor + * @param usageDuration Duration of the usage + */ + public SensorInfoItem(String name, long usageDuration) { + super(ATTRIBUTES); + + setAttribute(SENSOR_NAME, name); + setAttribute(USAGE_DURATION, usageDuration); + } + + /** + * Get the sensor name + */ + public String getSensorName() { + return (String) getAttribute(SENSOR_NAME); + } + + /** + * Get the sensor usage duration in milliseconds + */ + public long getUsageDurationMs() { + return (long) getAttribute(USAGE_DURATION); + } + } + + public static class ProcessUsageInfoItem extends GenericItem { + /** Constant for JSON output */ + public static final String ALARM_WAKEUPS = "ALARM_WAKEUPS"; + /** Constant for JSON output */ + public static final String SENSOR_USAGE = "SENSOR_USAGE"; + /** Constant for JSON output */ + public static final String PROCESS_UID = "PROCESS_UID"; + + private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList( + ALARM_WAKEUPS, SENSOR_USAGE, PROCESS_UID)); + + /** + * The constructor for {@link ProcessUsageItem} + * + * @param uid The name of the process + * @param alarmWakeups Number of alarm wakeups + * @param sensorUsage Different sensors used by the process + */ + public ProcessUsageInfoItem(String uid, int alarmWakeups, + LinkedList<SensorInfoItem> sensorUsage) { + super(ATTRIBUTES); + + setAttribute(PROCESS_UID, uid); + setAttribute(ALARM_WAKEUPS, alarmWakeups); + setAttribute(SENSOR_USAGE, sensorUsage); + } + + /** + * Get the number of Alarm wakeups + */ + public int getAlarmWakeups() { + return (int) getAttribute(ALARM_WAKEUPS); + } + + /** + * Get the Sensor usage of the process + */ + @SuppressWarnings("unchecked") + public LinkedList<SensorInfoItem> getSensorUsage() { + return (LinkedList<SensorInfoItem>) getAttribute(SENSOR_USAGE); + } + + /** + * Get the process name + */ + public String getProcessUID() { + return (String) getAttribute(PROCESS_UID); + } + + /** + * {@inheritDoc} + */ + @Override + public JSONObject toJson() { + JSONObject object = new JSONObject(); + try { + object.put(PROCESS_UID, getProcessUID()); + JSONArray sensorUsage = new JSONArray(); + for (SensorInfoItem usage : getSensorUsage()) { + sensorUsage.put(usage.toJson()); + } + object.put(SENSOR_USAGE, sensorUsage); + object.put(ALARM_WAKEUPS, getAlarmWakeups()); + + } catch (JSONException e) { + // Ignore + } + return object; + } + } + + /** + * Add individual process usage from the battery stats section. + * + * @param processUID The name of the process + * @param alarmWakeups The number of alarm wakeups + * @param sensorUsage Sensor usage of the process + */ + public void addProcessUsage(String processUID, int alarmWakeups, + LinkedList<SensorInfoItem> sensorUsage) { + mProcessUsage.add(new ProcessUsageInfoItem(processUID, alarmWakeups, sensorUsage)); + } + + /** + * {@inheritDoc} + */ + @Override + public IItem merge(IItem other) throws ConflictingItemException { + throw new ConflictingItemException("Wakelock items cannot be merged"); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isConsistent(IItem other) { + return false; + } + + public Collection<ProcessUsageInfoItem> getProcessUsage() { + return mProcessUsage; + } + + /** + * {@inheritDoc} + */ + @Override + public JSONObject toJson() { + JSONObject object = new JSONObject(); + if (mProcessUsage != null) { + try { + JSONArray processUsage = new JSONArray(); + for (ProcessUsageInfoItem usage : mProcessUsage) { + processUsage.put(usage.toJson()); + } + object.put(PROCESS_USAGE, processUsage); + } catch (JSONException e) { + // Ignore + } + } + return object; + } +} diff --git a/src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java b/src/com/android/loganalysis/item/WakelockItem.java index 8525f94..7bfab4b 100644 --- a/src/com/android/loganalysis/item/DumpsysBatteryInfoItem.java +++ b/src/com/android/loganalysis/item/WakelockItem.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2015 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. @@ -27,32 +27,30 @@ import java.util.List; import java.util.Set; /** - * An {@link IItem} used to store the battery info part of the dumpsys output. + * An {@link IItem} used to store information related to wake locks and kernel wake locks */ -public class DumpsysBatteryInfoItem implements IItem { +public class WakelockItem implements IItem { /** Constant for JSON output */ - public static final String WAKELOCKS = "WAKELOCKS"; + public static final String WAKELOCKS = "WAKELOCKS_INFO"; + + private Collection<WakelockInfoItem> mWakeLocks = new LinkedList<WakelockInfoItem>(); /** * Enum for describing the type of wakelock */ public enum WakeLockCategory { - LAST_CHARGE_WAKELOCK, - LAST_CHARGE_KERNEL_WAKELOCK, - LAST_UNPLUGGED_WAKELOCK, - LAST_UNPLUGGED_KERNEL_WAKELOCK; + KERNEL_WAKELOCK, + PARTIAL_WAKELOCK, } - /** - * A class designed to store information related to wake locks and kernel wake locks. - */ - public static class WakeLock extends GenericItem { - + public static class WakelockInfoItem extends GenericItem { /** Constant for JSON output */ public static final String NAME = "NAME"; /** Constant for JSON output */ - public static final String NUMBER = "NUMBER"; + public static final String PROCESS_UID = "PROCESS_UID"; + /** Constant for JSON output */ + public static final String PROCESS_NAME = "PROCESS_NAME"; /** Constant for JSON output */ public static final String HELD_TIME = "HELD_TIME"; /** Constant for JSON output */ @@ -61,35 +59,35 @@ public class DumpsysBatteryInfoItem implements IItem { public static final String CATEGORY = "CATEGORY"; private static final Set<String> ATTRIBUTES = new HashSet<String>(Arrays.asList( - NAME, NUMBER, HELD_TIME, LOCKED_COUNT, CATEGORY)); + NAME, PROCESS_UID, PROCESS_NAME, HELD_TIME, LOCKED_COUNT, CATEGORY)); /** - * The constructor for {@link WakeLock} + * The constructor for {@link WakelockItem} * * @param name The name of the wake lock * @param heldTime The amount of time held in milliseconds * @param lockedCount The number of times the wake lock was locked * @param category The {@link WakeLockCategory} of the wake lock */ - public WakeLock(String name, long heldTime, int lockedCount, WakeLockCategory category) { + public WakelockInfoItem(String name, long heldTime, int lockedCount, WakeLockCategory category) { this(name, null, heldTime, lockedCount, category); } /** - * The constructor for {@link WakeLock} + * The constructor for {@link WakelockItem} * * @param name The name of the wake lock - * @param number The number of the wake lock + * @param processUID The number of the wake lock * @param heldTime The amount of time held in milliseconds * @param lockedCount The number of times the wake lock was locked * @param category The {@link WakeLockCategory} of the wake lock */ - public WakeLock(String name, Integer number, long heldTime, int lockedCount, + public WakelockInfoItem(String name, String processUID, long heldTime, int lockedCount, WakeLockCategory category) { super(ATTRIBUTES); setAttribute(NAME, name); - setAttribute(NUMBER, number); + setAttribute(PROCESS_UID, processUID); setAttribute(HELD_TIME, heldTime); setAttribute(LOCKED_COUNT, lockedCount); setAttribute(CATEGORY, category); @@ -103,10 +101,10 @@ public class DumpsysBatteryInfoItem implements IItem { } /** - * Get the number of the wake lock. + * Get the process UID holding the wake lock. */ - public Integer getNumber() { - return (Integer) getAttribute(NUMBER); + public String getProcessUID() { + return (String) getAttribute(PROCESS_UID); } /** @@ -129,26 +127,31 @@ public class DumpsysBatteryInfoItem implements IItem { public WakeLockCategory getCategory() { return (WakeLockCategory) getAttribute(CATEGORY); } - } - private Collection<WakeLock> mWakeLocks = new LinkedList<WakeLock>(); + /** + * Set the process name holding the wake lock + */ + public void setWakelockProcessName(String processName) { + setAttribute(PROCESS_NAME, processName); + } + } /** - * Add a wakelock from the battery info section. + * Add a wakelock from the battery stats section. * * @param name The name of the wake lock. - * @param number The number of the wake lock. + * @param processUID The number of the wake lock. * @param heldTime The held time of the wake lock. * @param timesCalled The number of times the wake lock has been called. * @param category The {@link WakeLockCategory} of the wake lock. */ - public void addWakeLock(String name, Integer number, long heldTime, int timesCalled, + public void addWakeLock(String name, String processUID, long heldTime, int timesCalled, WakeLockCategory category) { - mWakeLocks.add(new WakeLock(name, number, heldTime, timesCalled, category)); + mWakeLocks.add(new WakelockInfoItem(name, processUID, heldTime, timesCalled, category)); } /** - * Add a wakelock from the battery info section. + * Add a wakelock from the battery stats section. * * @param name The name of the wake lock. * @param heldTime The held time of the wake lock. @@ -161,15 +164,15 @@ public class DumpsysBatteryInfoItem implements IItem { } /** - * Get a list of {@link WakeLock} objects matching a given {@link WakeLockCategory}. + * Get a list of {@link WakelockInfoItem} objects matching a given {@link WakeLockCategory}. */ - public List<WakeLock> getWakeLocks(WakeLockCategory category) { - LinkedList<WakeLock> wakeLocks = new LinkedList<WakeLock>(); + public List<WakelockInfoItem> getWakeLocks(WakeLockCategory category) { + LinkedList<WakelockInfoItem> wakeLocks = new LinkedList<WakelockInfoItem>(); if (category == null) { return wakeLocks; } - for (WakeLock wakeLock : mWakeLocks) { + for (WakelockInfoItem wakeLock : mWakeLocks) { if (category.equals(wakeLock.getCategory())) { wakeLocks.add(wakeLock); } @@ -178,11 +181,23 @@ public class DumpsysBatteryInfoItem implements IItem { } /** + * Get a list of {@link WakelockInfoItem} . + */ + public List<WakelockInfoItem> getWakeLocks() { + LinkedList<WakelockInfoItem> wakeLocks = new LinkedList<WakelockInfoItem>(); + + for (WakelockInfoItem wakeLock : mWakeLocks) { + wakeLocks.add(wakeLock); + } + return wakeLocks; + } + + /** * {@inheritDoc} */ @Override public IItem merge(IItem other) throws ConflictingItemException { - throw new ConflictingItemException("Dumpsys battery info items cannot be merged"); + throw new ConflictingItemException("Wakelock items cannot be merged"); } /** @@ -199,14 +214,16 @@ public class DumpsysBatteryInfoItem implements IItem { @Override public JSONObject toJson() { JSONObject object = new JSONObject(); - try { - JSONArray wakeLocks = new JSONArray(); - for (WakeLock wakeLock : mWakeLocks) { - wakeLocks.put(wakeLock.toJson()); + if (mWakeLocks != null) { + try { + JSONArray wakeLocks = new JSONArray(); + for (WakelockInfoItem wakeLock : mWakeLocks) { + wakeLocks.put(wakeLock.toJson()); + } + object.put(WAKELOCKS, wakeLocks); + } catch (JSONException e) { + // Ignore } - object.put(WAKELOCKS, wakeLocks); - } catch (JSONException e) { - // Ignore } return object; } diff --git a/src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParser.java b/src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParser.java new file mode 100644 index 0000000..f1bbed0 --- /dev/null +++ b/src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParser.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.BatteryStatsDetailedInfoItem; +import com.android.loganalysis.item.InterruptItem; +import com.android.loganalysis.item.ProcessUsageItem; +import com.android.loganalysis.item.WakelockItem; +import com.android.loganalysis.util.NumberFormattingUtil; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/** + * A {@link IParser} to parse the battery stats section of the bugreport + */ +public class BatteryStatsDetailedInfoParser extends AbstractSectionParser { + + private static final String WAKELOCK_SECTION_REGEX = "^\\s*All kernel wake locks:$"; + private static final String INTERRUPT_SECTION_REGEX = "^\\s*All wakeup reasons:$"; + private static final String PROCESS_USAGE_SECTION_REGEX = "^\\s*0:$"; + + /** + * Matches: Time on battery: 7h 45m 54s 332ms (98.3%) realtime, 4h 40m 51s 315ms (59.3%) uptime + */ + private static final Pattern TIME_ON_BATTERY_PATTERN = Pattern.compile( + "^\\s*Time on battery: (?:(\\d+)d)?\\s?(?:(\\d+)h)?\\s?(?:(\\d+)m)?\\s?(?:(\\d+)s)?" + + "\\s?(?:(\\d+)ms)?.*"); + /** + * Matches:Time on battery screen off: 1d 4h 6m 16s 46ms (99.1%) realtime, 6h 37m 49s 201ms + */ + private static final Pattern SCREEN_OFF_TIME_PATTERN = Pattern.compile("^\\s*Time on battery " + + "screen off: (?:(\\d+)d)?\\s?(?:(\\d+)h)?\\s?(?:(\\d+)m)?\\s?(?:(\\d+)s)?\\s?" + + "(?:(\\d+)ms).*"); + + private WakelockParser mWakelockParser = new WakelockParser(); + private InterruptParser mInterruptParser = new InterruptParser(); + private ProcessUsageParser mProcessUsageParser = new ProcessUsageParser(); + + private IParser mBatteryTimeParser = new IParser() { + @Override + public BatteryStatsDetailedInfoItem parse(List<String> lines) { + BatteryStatsDetailedInfoItem detailedInfo = null; + long timeOnBattery = 0, screenOffTime = 0; + Matcher m = null; + for (String line : lines) { + if (detailedInfo == null && !"".equals(line.trim())) { + detailedInfo = new BatteryStatsDetailedInfoItem(); + } + m = TIME_ON_BATTERY_PATTERN.matcher(line); + if (m.matches()) { + timeOnBattery = NumberFormattingUtil.getMs( + NumberFormattingUtil.parseIntOrZero(m.group(1)), + NumberFormattingUtil.parseIntOrZero(m.group(2)), + NumberFormattingUtil.parseIntOrZero(m.group(3)), + NumberFormattingUtil.parseIntOrZero(m.group(4)), + NumberFormattingUtil.parseIntOrZero(m.group(5))); + detailedInfo.setTimeOnBattery(timeOnBattery); + } else { + m = SCREEN_OFF_TIME_PATTERN.matcher(line); + if (m.matches()) { + screenOffTime = NumberFormattingUtil.getMs( + NumberFormattingUtil.parseIntOrZero(m.group(1)), + NumberFormattingUtil.parseIntOrZero(m.group(2)), + NumberFormattingUtil.parseIntOrZero(m.group(3)), + NumberFormattingUtil.parseIntOrZero(m.group(4)), + NumberFormattingUtil.parseIntOrZero(m.group(5))); + detailedInfo.setScreenOnTime(getScreenOnTime(timeOnBattery, screenOffTime)); + return detailedInfo; + } + } + } + return detailedInfo; + } + + private long getScreenOnTime(long timeOnBattery, long screenOffTime) { + if (timeOnBattery > screenOffTime) { + return (timeOnBattery - screenOffTime); + } + return 0; + } + }; + + private BatteryStatsDetailedInfoItem mBatteryStatsDetailedInfoItem = null; + private boolean mParsedInput = false; + + /** + * {@inheritDoc} + * + * @return The {@link BatteryStatsDetailedInfoItem} + */ + @Override + public BatteryStatsDetailedInfoItem parse(List<String> lines) { + setup(); + for (String line : lines) { + if (!mParsedInput && !"".equals(line.trim())) { + mParsedInput = true; + } + parseLine(line); + } + commit(); + return mBatteryStatsDetailedInfoItem; + } + + /** + * Sets up the parser by adding the section parsers. + */ + protected void setup() { + setParser(mBatteryTimeParser); + addSectionParser(mWakelockParser, WAKELOCK_SECTION_REGEX); + addSectionParser(mInterruptParser, INTERRUPT_SECTION_REGEX); + addSectionParser(mProcessUsageParser, PROCESS_USAGE_SECTION_REGEX); + } + + /** + * Set the {@link BatteryStatsDetailedInfoItem} + * + */ + @Override + protected void onSwitchParser() { + if (mBatteryStatsDetailedInfoItem == null) { + mBatteryStatsDetailedInfoItem = (BatteryStatsDetailedInfoItem) + getSection(mBatteryTimeParser); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void commit() { + // signal EOF + super.commit(); + if (mParsedInput) { + if (mBatteryStatsDetailedInfoItem == null) { + mBatteryStatsDetailedInfoItem = new BatteryStatsDetailedInfoItem(); + } + } + + if (mBatteryStatsDetailedInfoItem != null) { + mBatteryStatsDetailedInfoItem.setWakelockItem( + (WakelockItem) getSection(mWakelockParser)); + mBatteryStatsDetailedInfoItem.setInterruptItem( + (InterruptItem) getSection(mInterruptParser)); + mBatteryStatsDetailedInfoItem.setProcessUsageItem( + (ProcessUsageItem) getSection(mProcessUsageParser)); + } + } +} diff --git a/src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParser.java b/src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParser.java new file mode 100644 index 0000000..d5177e3 --- /dev/null +++ b/src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParser.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.BatteryDischargeItem; +import com.android.loganalysis.item.BatteryDischargeItem.BatteryDischargeInfoItem; +import com.android.loganalysis.item.BatteryStatsSummaryInfoItem; +import com.android.loganalysis.util.NumberFormattingUtil; + +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A {@link IParser} to parse batterystats summary + */ +public class BatteryStatsSummaryInfoParser implements IParser{ + + /** + * Matches: 0 (15) RESET:TIME: 2015-01-18-12-56-57 + */ + private static final Pattern RESET_TIME_PATTERN = Pattern.compile("^\\s*" + + "\\d\\s*\\(\\d+\\)\\s*RESET:TIME:\\s*(\\d+)-(\\d+)-(\\d+)-(\\d+)-(\\d+)-(\\d+)$"); + + /** + * Matches: +1d01h03m37s246ms (1) 028 c10400010 -running -wake_lock + */ + private static final Pattern BATTERY_DISCHARGE_PATTERN = Pattern.compile( + "^\\s*\\+(?:(\\d+)d)?(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+)s)?(?:(\\d+)ms)? \\(\\d+\\) " + + "(\\d+) \\w+ .*"); + + private BatteryDischargeItem mBatteryDischarge = new BatteryDischargeItem(); + private BatteryStatsSummaryInfoItem mItem = new BatteryStatsSummaryInfoItem(); + private long mBatteryDischargeRateAvg = 0; + private int mBatteryDischargeSamples = 0; + private Calendar mResetTime; + private static final int BATTERY_GROUP_LIMIT = 10; + + /** + * {@inheritDoc} + * + * @return The {@link BatteryStatsSummaryInfoItem}. + */ + @Override + public BatteryStatsSummaryInfoItem parse(List<String> lines) { + Matcher resetTimeMatcher = null; + Matcher dischargeMatcher = null; + + long previousDischargeElapsedTime= 0; + int previousBatteryLevel = 0; + boolean batteryDischargedFully = false; + for (String line : lines) { + resetTimeMatcher = RESET_TIME_PATTERN.matcher(line); + dischargeMatcher = BATTERY_DISCHARGE_PATTERN.matcher(line); + if (resetTimeMatcher.matches()) { + mResetTime = new GregorianCalendar(); + final int year = Integer.parseInt(resetTimeMatcher.group(1)); + final int month = Integer.parseInt(resetTimeMatcher.group(2)); + final int day = Integer.parseInt(resetTimeMatcher.group(3)); + final int hour = Integer.parseInt(resetTimeMatcher.group(4)); + final int minute = Integer.parseInt(resetTimeMatcher.group(5)); + final int second = Integer.parseInt(resetTimeMatcher.group(6)); + // Calendar month is zero indexed but the parsed date is 1-12 + mResetTime.set(year, (month - 1), day, hour, minute, second); + } else if (dischargeMatcher.matches()) { + final int days = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(1)); + final int hours = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(2)); + final int mins = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(3)); + final int secs = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(4)); + final int msecs = NumberFormattingUtil.parseIntOrZero(dischargeMatcher.group(5)); + final int batteryLevel = Integer.parseInt(dischargeMatcher.group(6)); + if (batteryLevel == 0) { + // Ignore the subsequent battery drop readings + batteryDischargedFully = true; + continue; + } else if (previousBatteryLevel == 0) { + // Ignore the first drop + previousBatteryLevel = batteryLevel; + continue; + } else if (!batteryDischargedFully && previousBatteryLevel != batteryLevel) { + long elapsedTime = NumberFormattingUtil.getMs(days, hours, mins, secs, msecs); + mBatteryDischargeRateAvg += (elapsedTime - previousDischargeElapsedTime); + mBatteryDischargeSamples++; + mBatteryDischarge.addBatteryDischargeInfo( + getDischargeClockTime(days, hours, mins, secs), + (elapsedTime - previousDischargeElapsedTime), batteryLevel); + previousDischargeElapsedTime = elapsedTime; + previousBatteryLevel = batteryLevel; + } + } + } + mItem.setBatteryDischargeRate(getAverageDischargeRate()); + mItem.setPeakDischargeTime(getPeakDischargeTime()); + return mItem; + } + + private Calendar getDischargeClockTime(int days, int hours, int mins, int secs) { + Calendar dischargeClockTime = new GregorianCalendar(); + + dischargeClockTime.setTime(mResetTime.getTime()); + dischargeClockTime.add(Calendar.DATE, days); + dischargeClockTime.add(Calendar.HOUR, hours); + dischargeClockTime.add(Calendar.MINUTE, mins); + dischargeClockTime.add(Calendar.SECOND, secs); + return dischargeClockTime; + } + + private String getAverageDischargeRate() { + if (mBatteryDischargeSamples == 0) { + return "The battery did not discharge"; + } + + final long minsPerLevel = mBatteryDischargeRateAvg / (mBatteryDischargeSamples * 60 * 1000); + return String.format("The battery dropped a level %d mins on average", minsPerLevel); + } + + private String getPeakDischargeTime() { + + int peakDischargeStartBatteryLevel = 0, peakDischargeStopBatteryLevel = 0; + long minDischargeDuration = 0; + Calendar peakDischargeStartTime= null, peakDischargeStopTime = null; + Queue <BatteryDischargeInfoItem> batteryDischargeWindow = + new LinkedList <BatteryDischargeInfoItem>(); + long sumDischargeDuration = 0; + for (BatteryDischargeInfoItem dischargeSteps : mBatteryDischarge.getDischargeStepsInfo()) { + batteryDischargeWindow.add(dischargeSteps); + sumDischargeDuration += dischargeSteps.getElapsedTime(); + if (batteryDischargeWindow.size() >= BATTERY_GROUP_LIMIT) { + final long averageDischargeDuration = sumDischargeDuration/BATTERY_GROUP_LIMIT; + final BatteryDischargeInfoItem startNode = batteryDischargeWindow.remove(); + sumDischargeDuration -= startNode.getElapsedTime(); + + if (minDischargeDuration == 0 || averageDischargeDuration < minDischargeDuration) { + minDischargeDuration = averageDischargeDuration; + peakDischargeStartBatteryLevel = startNode.getBatteryLevel(); + peakDischargeStopBatteryLevel = dischargeSteps.getBatteryLevel(); + peakDischargeStartTime = startNode.getClockTime(); + peakDischargeStopTime = dischargeSteps.getClockTime(); + } + } + } + if (peakDischargeStartTime != null && peakDischargeStopTime != null && + peakDischargeStartBatteryLevel > 0 && peakDischargeStopBatteryLevel > 0) { + return String.format( + "The peak discharge time was during %s to %s where battery dropped from %d to " + + "%d", peakDischargeStartTime.getTime().toString(), + peakDischargeStopTime.getTime().toString(), peakDischargeStartBatteryLevel, + peakDischargeStopBatteryLevel); + + } else { + return "The battery did not discharge"; + } + } + + /** + * Get the {@link BatteryStatsSummaryInfoItem}. + * <p> + * Exposed for unit testing. + * </p> + */ + BatteryStatsSummaryInfoItem getItem() { + return mItem; + } +} diff --git a/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java b/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java deleted file mode 100644 index afd4359..0000000 --- a/src/com/android/loganalysis/parser/DumpsysBatteryInfoParser.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.loganalysis.parser; - -import com.android.loganalysis.item.DumpsysBatteryInfoItem; -import com.android.loganalysis.item.DumpsysBatteryInfoItem.WakeLockCategory; - -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A {@link IParser} to handle the "dumpsys batteryinfo" command output. - */ -public class DumpsysBatteryInfoParser implements IParser { - private static final Pattern LAST_CHARGED_START_PAT = Pattern.compile( - "^Statistics since last charge:$"); - private static final Pattern LAST_UNPLUGGED_START_PAT = Pattern.compile( - "^Statistics since last unplugged:$"); - private static final Pattern WAKE_LOCK_START_PAT = Pattern.compile( - "^ All partial wake locks:$"); - - private static final String WAKE_LOCK_PAT_SUFFIX = - "((\\d+)d )?((\\d+)h )?((\\d+)m )?((\\d+)s )?((\\d+)ms )?\\((\\d+) times\\) realtime"; - - /** - * Match a valid line such as: - * " Kernel Wake lock \"Process\": 1d 2h 3m 4s 5ms (6 times) realtime"; - */ - private static final Pattern KERNEL_WAKE_LOCK_PAT = Pattern.compile( - "^ Kernel Wake lock \"([^\"]+)\": " + WAKE_LOCK_PAT_SUFFIX); - /** - * Match a valid line such as: - * " Wake lock #1234 Process: 1d 2h 3m 4s 5ms (6 times) realtime"; - */ - private static final Pattern WAKE_LOCK_PAT = Pattern.compile( - "^ Wake lock #(\\d+) (.+): " + WAKE_LOCK_PAT_SUFFIX); - - private DumpsysBatteryInfoItem mItem = new DumpsysBatteryInfoItem(); - - /** - * {@inheritDoc} - */ - @Override - public DumpsysBatteryInfoItem parse(List<String> lines) { - WakeLockCategory kernelWakeLockCategory = null; - WakeLockCategory wakeLockCategory = null; - boolean inKernelWakeLock = false; - boolean inWakeLock = false; - - // Look for the section for last unplugged statistics. Kernel wakelocks are in the lines - // immediately following, until a blank line. Partial wake locks are in their own block, - // until a blank line. Return immediately after since there is nothing left to parse. - for (String line : lines) { - if (kernelWakeLockCategory == null || wakeLockCategory == null) { - Matcher m = LAST_CHARGED_START_PAT.matcher(line); - if (m.matches()) { - kernelWakeLockCategory = WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK; - wakeLockCategory = WakeLockCategory.LAST_CHARGE_WAKELOCK; - inKernelWakeLock = true; - } - m = LAST_UNPLUGGED_START_PAT.matcher(line); - if (m.matches()) { - kernelWakeLockCategory = WakeLockCategory.LAST_UNPLUGGED_KERNEL_WAKELOCK; - wakeLockCategory = WakeLockCategory.LAST_UNPLUGGED_WAKELOCK; - inKernelWakeLock = true; - } - } else { - if (inKernelWakeLock) { - if ("".equals(line.trim())) { - inKernelWakeLock = false; - } else { - parseKernelWakeLock(line, kernelWakeLockCategory); - } - } else if (inWakeLock) { - if ("".equals(line.trim())) { - inWakeLock = false; - kernelWakeLockCategory = null; - wakeLockCategory = null; - } else { - parseWakeLock(line, wakeLockCategory); - } - } else { - Matcher m = WAKE_LOCK_START_PAT.matcher(line); - if (m.matches()) { - inWakeLock = true; - } - } - } - } - return mItem; - } - - /** - * Parse a line of output and add it to the last unplugged kernel wake lock section. - * <p> - * Exposed for unit testing. - * </p> - */ - void parseKernelWakeLock(String line, WakeLockCategory category) { - Matcher m = KERNEL_WAKE_LOCK_PAT.matcher(line); - if (!m.matches()) { - return; - } - - final String name = m.group(1); - final long days = parseLongOrZero(m.group(3)); - final long hours = parseLongOrZero(m.group(5)); - final long mins = parseLongOrZero(m.group(7)); - final long secs = parseLongOrZero(m.group(9)); - final long msecs = parseLongOrZero(m.group(11)); - final int timesCalled = Integer.parseInt(m.group(12)); - - mItem.addWakeLock(name, getMs(days, hours, mins, secs, msecs), timesCalled, category); - } - - /** - * Parse a line of output and add it to the last unplugged wake lock section. - * <p> - * Exposed for unit testing. - * </p> - */ - void parseWakeLock(String line, WakeLockCategory category) { - Matcher m = WAKE_LOCK_PAT.matcher(line); - if (!m.matches()) { - return; - } - - final int number = Integer.parseInt(m.group(1)); - final String name = m.group(2); - final long days = parseLongOrZero(m.group(4)); - final long hours = parseLongOrZero(m.group(6)); - final long mins = parseLongOrZero(m.group(8)); - final long secs = parseLongOrZero(m.group(10)); - final long msecs = parseLongOrZero(m.group(12)); - final int timesCalled = Integer.parseInt(m.group(13)); - - mItem.addWakeLock(name, number, getMs(days, hours, mins, secs, msecs), timesCalled, - category); - } - - /** - * Get the {@link DumpsysBatteryInfoItem}. - * <p> - * Exposed for unit testing. - * </p> - */ - DumpsysBatteryInfoItem getItem() { - return mItem; - } - - /** - * Convert days/hours/mins/secs/msecs into milliseconds. - * <p> - * Exposed for unit testing. - * </p> - */ - static long getMs(long days, long hours, long mins, long secs, long msecs) { - return (((24 * days + hours) * 60 + mins) * 60 + secs) * 1000 + msecs; - } - - /** - * Parses a string into a long, or returns 0 if the string is null. - * - * @param s a {@link String} containing the long representation to be parsed - * @return the long represented by the argument in decimal, or 0 if the string is {@code null}. - * @throws NumberFormatException if the string is not {@code null} or does not contain a - * parsable long. - */ - private long parseLongOrZero(String s) { - if (s == null) { - return 0; - } - return Long.parseLong(s); - } -} diff --git a/src/com/android/loganalysis/parser/DumpsysBatteryStatsParser.java b/src/com/android/loganalysis/parser/DumpsysBatteryStatsParser.java new file mode 100644 index 0000000..3e8be2c --- /dev/null +++ b/src/com/android/loganalysis/parser/DumpsysBatteryStatsParser.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.BatteryStatsDetailedInfoItem; +import com.android.loganalysis.item.DumpsysBatteryStatsItem; +import com.android.loganalysis.item.BatteryStatsSummaryInfoItem; + +import java.util.List; + + +/** + * A {@link IParser} to parse the battery stats section of the bugreport + */ +public class DumpsysBatteryStatsParser extends AbstractSectionParser { + + private static final String SUMMARY_INFO_SECTION_REGEX = + "Battery History \\(\\d+% used, \\d+KB used of \\d+KB, \\d+ strings using \\d+KB\\):$"; + private static final String DETAILED_INFO_SECTION_REGEX = "^Statistics since last charge:$"; + private static final String NOOP_SECTION_REGEX = "^Statistics since last unplugged:$"; + + private BatteryStatsSummaryInfoParser mSummaryParser = new BatteryStatsSummaryInfoParser(); + private BatteryStatsDetailedInfoParser mDetailedParser = new BatteryStatsDetailedInfoParser(); + + private DumpsysBatteryStatsItem mDumpsysBatteryStatsItem = null; + private boolean mParsedInput = false; + + /** + * {@inheritDoc} + * + * @return The {@link DumpsysBatteryStatsItem} + */ + @Override + public DumpsysBatteryStatsItem parse(List<String> lines) { + setup(); + for (String line : lines) { + if (!mParsedInput && !"".equals(line.trim())) { + mParsedInput = true; + } + parseLine(line); + } + commit(); + return mDumpsysBatteryStatsItem; + } + + /** + * Sets up the parser by adding the section parsers. + */ + protected void setup() { + addSectionParser(mSummaryParser, SUMMARY_INFO_SECTION_REGEX); + addSectionParser(mDetailedParser, DETAILED_INFO_SECTION_REGEX); + addSectionParser(new NoopParser(), NOOP_SECTION_REGEX); + } + + /** + * {@inheritDoc} + */ + @Override + protected void commit() { + // signal EOF + super.commit(); + if (mParsedInput) { + if (mDumpsysBatteryStatsItem == null) { + mDumpsysBatteryStatsItem = new DumpsysBatteryStatsItem(); + } + } + + if (mDumpsysBatteryStatsItem != null) { + mDumpsysBatteryStatsItem.setBatteryStatsSummarytem( + (BatteryStatsSummaryInfoItem) getSection(mSummaryParser)); + mDumpsysBatteryStatsItem.setDetailedBatteryStatsItem( + (BatteryStatsDetailedInfoItem) getSection(mDetailedParser)); + } + } +} diff --git a/src/com/android/loganalysis/parser/DumpsysParser.java b/src/com/android/loganalysis/parser/DumpsysParser.java index e950341..d8941e0 100644 --- a/src/com/android/loganalysis/parser/DumpsysParser.java +++ b/src/com/android/loganalysis/parser/DumpsysParser.java @@ -15,8 +15,10 @@ */ package com.android.loganalysis.parser; -import com.android.loganalysis.item.DumpsysBatteryInfoItem; + +import com.android.loganalysis.item.DumpsysBatteryStatsItem; import com.android.loganalysis.item.DumpsysItem; +import com.android.loganalysis.item.DumpsysProcStatsItem; import java.util.List; @@ -24,10 +26,14 @@ import java.util.List; * A {@link IParser} to handle the output of the dumpsys section of the bugreport. */ public class DumpsysParser extends AbstractSectionParser { - private static final String BATTERY_INFO_SECTION_REGEX = "DUMP OF SERVICE batteryinfo:"; + + private static final String BATTERY_STATS_SECTION_REGEX = "^DUMP OF SERVICE batterystats:$"; + private static final String PROC_STATS_SECTION_REGEX = "^DUMP OF SERVICE procstats:"; private static final String NOOP_SECTION_REGEX = "DUMP OF SERVICE .*"; - private DumpsysBatteryInfoParser mBatteryInfoParser = new DumpsysBatteryInfoParser(); + private DumpsysBatteryStatsParser mBatteryStatsParser = new DumpsysBatteryStatsParser(); + private DumpsysProcStatsParser mProcStatsParser = new DumpsysProcStatsParser(); + private DumpsysItem mDumpsys = null; /** @@ -53,7 +59,8 @@ public class DumpsysParser extends AbstractSectionParser { * Sets up the parser by adding the section parsers. */ protected void setup() { - addSectionParser(mBatteryInfoParser, BATTERY_INFO_SECTION_REGEX); + addSectionParser(mBatteryStatsParser, BATTERY_STATS_SECTION_REGEX); + addSectionParser(mProcStatsParser, PROC_STATS_SECTION_REGEX); addSectionParser(new NoopParser(), NOOP_SECTION_REGEX); } @@ -64,10 +71,12 @@ public class DumpsysParser extends AbstractSectionParser { protected void commit() { // signal EOF super.commit(); - + if (mDumpsys == null) { + mDumpsys = new DumpsysItem(); + } if (mDumpsys != null) { - mDumpsys.setBatteryInfo( - (DumpsysBatteryInfoItem) getSection(mBatteryInfoParser)); + mDumpsys.setBatteryInfo((DumpsysBatteryStatsItem) getSection(mBatteryStatsParser)); + mDumpsys.setProcStats((DumpsysProcStatsItem) getSection(mProcStatsParser)); } } } diff --git a/src/com/android/loganalysis/parser/DumpsysProcStatsParser.java b/src/com/android/loganalysis/parser/DumpsysProcStatsParser.java new file mode 100644 index 0000000..010036b --- /dev/null +++ b/src/com/android/loganalysis/parser/DumpsysProcStatsParser.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.DumpsysProcStatsItem; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A {@link IParser} to parse procstats and create a mapping table of process names and UIDs + */ +public class DumpsysProcStatsParser implements IParser { + + /** + * Matches: * com.google.android.googlequicksearchbox:search / u0a19 / v300401240: ----- + */ + private static final Pattern UID = Pattern.compile("^\\s*\\* (.*):?.*/ (.*)/.*"); + + /** + * {@inheritDoc} + * + * @return The {@link DumpsysProcStatsItem}. + */ + @Override + public DumpsysProcStatsItem parse(List<String> lines) { + DumpsysProcStatsItem item = new DumpsysProcStatsItem(); + for (String line : lines) { + Matcher m = UID.matcher(line); + if(m.matches()) { + item.put(m.group(2).trim(), m.group(1).trim()); + } + } + return item; + } + +} diff --git a/src/com/android/loganalysis/parser/InterruptParser.java b/src/com/android/loganalysis/parser/InterruptParser.java new file mode 100644 index 0000000..26417c4 --- /dev/null +++ b/src/com/android/loganalysis/parser/InterruptParser.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.InterruptItem; +import com.android.loganalysis.item.InterruptItem.InterruptCategory; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A {@link IParser} to parse wake up interrupts + */ +public class InterruptParser implements IParser { + + /** + * Matches: Wakeup reason 289:bcmsdh_sdmmc:200:qcom,smd-rpm:240:msmgpio: + * 20m 5s 194ms (1485 times) realtime + */ + private static final Pattern Interrupt = Pattern.compile( + "^\\s*Wakeup reason (.*): (?:\\d+h )?(?:\\d+m )?(?:\\d+s )(?:\\d+ms )" + + "\\((\\d+) times\\) realtime"); + + private InterruptItem mItem = new InterruptItem(); + + /** + * {@inheritDoc} + * + * @return The {@link InterruptItem}. + */ + @Override + public InterruptItem parse(List<String> lines) { + for (String line : lines) { + Matcher m = Interrupt.matcher(line); + if(m.matches()) { + final String interruptName = m.group(1); + final int interruptCount = Integer.parseInt(m.group(2)); + mItem.addInterrupt(interruptName, interruptCount, + getInterruptCategory(interruptName)); + } else { + // Done with interrupts + break; + } + } + return mItem; + } + + /** + * Get the {@link InterruptItem}. + * <p> + * Exposed for unit testing. + * </p> + */ + InterruptItem getItem() { + return mItem; + } + + private InterruptCategory getInterruptCategory(String interruptName) { + if (interruptName.contains("bcmsdh_sdmmc")) { + return InterruptCategory.WIFI_INTERRUPT; + } else if (interruptName.contains("smd-modem") || + interruptName.contains("smsm-modem")) { + return InterruptCategory.MODEM_INTERRUPT; + } else if (interruptName.contains("smd-adsp")) { + return InterruptCategory.ADSP_INTERRUPT; + } else if (interruptName.contains("max77686-irq") || + interruptName.contains("cpcap-irq") || + interruptName.contains("TWL6030-PIH")) { + return InterruptCategory.ALARM_INTERRUPT; + } + + return InterruptCategory.UNKNOWN_INTERRUPT; + } + +} diff --git a/src/com/android/loganalysis/parser/ProcessUsageParser.java b/src/com/android/loganalysis/parser/ProcessUsageParser.java new file mode 100644 index 0000000..9609cd2 --- /dev/null +++ b/src/com/android/loganalysis/parser/ProcessUsageParser.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.ProcessUsageItem; +import com.android.loganalysis.item.ProcessUsageItem.SensorInfoItem; +import com.android.loganalysis.util.NumberFormattingUtil; + +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A {@link IParser} to handle the parsing of process usage information + */ +public class ProcessUsageParser implements IParser { + + private ProcessUsageItem mItem = new ProcessUsageItem(); + private LinkedList<SensorInfoItem> mSensorUsage = new LinkedList<SensorInfoItem>(); + + /** + * Matches: 1000: + */ + private static final Pattern UID_PATTERN = Pattern.compile("^\\s*(\\w+):$"); + + /** + * Matches: Sensor 1: 12m 52s 311ms realtime (29 times) + */ + private static final Pattern SENSOR_PATTERN = Pattern.compile( + "^\\s*Sensor (\\d+): (?:(\\d+)d\\s)?" + + "(?:(\\d+)h\\s)?(?:(\\d+)m\\s)?(?:(\\d+)s\\s)?(\\d+)ms " + + "realtime \\((\\d+) times\\)$"); + + /** + * Matches: 507 wakeup alarms + */ + private static final Pattern ALARM_PATTERN = Pattern.compile("^\\s*(\\d+) wakeup alarms$"); + + /** + * {@inheritDoc} + */ + @Override + public ProcessUsageItem parse(List<String> lines) { + String processUid = null; + int alarmWakeups = 0; + for (String line : lines) { + Matcher m = UID_PATTERN.matcher(line); + if (m.matches()) { + if (processUid != null) { + // Save the process usage info for the previous process + mItem.addProcessUsage(processUid, alarmWakeups, mSensorUsage); + } + processUid = m.group(1); + mSensorUsage = new LinkedList<SensorInfoItem>(); + continue; + } + m = SENSOR_PATTERN.matcher(line); + if (m.matches()) { + final long duration = NumberFormattingUtil.getMs( + NumberFormattingUtil.parseIntOrZero(m.group(2)), + NumberFormattingUtil.parseIntOrZero(m.group(3)), + NumberFormattingUtil.parseIntOrZero(m.group(4)), + NumberFormattingUtil.parseIntOrZero(m.group(5)), + NumberFormattingUtil.parseIntOrZero(m.group(6))); + mSensorUsage.add(new SensorInfoItem(m.group(1), duration)); + continue; + } + m = ALARM_PATTERN.matcher(line); + if (m.matches()) { + alarmWakeups = Integer.parseInt(m.group(1)); + } + } + return mItem; + } + + /** + * Get the {@link ProcessUsageItem}. + * <p> + * Exposed for unit testing. + * </p> + */ + ProcessUsageItem getItem() { + return mItem; + } +} diff --git a/src/com/android/loganalysis/parser/WakelockParser.java b/src/com/android/loganalysis/parser/WakelockParser.java new file mode 100644 index 0000000..91652f7 --- /dev/null +++ b/src/com/android/loganalysis/parser/WakelockParser.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.WakelockItem; +import com.android.loganalysis.item.WakelockItem.WakeLockCategory; +import com.android.loganalysis.util.NumberFormattingUtil; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A {@link IParser} to handle the parsing of wakelock information + */ +public class WakelockParser implements IParser { + + private static final Pattern PARTIAL_WAKE_LOCK_START_PAT = Pattern.compile( + "^\\s*All partial wake locks:$"); + + private static final String WAKE_LOCK_PAT_SUFFIX = + "(?:(\\d+)d)?\\s?(?:(\\d+)h)?\\s?(?:(\\d+)m)?\\s?(?:(\\d+)s)?\\s?(?:(\\d+)ms)?" + + "\\s?\\((\\d+) times\\) realtime"; + + /** + * Match a valid line such as: + * " Kernel Wake lock PowerManagerService.WakeLocks: 1h 13m 50s 950ms (2858 times) realtime" + */ + private static final Pattern KERNEL_WAKE_LOCK_PAT = Pattern.compile( + "^\\s*Kernel Wake lock (.+): " + WAKE_LOCK_PAT_SUFFIX); + /** + * Match a valid line such as: + * " Wake lock u0a7 NlpWakeLock: 8m 13s 203ms (1479 times) realtime"; + */ + private static final Pattern PARTIAL_WAKE_LOCK_PAT = Pattern.compile( + "^\\s*Wake lock (.*)\\s(.+): " + WAKE_LOCK_PAT_SUFFIX); + + private WakelockItem mItem = new WakelockItem(); + + public static final int TOP_WAKELOCK_COUNT = 5; + + /** + * {@inheritDoc} + */ + @Override + public WakelockItem parse(List<String> lines) { + boolean inPartialWakeLock = false; + Matcher m = null; + int wakelockCounter = 0; + for (String line : lines) { + if ("".equals(line.trim())) { + if (inPartialWakeLock) { + // Done with parsing wakelock sections + break; + } else { + // Done with parsing kernel wakelocks and continue with + // partial wakelock + wakelockCounter = 0; + continue; + } + } + m = KERNEL_WAKE_LOCK_PAT.matcher(line); + if (m.matches()) { + if (wakelockCounter < TOP_WAKELOCK_COUNT && + !line.contains("PowerManagerService.WakeLocks")) { + parseKernelWakeLock(line, WakeLockCategory.KERNEL_WAKELOCK); + wakelockCounter++; + } + continue; + } + m = PARTIAL_WAKE_LOCK_START_PAT.matcher(line); + if (m.matches()) { + inPartialWakeLock = true; + continue; + } + m = PARTIAL_WAKE_LOCK_PAT.matcher(line); + if (m.matches() && wakelockCounter < TOP_WAKELOCK_COUNT) { + parsePartialWakeLock(line, WakeLockCategory.PARTIAL_WAKELOCK); + wakelockCounter++; + } + } + return mItem; + } + + /** + * Parse a line of output and add it to wakelock section + * <p> + * Exposed for unit testing. + * </p> + */ + void parseKernelWakeLock(String line, WakeLockCategory category) { + Matcher m = KERNEL_WAKE_LOCK_PAT.matcher(line); + if (!m.matches()) { + return; + } + final String name = m.group(1); + final long wakelockTime = NumberFormattingUtil.getMs( + NumberFormattingUtil.parseIntOrZero(m.group(2)), + NumberFormattingUtil.parseIntOrZero(m.group(3)), + NumberFormattingUtil.parseIntOrZero(m.group(4)), + NumberFormattingUtil.parseIntOrZero(m.group(5)), + NumberFormattingUtil.parseIntOrZero(m.group(6))); + + final int timesCalled = Integer.parseInt(m.group(7)); + + mItem.addWakeLock(name, wakelockTime, timesCalled, category); + } + + /** + * Parse a line of output and add it to wake lock section. + * <p> + * Exposed for unit testing. + * </p> + */ + void parsePartialWakeLock(String line, WakeLockCategory category) { + Matcher m = PARTIAL_WAKE_LOCK_PAT.matcher(line); + if (!m.matches()) { + return; + } + final String processUID = m.group(1); + final String name = m.group(2); + final long wakelockTime = NumberFormattingUtil.getMs( + NumberFormattingUtil.parseIntOrZero(m.group(3)), + NumberFormattingUtil.parseIntOrZero(m.group(4)), + NumberFormattingUtil.parseIntOrZero(m.group(5)), + NumberFormattingUtil.parseIntOrZero(m.group(6)), + NumberFormattingUtil.parseIntOrZero(m.group(7))); + final int timesCalled = Integer.parseInt(m.group(8)); + + mItem.addWakeLock(name, processUID, wakelockTime, timesCalled, category); + } + + /** + * Get the {@link WakelockItem}. + * <p> + * Exposed for unit testing. + * </p> + */ + WakelockItem getItem() { + return mItem; + } +} diff --git a/src/com/android/loganalysis/rule/AbstractPowerRule.java b/src/com/android/loganalysis/rule/AbstractPowerRule.java new file mode 100644 index 0000000..cab46a3 --- /dev/null +++ b/src/com/android/loganalysis/rule/AbstractPowerRule.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2015 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.rule; + +import com.android.loganalysis.item.BatteryStatsDetailedInfoItem; +import com.android.loganalysis.item.BugreportItem; +import com.android.loganalysis.item.BatteryStatsSummaryInfoItem; +import com.android.loganalysis.item.DumpsysProcStatsItem; + +import org.json.JSONObject; + +/** + * Base class for all power rules + */ +public abstract class AbstractPowerRule implements IRule { + + private BugreportItem mBugreportItem; + private BatteryStatsSummaryInfoItem mPowerSummaryAnalysisItem; + private BatteryStatsDetailedInfoItem mPowerDetailedAnalysisItem; + private DumpsysProcStatsItem mProcStatsItem; + + public AbstractPowerRule(BugreportItem bugreportItem) { + mBugreportItem = bugreportItem; + mPowerSummaryAnalysisItem = mBugreportItem.getDumpsys().getBatteryStats(). + getBatteryStatsSummaryItem(); + mPowerDetailedAnalysisItem = mBugreportItem.getDumpsys().getBatteryStats(). + getDetailedBatteryStatsItem(); + mProcStatsItem = mBugreportItem.getDumpsys().getProcStats(); + } + + protected long getTimeOnBattery() { + return mPowerDetailedAnalysisItem.getTimeOnBattery(); + } + + protected BatteryStatsSummaryInfoItem getSummaryItem() { + return mPowerSummaryAnalysisItem; + } + + protected BatteryStatsDetailedInfoItem getDetailedAnalysisItem() { + return mPowerDetailedAnalysisItem; + } + + protected DumpsysProcStatsItem getProcStatsItem() { + return mProcStatsItem; + } + + @Override + public abstract void applyRule(); + + @Override + public abstract JSONObject getAnalysis(); + +} diff --git a/src/com/android/loganalysis/rule/IRule.java b/src/com/android/loganalysis/rule/IRule.java new file mode 100644 index 0000000..416d7f6 --- /dev/null +++ b/src/com/android/loganalysis/rule/IRule.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015 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.rule; + +import org.json.JSONObject; + +/** + * An interface which defines the rules. Individual rules will apply the filter on the parsed data + * and return the high level analysis in JSON Format + */ +public interface IRule { + // Apply the rules + public void applyRule(); + + public JSONObject getAnalysis(); +} diff --git a/src/com/android/loganalysis/rule/ProcessUsageRule.java b/src/com/android/loganalysis/rule/ProcessUsageRule.java new file mode 100644 index 0000000..4e15aee --- /dev/null +++ b/src/com/android/loganalysis/rule/ProcessUsageRule.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2015 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.rule; + +import com.android.loganalysis.item.BugreportItem; +import com.android.loganalysis.item.DumpsysProcStatsItem; +import com.android.loganalysis.item.ProcessUsageItem; +import com.android.loganalysis.item.ProcessUsageItem.ProcessUsageInfoItem; + +import org.json.JSONException; +import org.json.JSONObject; + + +/** + * Rules definition for Process usage + */ +public class ProcessUsageRule extends AbstractPowerRule { + + private static final String PROCESS_USAGE_ANALYSIS = "PROCESS_USAGE_ANALYSIS"; + private static final long ALARM_THRESHOLD = 60000; + + private StringBuffer mAnalysisBuffer; + + public ProcessUsageRule (BugreportItem bugreportItem) { + super(bugreportItem); + } + + + @Override + public void applyRule() { + mAnalysisBuffer = new StringBuffer(); + ProcessUsageItem processUsageItem = getDetailedAnalysisItem().getProcessUsageItem(); + DumpsysProcStatsItem procStatsItem = getProcStatsItem(); + if (processUsageItem != null && procStatsItem!= null) { + for (ProcessUsageInfoItem usage : processUsageItem.getProcessUsage()) { + if (usage.getAlarmWakeups() > 0) { + final long alarmsPerMs = getTimeOnBattery()/usage.getAlarmWakeups(); + if (alarmsPerMs < ALARM_THRESHOLD) { + final String processName = procStatsItem.get(usage.getProcessUID()); + if (processName != null) { + mAnalysisBuffer.append(processName); + } else { + mAnalysisBuffer.append(usage.getProcessUID()); + } + mAnalysisBuffer.append(" has requested frequent repeating alarms"); + } + } + } + } + } + + @Override + public JSONObject getAnalysis() { + JSONObject usageAnalysis = new JSONObject(); + try { + usageAnalysis.put(PROCESS_USAGE_ANALYSIS, mAnalysisBuffer.toString()); + } catch (JSONException e) { + // do nothing + } + return usageAnalysis; + } +} diff --git a/src/com/android/loganalysis/rule/RuleEngine.java b/src/com/android/loganalysis/rule/RuleEngine.java new file mode 100644 index 0000000..add61e6 --- /dev/null +++ b/src/com/android/loganalysis/rule/RuleEngine.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 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.rule; + +import com.android.loganalysis.item.BugreportItem; + +import org.json.JSONArray; + +import java.util.Collection; +import java.util.LinkedList; + + +/** + * Applies rules to the parsed bugreport + */ +public class RuleEngine { + + public enum RuleType{ + ALL, POWER; + } + + BugreportItem mBugreportItem; + private Collection<IRule> mRulesList; + + public RuleEngine(BugreportItem bugreportItem) { + mBugreportItem = bugreportItem; + mRulesList = new LinkedList<IRule>(); + } + + public void registerRules(RuleType ruleType) { + if (ruleType == RuleType.ALL) { + // add all rules + addPowerRules(); + } else if (ruleType == RuleType.POWER) { + addPowerRules(); + } + } + + public void executeRules() { + for (IRule rule : mRulesList) { + rule.applyRule(); + } + } + + public JSONArray getAnalysis() { + JSONArray result = new JSONArray(); + for (IRule rule : mRulesList) { + result.put(rule.getAnalysis()); + } + return result; + } + + private void addPowerRules() { + mRulesList.add(new WakelockRule(mBugreportItem)); + mRulesList.add(new ProcessUsageRule(mBugreportItem)); + } +} diff --git a/src/com/android/loganalysis/rule/WakelockRule.java b/src/com/android/loganalysis/rule/WakelockRule.java new file mode 100644 index 0000000..b2db2cd --- /dev/null +++ b/src/com/android/loganalysis/rule/WakelockRule.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 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.rule; + +import com.android.loganalysis.item.BugreportItem; +import com.android.loganalysis.item.WakelockItem; +import com.android.loganalysis.item.WakelockItem.WakelockInfoItem; +import com.android.loganalysis.util.NumberFormattingUtil; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Rules definition for wakelock + */ +public class WakelockRule extends AbstractPowerRule { + + private static final String WAKELOCK_ANALYSIS = "WAKELOCK_ANALYSIS"; + private static final float WAKELOCK_HELD_TIME_THRESHOLD_PERCENTAGE = 0.1f; // 10% + + private String mAnalysis = null; + + public WakelockRule (BugreportItem bugreportItem) { + super(bugreportItem); + } + + @SuppressWarnings("cast") + @Override + public void applyRule() { + WakelockItem wakelockItem = getDetailedAnalysisItem().getWakelockItem(); + if (wakelockItem != null) { + long wakelockThreshold = (long)(getTimeOnBattery() + * WAKELOCK_HELD_TIME_THRESHOLD_PERCENTAGE); + + for (WakelockInfoItem wakelocks : wakelockItem.getWakeLocks()) { + if (wakelocks.getHeldTime() > wakelockThreshold) { + mAnalysis = String.format("%s %s is held for %s", wakelocks.getName(), + wakelocks.getCategory(), + NumberFormattingUtil.getDuration(wakelocks.getHeldTime())); + } + } + } + } + + @Override + public JSONObject getAnalysis() { + JSONObject wakelockAnalysis = new JSONObject(); + try { + if (mAnalysis != null) { + wakelockAnalysis.put(WAKELOCK_ANALYSIS, mAnalysis); + } + } catch (JSONException e) { + // do nothing + } + return wakelockAnalysis; + } +} diff --git a/src/com/android/loganalysis/util/NumberFormattingUtil.java b/src/com/android/loganalysis/util/NumberFormattingUtil.java new file mode 100644 index 0000000..8652c2e --- /dev/null +++ b/src/com/android/loganalysis/util/NumberFormattingUtil.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2015 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.util; + +import java.util.concurrent.TimeUnit; + + + +/** + * Utility methods for number formatting + */ +public class NumberFormattingUtil { + + private NumberFormattingUtil() { + } + + /** + * Convert days/hours/mins/secs/msecs into milliseconds. + */ + public static long getMs(long days, long hours, long mins, long secs, long msecs) { + return (((24 * days + hours) * 60 + mins) * 60 + secs) * 1000 + msecs; + } + + /** + * Convert hours/mins/secs/msecs into milliseconds. + */ + public static long getMs(long hours, long mins, long secs, long msecs) { + return getMs(0, hours, mins, secs, msecs); + } + + /** + * Parses a string into a long, or returns 0 if the string is null. + * + * @param s a {@link String} containing the long representation to be parsed + * @return the long represented by the argument in decimal, or 0 if the string is {@code null}. + * @throws NumberFormatException if the string is not {@code null} or does not contain a + * parsable long. + */ + public static long parseLongOrZero(String s) throws NumberFormatException { + if (s == null) { + return 0; + } + return Long.parseLong(s); + } + + /** + * Parses a string into a int, or returns 0 if the string is null. + * + * @param s a {@link String} containing the int representation to be parsed + * @return the int 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. + */ + public static int parseIntOrZero(String s) throws NumberFormatException { + if (s == null) { + return 0; + } + return Integer.parseInt(s); + } + + /** + * Converts milliseconds to days/hours/mins/secs + * + * @param ms elapsed time in ms + * @return the duration in days/hours/mins/secs + */ + public static String getDuration(long ms) { + if (ms <= 0) { + return "Not a valid time"; + } + final long days = TimeUnit.MILLISECONDS.toDays(ms); + final long hrs = TimeUnit.MILLISECONDS.toHours(ms - TimeUnit.DAYS.toMillis(days)); + final long mins = TimeUnit.MILLISECONDS.toMinutes(ms - TimeUnit.DAYS.toMillis(days) + - TimeUnit.HOURS.toMillis(hrs)); + final long secs = TimeUnit.MILLISECONDS.toSeconds(ms - TimeUnit.DAYS.toMillis(days) + - TimeUnit.HOURS.toMillis(hrs) - TimeUnit.MINUTES.toMillis(mins)); + + return String.format("%dd %dh %dm %ds", days, hrs, mins, secs); + } +} + diff --git a/tests/src/com/android/loganalysis/UnitTests.java b/tests/src/com/android/loganalysis/UnitTests.java index eba92aa..c896d90 100644 --- a/tests/src/com/android/loganalysis/UnitTests.java +++ b/tests/src/com/android/loganalysis/UnitTests.java @@ -16,29 +16,32 @@ package com.android.loganalysis; -import com.android.loganalysis.item.DumpsysBatteryInfoItemTest; +import com.android.loganalysis.item.BatteryDischargeItemTest; import com.android.loganalysis.item.GenericItemTest; +import com.android.loganalysis.item.InterruptItemTest; import com.android.loganalysis.item.MemInfoItemTest; import com.android.loganalysis.item.MonkeyLogItemTest; import com.android.loganalysis.item.ProcrankItemTest; import com.android.loganalysis.item.SmartMonkeyLogItemTest; import com.android.loganalysis.item.SystemPropsItemTest; import com.android.loganalysis.item.TopItemTest; +import com.android.loganalysis.item.WakelockItemTest; import com.android.loganalysis.parser.AbstractSectionParserTest; import com.android.loganalysis.parser.AnrParserTest; import com.android.loganalysis.parser.BugreportParserTest; -import com.android.loganalysis.parser.DumpsysBatteryInfoParserTest; -import com.android.loganalysis.parser.DumpsysParserTest; +import com.android.loganalysis.parser.InterruptParserTest; import com.android.loganalysis.parser.JavaCrashParserTest; import com.android.loganalysis.parser.KernelLogParserTest; import com.android.loganalysis.parser.LogcatParserTest; import com.android.loganalysis.parser.MemInfoParserTest; import com.android.loganalysis.parser.MonkeyLogParserTest; import com.android.loganalysis.parser.NativeCrashParserTest; +import com.android.loganalysis.parser.ProcessUsageParserTest; import com.android.loganalysis.parser.ProcrankParserTest; import com.android.loganalysis.parser.SystemPropsParserTest; import com.android.loganalysis.parser.TopParserTest; import com.android.loganalysis.parser.TracesParserTest; +import com.android.loganalysis.parser.WakelockParserTest; import com.android.loganalysis.util.ArrayUtilTest; import com.android.loganalysis.util.LogPatternUtilTest; import com.android.loganalysis.util.LogTailUtilTest; @@ -61,31 +64,34 @@ public class UnitTests extends TestSuite { super(); // item - addTestSuite(DumpsysBatteryInfoItemTest.class); + addTestSuite(BatteryDischargeItemTest.class); addTestSuite(GenericItemTest.class); + addTestSuite(InterruptItemTest.class); addTestSuite(MemInfoItemTest.class); addTestSuite(MonkeyLogItemTest.class); addTestSuite(ProcrankItemTest.class); addTestSuite(SmartMonkeyLogItemTest.class); addTestSuite(SystemPropsItemTest.class); addTestSuite(TopItemTest.class); + addTestSuite(WakelockItemTest.class); // parser addTestSuite(AbstractSectionParserTest.class); addTestSuite(AnrParserTest.class); addTestSuite(BugreportParserTest.class); - addTestSuite(DumpsysParserTest.class); - addTestSuite(DumpsysBatteryInfoParserTest.class); + addTestSuite(InterruptParserTest.class); addTestSuite(JavaCrashParserTest.class); addTestSuite(KernelLogParserTest.class); addTestSuite(LogcatParserTest.class); addTestSuite(MemInfoParserTest.class); addTestSuite(MonkeyLogParserTest.class); addTestSuite(NativeCrashParserTest.class); + addTestSuite(ProcessUsageParserTest.class); addTestSuite(ProcrankParserTest.class); addTestSuite(SystemPropsParserTest.class); addTestSuite(TopParserTest.class); addTestSuite(TracesParserTest.class); + addTestSuite(WakelockParserTest.class); // util addTestSuite(ArrayUtilTest.class); diff --git a/tests/src/com/android/loganalysis/item/BatteryDischargeItemTest.java b/tests/src/com/android/loganalysis/item/BatteryDischargeItemTest.java new file mode 100644 index 0000000..5e27eb2 --- /dev/null +++ b/tests/src/com/android/loganalysis/item/BatteryDischargeItemTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.item; + +import com.android.loganalysis.item.BatteryDischargeItem.BatteryDischargeInfoItem; + +import junit.framework.TestCase; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Calendar; + +/** + * Unit test for {@link BatteryDischargeItem}. + */ +public class BatteryDischargeItemTest extends TestCase { + + /** + * Test that {@link BatteryDischargeItem#toJson()} returns correctly. + */ + public void testToJson() throws JSONException { + BatteryDischargeItem item = new BatteryDischargeItem(); + item.addBatteryDischargeInfo(Calendar.getInstance(),25, 95); + + // Convert to JSON string and back again + JSONObject output = new JSONObject(item.toJson().toString()); + + assertTrue(output.has(BatteryDischargeItem.BATTERY_DISCHARGE)); + assertTrue(output.get(BatteryDischargeItem.BATTERY_DISCHARGE) instanceof JSONArray); + + JSONArray dischargeInfo = output.getJSONArray(BatteryDischargeItem.BATTERY_DISCHARGE); + + assertEquals(1, dischargeInfo.length()); + assertTrue(dischargeInfo.getJSONObject(0).has(BatteryDischargeInfoItem.BATTERY_LEVEL)); + assertTrue(dischargeInfo.getJSONObject(0).has( + BatteryDischargeInfoItem.DISCHARGE_ELAPSED_TIME)); + assertTrue(dischargeInfo.getJSONObject(0).has( + BatteryDischargeInfoItem.CLOCK_TIME_OF_DISCHARGE)); + + } +} diff --git a/tests/src/com/android/loganalysis/item/DumpsysBatteryInfoItemTest.java b/tests/src/com/android/loganalysis/item/DumpsysBatteryInfoItemTest.java deleted file mode 100644 index bb48da7..0000000 --- a/tests/src/com/android/loganalysis/item/DumpsysBatteryInfoItemTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.loganalysis.item; - -import com.android.loganalysis.item.DumpsysBatteryInfoItem.WakeLockCategory; - -import junit.framework.TestCase; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Unit test for {@link DumpsysBatteryInfoItem}. - */ -public class DumpsysBatteryInfoItemTest extends TestCase { - private static final String WAKELOCKS = "WAKELOCKS"; - private static final String LAST_CHARGE_KERNEL_WAKELOCKS = "LAST_CHARGE_KERNEL_WAKELOCKS"; - private static final String LAST_UNPLUGGED_WAKELOCKS = "LAST_UNPLUGGED_WAKELOCKS"; - private static final String LAST_UNPLUGGED_KERNEL_WAKELOCKS = "LAST_UNPLUGGED_KERNEL_WAKELOCKS"; - /** - * Test that {@link DumpsysBatteryInfoItem#toJson()} returns correctly. - */ - public void testToJson() throws JSONException { - DumpsysBatteryInfoItem item = new DumpsysBatteryInfoItem(); - item.addWakeLock("a", 0, 1, WakeLockCategory.LAST_UNPLUGGED_KERNEL_WAKELOCK); - item.addWakeLock("b", 2, 3, WakeLockCategory.LAST_UNPLUGGED_KERNEL_WAKELOCK); - item.addWakeLock("c", 4, 5, 6, WakeLockCategory.LAST_UNPLUGGED_WAKELOCK); - item.addWakeLock("d", 7, 8, 9, WakeLockCategory.LAST_UNPLUGGED_WAKELOCK); - item.addWakeLock("e", 10, 11, 12, WakeLockCategory.LAST_UNPLUGGED_WAKELOCK); - item.addWakeLock("w", 0, 1, WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK); - item.addWakeLock("v", 2, 3, WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK); - item.addWakeLock("x", 4, 5, 6, WakeLockCategory.LAST_CHARGE_WAKELOCK); - item.addWakeLock("y", 7, 8, 9, WakeLockCategory.LAST_CHARGE_WAKELOCK); - item.addWakeLock("z", 10, 11, 12, WakeLockCategory.LAST_CHARGE_WAKELOCK); - - // Convert to JSON string and back again - JSONObject output = new JSONObject(item.toJson().toString()); - - assertTrue(output.has(WAKELOCKS)); - assertTrue(output.get(WAKELOCKS) instanceof JSONArray); - - JSONArray wakeLocks = output.getJSONArray(WAKELOCKS); - assertEquals(10, wakeLocks.length()); - assertTrue(wakeLocks.get(0) instanceof JSONObject); - - JSONObject wakeLock = wakeLocks.getJSONObject(0); - assertEquals("a", wakeLock.get(DumpsysBatteryInfoItem.WakeLock.NAME)); - assertFalse(wakeLock.has(DumpsysBatteryInfoItem.WakeLock.NUMBER)); - assertEquals(0, wakeLock.get(DumpsysBatteryInfoItem.WakeLock.HELD_TIME)); - assertEquals(1, wakeLock.get(DumpsysBatteryInfoItem.WakeLock.LOCKED_COUNT)); - assertEquals(WakeLockCategory.LAST_UNPLUGGED_KERNEL_WAKELOCK.toString(), - wakeLock.get(DumpsysBatteryInfoItem.WakeLock.CATEGORY)); - } -} diff --git a/tests/src/com/android/loganalysis/item/InterruptItemTest.java b/tests/src/com/android/loganalysis/item/InterruptItemTest.java new file mode 100644 index 0000000..9e9df61 --- /dev/null +++ b/tests/src/com/android/loganalysis/item/InterruptItemTest.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.item; + + +import com.android.loganalysis.item.InterruptItem.InterruptInfoItem; + +import junit.framework.TestCase; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Unit test for {@link InterruptItem}. + */ +public class InterruptItemTest extends TestCase { + + /** + * Test that {@link InterruptItem#toJson()} returns correctly. + */ + public void testToJson() throws JSONException { + InterruptItem item = new InterruptItem(); + item.addInterrupt("smd-modem",25, InterruptItem.InterruptCategory.ALARM_INTERRUPT); + + // Convert to JSON string and back again + JSONObject output = new JSONObject(item.toJson().toString()); + + assertTrue(output.has(InterruptItem.INTERRUPTS)); + assertTrue(output.get(InterruptItem.INTERRUPTS) instanceof JSONArray); + + JSONArray interruptsInfo = output.getJSONArray(InterruptItem.INTERRUPTS); + + assertEquals(1, interruptsInfo.length()); + assertTrue(interruptsInfo.getJSONObject(0).has(InterruptInfoItem.NAME)); + assertTrue(interruptsInfo.getJSONObject(0).has(InterruptInfoItem.CATEGORY)); + assertTrue(interruptsInfo.getJSONObject(0).has(InterruptInfoItem.INTERRUPT_COUNT)); + + } +} diff --git a/tests/src/com/android/loganalysis/item/WakelockItemTest.java b/tests/src/com/android/loganalysis/item/WakelockItemTest.java new file mode 100644 index 0000000..f570a7b --- /dev/null +++ b/tests/src/com/android/loganalysis/item/WakelockItemTest.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.item; + +import com.android.loganalysis.item.WakelockItem.WakelockInfoItem; + +import junit.framework.TestCase; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Unit test for {@link WakelockItem}. + */ +public class WakelockItemTest extends TestCase { + + /** + * Test that {@link WakelockItem#toJson()} returns correctly. + */ + public void testToJson() throws JSONException { + WakelockItem item = new WakelockItem(); + item.addWakeLock("screen","u100", 150000, 25, + WakelockItem.WakeLockCategory.PARTIAL_WAKELOCK); + item.addWakeLock("wlan_rx", 150000, 25, + WakelockItem.WakeLockCategory.KERNEL_WAKELOCK); + + // Convert to JSON string and back again + JSONObject output = new JSONObject(item.toJson().toString()); + + assertTrue(output.has(WakelockItem.WAKELOCKS)); + assertTrue(output.get(WakelockItem.WAKELOCKS) instanceof JSONArray); + + JSONArray wakelockInfo = output.getJSONArray(WakelockItem.WAKELOCKS); + + assertEquals(2, wakelockInfo.length()); + assertTrue(wakelockInfo.getJSONObject(0).has(WakelockInfoItem.NAME)); + assertTrue(wakelockInfo.getJSONObject(0).has(WakelockInfoItem.PROCESS_UID)); + assertTrue(wakelockInfo.getJSONObject(0).has(WakelockInfoItem.HELD_TIME)); + assertTrue(wakelockInfo.getJSONObject(0).has(WakelockInfoItem.LOCKED_COUNT)); + assertTrue(wakelockInfo.getJSONObject(0).has(WakelockInfoItem.CATEGORY)); + + assertTrue(wakelockInfo.getJSONObject(1).has(WakelockInfoItem.NAME)); + assertFalse(wakelockInfo.getJSONObject(1).has(WakelockInfoItem.PROCESS_UID)); + assertTrue(wakelockInfo.getJSONObject(1).has(WakelockInfoItem.HELD_TIME)); + assertTrue(wakelockInfo.getJSONObject(1).has(WakelockInfoItem.LOCKED_COUNT)); + assertTrue(wakelockInfo.getJSONObject(1).has(WakelockInfoItem.CATEGORY)); + + } +} diff --git a/tests/src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParserTest.java b/tests/src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParserTest.java new file mode 100644 index 0000000..b176fa2 --- /dev/null +++ b/tests/src/com/android/loganalysis/parser/BatteryStatsDetailedInfoParserTest.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.BatteryStatsDetailedInfoItem; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.List; + +/** + * Unit tests for {@link BatteryStatsDetailedInfoParser} + */ +public class BatteryStatsDetailedInfoParserTest extends TestCase { + + /** + * Test that normal input is parsed. + */ + public void testBatteryStatsDetailedInfoParser() { + List<String> inputBlock = Arrays.asList( + " Time on battery: 2h 21m 5s 622ms (12.0%) realtime, 7m 54s 146ms (0.7%) uptime", + " Time on battery screen off: 2h 5m 55s 3ms (1%) realtime, 7m 4s 5ms (7%) uptime", + " Total run time: 19h 38m 43s 650ms realtime, 17h 25m 32s 175ms uptime", + " All kernel wake locks:", + " Kernel Wake lock PowerManagerService.WakeLocks: 1h 3m 50s 5ms (8 times) realtime", + " Kernel Wake lock event0-2656 : 3m 49s 268ms (2399 times) realtime", + " Kernel Wake lock wlan_wd_wake: 3m 34s 639ms (1751 times) realtime", + " Kernel Wake lock wlan_rx_wake: 3m 19s 887ms (225 times) realtime", + " Kernel Wake lock wlan_tx_wake: 2m 19s 887ms (225 times) realtime", + " Kernel Wake lock tx_wake: 1m 19s 887ms (225 times) realtime", + " ", + " All partial wake locks:", + " Wake lock u0a7 NlpWakeLock: 8m 13s 203ms (1479 times) realtime", + " Wake lock u0a7 NlpCollectorWakeLock: 6m 29s 18ms (238 times) realtime", + " Wake lock u0a7 GCM_CONN_ALARM: 6m 8s 587ms (239 times) realtime", + " Wake lock 1000 *alarm*: 5m 11s 316ms (1469 times) realtime", + " Wake lock u10 xxx: 4m 11s 316ms (1469 times) realtime", + " Wake lock u30 cst: 2m 11s 316ms (1469 times) realtime", + " ", + " All wakeup reasons:", + " Wakeup reason 200:qcom,smd-rpm:222:fc4: 11m 49s 332ms (0 times) realtime", + " Wakeup reason 200:qcom,smd-rpm: 48s 45ms (0 times) realtime", + " Wakeup reason 2:qcom,smd-rpm:2:f0.qm,mm:22:fc4mi: 3s 417ms (0 times) realtime", + " Wakeup reason 188:qcom,smd-adsp:200:qcom,smd-rpm: 1s 656ms (0 times) realtime", + " Wakeup reason 58:qcom,smsm-modem:2:qcom,smd-rpm: 6m 16s 1ms (5 times) realtime", + " Wakeup reason 57:qcom,smd-modem:200:qcom,smd-rpm: 40s 995ms (0 times) realtime", + " Wakeup reason unknown: 8s 455ms (0 times) realtime", + " Wakeup reason 9:bcmsdh_sdmmc:2:qcomd-rpm:240:mso: 8m 5s 9ms (0 times) realtime", + " ", + " 0:", + " User activity: 2 other", + " Wake lock SCREEN_FROZEN realtime", + " Sensor 0: 9s 908ms realtime (1 times)", + " Sensor 1: 9s 997ms realtime (1 times)", + " Foreground for: 2h 21m 5s 622ms", + " Apk android:", + " 24 wakeup alarms", + " u0a9:", + " Mobile network: 8.1KB received, 1.6KB sent (packets 291 received, 342 sent)", + " Mobile radio active: 3m 43s 890ms (34.2%) 39x @ 354 mspp", + " Sensor 2: 12m 13s 15ms realtime (5 times)", + " Sensor 32: (not used)", + " Sensor 35: (not used)"); + + BatteryStatsDetailedInfoItem stats = new BatteryStatsDetailedInfoParser().parse(inputBlock); + + assertEquals(8465622, stats.getTimeOnBattery()); + assertEquals(910619, stats.getScreenOnTime()); + assertNotNull(stats.getWakelockItem()); + assertNotNull(stats.getInterruptItem()); + assertNotNull(stats.getProcessUsageItem()); + } + + /** + * Test with missing wakelock section + */ + public void testMissingWakelockSection() { + List<String> inputBlock = Arrays.asList( + " Time on battery: 2h 21m 5s 622ms (12.0%) realtime, 7m 54s 146ms (0.7%) uptime", + " Time on battery screen off: 2h 5m 55s 3ms (1%) realtime, 7m 4s 5ms (7%) uptime", + " Total run time: 19h 38m 43s 650ms realtime, 17h 25m 32s 175ms uptime", + " All wakeup reasons:", + " Wakeup reason 200:qcom,smd-rpm:222:fc4: 11m 49s 332ms (0 times) realtime", + " Wakeup reason 200:qcom,smd-rpm: 48s 45ms (0 times) realtime", + " Wakeup reason 2:qcom,smd-rpm:2:f0.qm,mm:22:fc4mi: 3s 417ms (0 times) realtime", + " Wakeup reason 188:qcom,smd-adsp:200:qcom,smd-rpm: 1s 656ms (0 times) realtime", + " Wakeup reason 58:qcom,smsm-modem:2:qcom,smd-rpm: 6m 16s 1ms (5 times) realtime", + " Wakeup reason 57:qcom,smd-modem:200:qcom,smd-rpm: 40s 995ms (0 times) realtime", + " Wakeup reason unknown: 8s 455ms (0 times) realtime", + " Wakeup reason 9:bcmsdh_sdmmc:2:qcomd-rpm:240:mso: 8m 5s 9ms (0 times) realtime", + " ", + " 0:", + " User activity: 2 other", + " Wake lock SCREEN_FROZEN realtime", + " Sensor 0: 9s 908ms realtime (1 times)", + " Sensor 1: 9s 997ms realtime (1 times)", + " Foreground for: 2h 21m 5s 622ms", + " Apk android:", + " 24 wakeup alarms", + " u0a9:", + " Mobile network: 8.1KB received, 1.6KB sent (packets 291 received, 342 sent)", + " Mobile radio active: 3m 43s 890ms (34.2%) 39x @ 354 mspp", + " Sensor 2: 12m 13s 15ms realtime (5 times)", + " Sensor 32: (not used)", + " Sensor 35: (not used)"); + BatteryStatsDetailedInfoItem stats = new BatteryStatsDetailedInfoParser().parse(inputBlock); + + assertEquals(8465622, stats.getTimeOnBattery()); + assertEquals(910619, stats.getScreenOnTime()); + + assertNull(stats.getWakelockItem()); + + assertNotNull(stats.getInterruptItem()); + assertNotNull(stats.getProcessUsageItem()); + } +} + diff --git a/tests/src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParserTest.java b/tests/src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParserTest.java new file mode 100644 index 0000000..ad0c999 --- /dev/null +++ b/tests/src/com/android/loganalysis/parser/BatteryStatsSummaryInfoParserTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.BatteryStatsSummaryInfoItem; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.List; + +/** + * Unit tests for {@link BatteryStatsSummaryInfoParser} + */ +public class BatteryStatsSummaryInfoParserTest extends TestCase { + + /** + * Test that normal input is parsed. + */ + public void testBatteryStatsSummaryInfoParser() { + List<String> inputBlock = Arrays.asList( + "Battery History (37% used, 95KB used of 256KB, 166 strings using 15KB):", + " 0 (9) RESET:TIME: 2014-12-09-11-33-29", + " +1s067ms (1) 100 c0500020 -wifi_full_lock -wifi_scan", + " +3s297ms (2) 100 80400020 -wake_lock -screen", + " +30m02s075ms (1) 100 c0500020 wifi_signal_strength=4 wifi_suppl=completed", + " +30m03s012ms (2) 099 c0500020 temp=306 volt=4217", + " +33m48s967ms (1) 099 f8400020 +wifi_scan", + " +33m49s335ms (2) 098 f0400020 temp=324 -wifi_scan", + " +1h07m27s735ms (1) 098 80400020 -wake_lock", + " +1h07m27s950ms (2) 097 c0400020", + " +1h07m29s000ms (2) 097 c0400020 -sync=u0a41:\"gmail-ls/com.google/a@g", + " +1h25m34s877ms (2) 097 00400020 -running wake_reason=0:200:qcom,smd-rpm", + " +1h25m41s948ms (2) 096 80400020 wifi_suppl=associated", + " +2h13m40s055ms (1) 096 00400018 -running", + " +2h13m40s570ms (2) 095 c0400008 temp=304 volt=4167", + " +2h56m50s792ms (1) 095 80400020 -wake_lock", + " +2h56m50s967ms (2) 094 00400020 temp=317 -running", + " +3h38m57s986ms (2) 094 80400020 +running wake_reason=0:289:bcmsdh_sdmmc", + " +3h38m58s241ms (2) 093 00400020 temp=327 -running", + " +3h56m33s329ms (1) 093 00400020 -running -wake_lock", + " +3h56m43s245ms (2) 092 00400020 -running", + " +4h13m00s551ms (1) 092 00400020 -running -wake_lock", + " +4h13m24s250ms (2) 091 00400020 -running", + " +4h34m52s233ms (2) 091 80400020 +running wake_reason=0:289:bcmsdh_sdmmc", + " +4h34m52s485ms (3) 090 00400020 -running wake_reason=0:200:qcom,smd-rpm", + " +4h57m20s644ms (1) 090 00400020 -running", + " +4h57m38s484ms (2) 089 00400020 -running", + " +5h20m58s226ms (1) 089 80400020 +running wifi_suppl=associated", + " +5h21m03s909ms (1) 088 80400020 -wake_lock -wifi_full_lock", + " +5h40m38s169ms (2) 088 c0500020 +top=u0a19:com.google.android.googlequick", + " +5h40m38s479ms (2) 087 c0500020 volt=4036", + " +6h16m45s248ms (2) 087 d0440020 -sync=u0a41:gmail-ls/com.google/avellore@go", + " +6h16m45s589ms (2) 086 d0440020 volt=4096", + " +6h52m43s316ms (1) 086 80400020 -wake_lock", + " +6h53m18s952ms (2) 085 c0400020", + " +7h24m02s415ms (1) 085 80400020 -wake_lock", + " +7h24m02s904ms (3) 084 c0400020 volt=4105 +wake_lock=u0a7:NlpWakeLock", + " +7h29m10s379ms (1) 084 00400020 -running -wake_lock", + " +7h29m11s841ms (2) 083 00400020 temp=317 volt=4047 -running", + " +7h41m08s963ms (1) 083 00400020 -running", + " +7h41m20s494ms (2) 082 00400020 temp=300 -running", + " +7h54m57s288ms (1) 082 52441420 -running", + " +7h55m00s801ms (1) 081 52441420 -running", + " +8h02m18s594ms (1) 081 50440020 -running", + " +8h02m23s493ms (2) 080 50440020 temp=313 -running"); + + BatteryStatsSummaryInfoItem summary = new BatteryStatsSummaryInfoParser().parse(inputBlock); + + assertEquals("The battery dropped a level 24 mins on average", + summary.getBatteryDischargeRate()); + + assertEquals("The peak discharge time was during Tue Dec 09 16:31:07 PST 2014 to " + + "Tue Dec 09 19:35:52 PST 2014 where battery dropped from 89 to 80", + summary.getPeakDischargeTime()); + } + + public void testNoBatteryDischarge() { + List<String> inputBlock = Arrays.asList( + "Battery History (37% used, 95KB used of 256KB, 166 strings using 15KB):", + " 0 (9) RESET:TIME: 2014-12-09-11-33-29"); + BatteryStatsSummaryInfoItem summary = new BatteryStatsSummaryInfoParser().parse(inputBlock); + + assertEquals("The battery did not discharge", summary.getBatteryDischargeRate()); + assertEquals("The battery did not discharge", summary.getPeakDischargeTime()); + } +} + diff --git a/tests/src/com/android/loganalysis/parser/BugreportParserTest.java b/tests/src/com/android/loganalysis/parser/BugreportParserTest.java index a17493e..ea7537f 100644 --- a/tests/src/com/android/loganalysis/parser/BugreportParserTest.java +++ b/tests/src/com/android/loganalysis/parser/BugreportParserTest.java @@ -118,14 +118,24 @@ public class BugreportParserTest extends TestCase { "------ SECTION ------", "", "------ DUMPSYS (dumpsys) ------", - "DUMP OF SERVICE batteryinfo:", - "Statistics since last unplugged:", - " Kernel Wake lock \"PowerManagerService.WakeLocks\": 5m 10s 61ms (2 times) realtime", - " Kernel Wake lock \"pm8921_eoc\": 9s 660ms (0 times) realtime", + "DUMP OF SERVICE batterystats:", + "Battery History (0% used, 1636 used of 256KB, 15 strings using 794):", + " 0 (15) RESET:TIME: 1970-01-10-06-23-28", + " +45s702ms (2) 001 80080000 volt=4187", + " +1m15s525ms (2) 001 80080000 temp=299 volt=4155", + "Statistics since last charged:", + " Time on battery: 1h 5m 2s 4ms (9%) realtime, 1h 5m 2s 4ms (9%) uptime", + " Time on battery screen off: 1h 4m 5s 8ms (9%) realtime, 1h 4m 5s 8ms (9%) uptime", + " All kernel wake locks:", + " Kernel Wake lock PowerManagerService.WakeLocks: 5m 10s 6ms (2 times) realtime", + " Kernel Wake lock msm_serial_hs_rx: 2m 13s 612ms (258 times) realtime", "", " All partial wake locks:", - " Wake lock #0 partialWakelock: 5m 9s 260ms (1 times) realtime", - " Wake lock #1000 AlarmManager: 422ms (7 times) realtime", + " Wake lock 1001 ProxyController: 1h 4m 47s 565ms (4 times) realtime", + " Wake lock 1013 AudioMix: 1s 979ms (3 times) realtime", + " All wakeup reasons:", + " Wakeup reason 2:bcmsdh_sdmmc:2:qcom,smd:2:msmgio: 1m 5s 4ms (2 times) realtime", + " Wakeup reason 2:qcom,smd-rpm:2:fc4c.qcom,spmi: 7m 1s 914ms (7 times) realtime", ""); BugreportItem bugreport = new BugreportParser().parse(lines); @@ -158,7 +168,7 @@ public class BugreportParserTest extends TestCase { assertEquals(4, bugreport.getSystemProps().size()); assertNotNull(bugreport.getDumpsys()); - assertNotNull(bugreport.getDumpsys().getBatteryInfo()); + assertNotNull(bugreport.getDumpsys().getBatteryStats()); } /** @@ -440,7 +450,7 @@ public class BugreportParserTest extends TestCase { bugreport = new BugreportParser().parse(lines); assertNotNull(bugreport); - assertNull(bugreport.getDumpsys()); + assertNotNull(bugreport.getDumpsys()); assertNull(bugreport.getKernelLog()); assertNull(bugreport.getLastKmsg()); assertNull(bugreport.getMemInfo()); diff --git a/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java b/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java deleted file mode 100644 index 66c7796..0000000 --- a/tests/src/com/android/loganalysis/parser/DumpsysBatteryInfoParserTest.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.loganalysis.parser; - -import com.android.loganalysis.item.DumpsysBatteryInfoItem; -import com.android.loganalysis.item.DumpsysBatteryInfoItem.WakeLock; -import com.android.loganalysis.item.DumpsysBatteryInfoItem.WakeLockCategory; - -import junit.framework.TestCase; - -import java.util.Arrays; -import java.util.List; - -/** - * Unit tests for {@link DumpsysBatteryInfoParser} - */ -public class DumpsysBatteryInfoParserTest extends TestCase { - - /** - * Test that complete battery info dumpsys is parsed. - */ - public void testParse() { - List<String> inputBlock = Arrays.asList( - "Battery History:", - " -15m07s754ms START", - " -15m05s119ms 088 20080000 status=charging health=good plug=usb temp=269 volt=4358 +plugged +sensor", - "", - "Per-PID Stats:", - " PID 4242 wake time: +5m10s24ms", - " PID 543 wake time: +3s585ms", - "", - "Statistics since last charge:", - " System starts: 2, currently on battery: false", - " Time on battery: 8m 20s 142ms (55.1%) realtime, 5m 17s 5ms (34.9%) uptime", - " Kernel Wake lock \"PowerManagerService.WakeLocks\": 5m 10s 61ms (2 times) realtime", - " Kernel Wake lock \"pm8921_eoc\": 9s 660ms (0 times) realtime", - " ", - " All partial wake locks:", - " Wake lock #0 partialWakelock: 5m 9s 260ms (1 times) realtime", - " Wake lock #1000 AlarmManager: 422ms (7 times) realtime", - "", - "Statistics since last unplugged:", - " Time on battery: 8m 20s 142ms (92.6%) realtime, 5m 17s 5ms (58.7%) uptime", - " Total run time: 8m 59s 968ms realtime, 5m 56s 831ms uptime, ", - " Screen on: 0ms (0.0%), Input events: 0, Active phone call: 0ms (0.0%)", - " Screen brightnesses: No activity", - " Kernel Wake lock \"PowerManagerService.WakeLocks\": 5m 10s 61ms (2 times) realtime", - " Kernel Wake lock \"pm8921_eoc\": 9s 660ms (0 times) realtime", - " Kernel Wake lock \"main\": 7s 323ms (0 times) realtime", - " Total received: 0B, Total sent: 0B", - " Total full wakelock time: 0ms , Total partial wakelock time: 5m 10s 60ms ", - " Signal levels: No activity", - " Signal scanning time: 0ms ", - " Radio types: none 8m 20s 142ms (100.0%) 0x", - " Radio data uptime when unplugged: 0 ms", - " Wifi on: 0ms (0.0%), Wifi running: 0ms (0.0%), Bluetooth on: 0ms (0.0%)", - " ", - " Device is currently plugged into power", - " Last discharge cycle start level: 87", - " Last discharge cycle end level: 87", - " Amount discharged while screen on: 0", - " Amount discharged while screen off: 0", - " ", - " All partial wake locks:", - " Wake lock #0 partialWakelock: 5m 9s 260ms (1 times) realtime", - " Wake lock #1000 AlarmManager: 422ms (7 times) realtime", - " Wake lock #1000 show keyguard: 277ms (1 times) realtime", - " Wake lock #1000 ActivityManager-Sleep: 72ms (1 times) realtime", - " Wake lock #10015 AlarmManager: 16ms (1 times) realtime", - "", - " #0:", - " Wake lock partialWakelock: 5m 9s 260ms partial (1 times) realtime", - " Proc /init:", - " CPU: 10ms usr + 0ms krn", - " Proc flush-179:0:", - " CPU: 0ms usr + 10ms krn", - " Proc vold:", - " CPU: 20ms usr + 10ms krn", - " #1000:", - " User activity: 3 other, 1 button", - " Wake lock show keyguard: 277ms partial (1 times) realtime", - " Wake lock AlarmManager: 422ms partial (7 times) realtime"); - - DumpsysBatteryInfoParser parser = new DumpsysBatteryInfoParser(); - DumpsysBatteryInfoItem item = parser.parse(inputBlock); - - assertEquals(2, item.getWakeLocks(WakeLockCategory.LAST_CHARGE_WAKELOCK).size()); - assertEquals(2, item.getWakeLocks(WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).size()); - assertEquals(5, item.getWakeLocks(WakeLockCategory.LAST_UNPLUGGED_WAKELOCK).size()); - assertEquals(3, item.getWakeLocks(WakeLockCategory.LAST_UNPLUGGED_KERNEL_WAKELOCK).size()); - - assertEquals("partialWakelock", - item.getWakeLocks(WakeLockCategory.LAST_CHARGE_WAKELOCK).get(0).getName()); - assertEquals("PowerManagerService.WakeLocks", - item.getWakeLocks(WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).get(0).getName()); - assertEquals("partialWakelock", - item.getWakeLocks(WakeLockCategory.LAST_UNPLUGGED_WAKELOCK).get(0).getName()); - assertEquals("PowerManagerService.WakeLocks", - item.getWakeLocks(WakeLockCategory.LAST_UNPLUGGED_KERNEL_WAKELOCK).get(0).getName()); - } - - /** - * Test that kernel wakelocks are parsed. - */ - public void testParseKernelWakeLock() { - String inputLine = " Kernel Wake lock \"Process\": 1d 2h 3m 4s 5ms (6 times) realtime"; - - DumpsysBatteryInfoParser parser = new DumpsysBatteryInfoParser(); - parser.parseKernelWakeLock(inputLine, WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK); - DumpsysBatteryInfoItem item = parser.getItem(); - - assertEquals(1, item.getWakeLocks(WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).size()); - WakeLock wakeLock = item.getWakeLocks(WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).get(0); - assertEquals("Process", wakeLock.getName()); - assertNull(wakeLock.getNumber()); - assertEquals(DumpsysBatteryInfoParser.getMs(1, 2, 3, 4, 5), wakeLock.getHeldTime()); - assertEquals(6, wakeLock.getLockedCount()); - - inputLine = " Kernel Wake lock \"Process\": 5m 7ms (2 times) realtime"; - - parser = new DumpsysBatteryInfoParser(); - parser.parseKernelWakeLock(inputLine, WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK); - item = parser.getItem(); - - assertEquals(1, item.getWakeLocks(WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).size()); - wakeLock = item.getWakeLocks(WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).get(0); - assertEquals("Process", wakeLock.getName()); - assertNull(wakeLock.getNumber()); - assertEquals(5 * 60 * 1000 + 7, wakeLock.getHeldTime()); - assertEquals(2, wakeLock.getLockedCount()); - } - - /** - * Test that wake locks are parsed. - */ - public void testParseWakeLock() { - String inputLine = " Wake lock #1234 Process: 1d 2h 3m 4s 5ms (6 times) realtime"; - - DumpsysBatteryInfoParser parser = new DumpsysBatteryInfoParser(); - parser.parseWakeLock(inputLine, WakeLockCategory.LAST_CHARGE_WAKELOCK); - DumpsysBatteryInfoItem item = parser.getItem(); - - assertEquals(1, item.getWakeLocks(WakeLockCategory.LAST_CHARGE_WAKELOCK).size()); - WakeLock wakeLock = item.getWakeLocks(WakeLockCategory.LAST_CHARGE_WAKELOCK).get(0); - assertEquals("Process", wakeLock.getName()); - assertEquals((Integer) 1234, wakeLock.getNumber()); - assertEquals(DumpsysBatteryInfoParser.getMs(1, 2, 3, 4, 5), wakeLock.getHeldTime()); - assertEquals(6, wakeLock.getLockedCount()); - - inputLine = " Wake lock #1234 Process:with:colons: 5m 7ms (2 times) realtime"; - - parser = new DumpsysBatteryInfoParser(); - parser.parseWakeLock(inputLine, WakeLockCategory.LAST_CHARGE_WAKELOCK); - item = parser.getItem(); - - assertEquals(1, item.getWakeLocks(WakeLockCategory.LAST_CHARGE_WAKELOCK).size()); - wakeLock = item.getWakeLocks(WakeLockCategory.LAST_CHARGE_WAKELOCK).get(0); - assertEquals("Process:with:colons", wakeLock.getName()); - assertEquals((Integer) 1234, wakeLock.getNumber()); - assertEquals(5 * 60 * 1000 + 7, wakeLock.getHeldTime()); - assertEquals(2, wakeLock.getLockedCount()); - } - - /** - * Test the helper function to covert time to ms. - */ - public void testGetMs() { - assertEquals(1, DumpsysBatteryInfoParser.getMs(0, 0, 0, 0, 1)); - assertEquals(1000, DumpsysBatteryInfoParser.getMs(0, 0, 0, 1, 0)); - assertEquals(60 * 1000, DumpsysBatteryInfoParser.getMs(0, 0, 1, 0, 0)); - assertEquals(60 * 60 * 1000, DumpsysBatteryInfoParser.getMs(0, 1, 0, 0, 0)); - assertEquals(24 * 60 * 60 * 1000, DumpsysBatteryInfoParser.getMs(1, 0, 0, 0, 0)); - } -} - diff --git a/tests/src/com/android/loganalysis/parser/DumpsysBatteryStatsParserTest.java b/tests/src/com/android/loganalysis/parser/DumpsysBatteryStatsParserTest.java new file mode 100644 index 0000000..8799a9b --- /dev/null +++ b/tests/src/com/android/loganalysis/parser/DumpsysBatteryStatsParserTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.DumpsysBatteryStatsItem; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.List; + +/** + * Unit tests for {@link DumpsysBatteryStatsParser} + */ +public class DumpsysBatteryStatsParserTest extends TestCase { + + /** + * Test that normal input is parsed. + */ + public void testBatteryStatsParser() { + List<String> inputBlock = Arrays.asList( + "Battery History (37% used, 95KB used of 256KB, 166 strings using 15KB):", + " 0 (9) RESET:TIME: 2014-12-09-11-33-29", + " +1s067ms (1) 100 c0500020 -wifi_full_lock -wifi_scan", + " +3s297ms (2) 100 80400020 -wake_lock -screen", + " +30m02s075ms (1) 100 c0500020 wifi_signal_strength=4 wifi_suppl=completed", + " +30m03s012ms (2) 099 c0500020 temp=306 volt=4217", + " +33m48s967ms (1) 099 f8400020 +wifi_scan", + " +33m49s335ms (2) 098 f0400020 temp=324 -wifi_scan", + "Statistics since last charge:", + " Time on battery: 2h 21m 5s 622ms (12.0%) realtime, 7m 54s 146ms (0.7%) uptime", + " Time on battery screen off: 2h 5m 55s 3ms (1%) realtime, 7m 4s 5ms (7%) uptime", + " Total run time: 19h 38m 43s 650ms realtime, 17h 25m 32s 175ms uptime", + " All kernel wake locks:", + " Kernel Wake lock PowerManagerService.WakeLocks: 1h 3m 50s 5ms (8 times) realtime", + " Kernel Wake lock event0-2656 : 3m 49s 268ms (2399 times) realtime", + " Kernel Wake lock wlan_wd_wake: 3m 34s 639ms (1751 times) realtime", + " Kernel Wake lock wlan_rx_wake: 3m 19s 887ms (225 times) realtime", + " Kernel Wake lock wlan_tx_wake: 2m 19s 887ms (225 times) realtime", + " Kernel Wake lock tx_wake: 1m 19s 887ms (225 times) realtime", + " ", + " All partial wake locks:", + " Wake lock u0a7 NlpWakeLock: 8m 13s 203ms (1479 times) realtime", + " Wake lock u0a7 NlpCollectorWakeLock: 6m 29s 18ms (238 times) realtime", + " Wake lock u0a7 GCM_CONN_ALARM: 6m 8s 587ms (239 times) realtime", + " Wake lock 1000 *alarm*: 5m 11s 316ms (1469 times) realtime", + " Wake lock u10 xxx: 4m 11s 316ms (1469 times) realtime", + " Wake lock u30 cst: 2m 11s 316ms (1469 times) realtime", + " ", + " All wakeup reasons:", + " Wakeup reason 200:qcom,smd-rpm:222:fc4: 11m 49s 332ms (0 times) realtime", + " Wakeup reason 200:qcom,smd-rpm: 48s 45ms (0 times) realtime", + " Wakeup reason 2:qcom,smd-rpm:2:f0.qm,mm:22:fc4mi: 3s 417ms (0 times) realtime", + " Wakeup reason 188:qcom,smd-adsp:200:qcom,smd-rpm: 1s 656ms (0 times) realtime", + " Wakeup reason 58:qcom,smsm-modem:2:qcom,smd-rpm: 6m 16s 1ms (5 times) realtime", + " Wakeup reason 57:qcom,smd-modem:200:qcom,smd-rpm: 40s 995ms (0 times) realtime", + " Wakeup reason unknown: 8s 455ms (0 times) realtime", + " Wakeup reason 9:bcmsdh_sdmmc:2:qcomd-rpm:240:mso: 8m 5s 9ms (0 times) realtime", + " ", + " 0:", + " User activity: 2 other", + " Wake lock SCREEN_FROZEN realtime", + " Sensor 0: 9s 908ms realtime (1 times)", + " Sensor 1: 9s 997ms realtime (1 times)", + " Foreground for: 2h 21m 5s 622ms", + " Apk android:", + " 24 wakeup alarms", + " u0a9:", + " Mobile network: 8.1KB received, 1.6KB sent (packets 291 received, 342 sent)", + " Mobile radio active: 3m 43s 890ms (34.2%) 39x @ 354 mspp", + " Sensor 2: 12m 13s 15ms realtime (5 times)", + " Sensor 32: (not used)", + " Sensor 35: (not used)"); + + DumpsysBatteryStatsItem batteryStats = new DumpsysBatteryStatsParser().parse(inputBlock); + assertNotNull(batteryStats.getBatteryStatsSummaryItem()); + assertNotNull(batteryStats.getDetailedBatteryStatsItem()); + } +} + diff --git a/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java b/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java index 423ec06..7dff3a1 100644 --- a/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java +++ b/tests/src/com/android/loganalysis/parser/DumpsysParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2015 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. @@ -15,7 +15,6 @@ */ package com.android.loganalysis.parser; -import com.android.loganalysis.item.DumpsysBatteryInfoItem.WakeLockCategory; import com.android.loganalysis.item.DumpsysItem; import junit.framework.TestCase; @@ -29,40 +28,75 @@ import java.util.List; public class DumpsysParserTest extends TestCase { /** - * Test that the dumpsys section of the bugreport is parsed. + * Test that normal input is parsed. */ - public void testParse() { + public void testDumpsysParser() { List<String> inputBlock = Arrays.asList( - "-------------------------------------------------------------------------------", - "DUMP OF SERVICE process1:", - "-------------------------------------------------------------------------------", - "DUMP OF SERVICE batteryinfo:", + "DUMP OF SERVICE batterystats:", + "Battery History (37% used, 95KB used of 256KB, 166 strings using 15KB):", + " 0 (9) RESET:TIME: 2014-12-09-11-33-29", + " +1s067ms (1) 100 c0500020 -wifi_full_lock -wifi_scan", + " +3s297ms (2) 100 80400020 -wake_lock -screen", + " +30m02s075ms (1) 100 c0500020 wifi_signal_strength=4 wifi_suppl=completed", + " +30m03s012ms (2) 099 c0500020 temp=306 volt=4217", + " +33m48s967ms (1) 099 f8400020 +wifi_scan", + " +33m49s335ms (2) 098 f0400020 temp=324 -wifi_scan", "Statistics since last charge:", - " Kernel Wake lock \"PowerManagerService.WakeLocks\": 5m 10s 61ms (2 times) realtime", - " Kernel Wake lock \"pm8921_eoc\": 9s 660ms (0 times) realtime", - "", - " All partial wake locks:", - " Wake lock #0 partialWakelock: 5m 9s 260ms (1 times) realtime", - " Wake lock #1000 AlarmManager: 422ms (7 times) realtime", - "", - "-------------------------------------------------------------------------------", - "DUMP OF SERVICE process2:", - "-------------------------------------------------------------------------------"); + " Time on battery: 2h 21m 5s 622ms (12.0%) realtime, 7m 54s 146ms (0.7%) uptime", + " Time on battery screen off: 2h 5m 55s 3ms (1%) realtime, 7m 4s 5ms (7%) uptime", + " Total run time: 19h 38m 43s 650ms realtime, 17h 25m 32s 175ms uptime", + " All kernel wake locks:", + " Kernel Wake lock PowerManagerService.WakeLocks: 1h 3m 50s 5ms (8 times) realtime", + " Kernel Wake lock event0-2656 : 3m 49s 268ms (2399 times) realtime", + " Kernel Wake lock wlan_wd_wake: 3m 34s 639ms (1751 times) realtime", + " Kernel Wake lock wlan_rx_wake: 3m 19s 887ms (225 times) realtime", + " Kernel Wake lock wlan_tx_wake: 2m 19s 887ms (225 times) realtime", + " Kernel Wake lock tx_wake: 1m 19s 887ms (225 times) realtime", + " ", + " All partial wake locks:", + " Wake lock u0a7 NlpWakeLock: 8m 13s 203ms (1479 times) realtime", + " Wake lock u0a7 NlpCollectorWakeLock: 6m 29s 18ms (238 times) realtime", + " Wake lock u0a7 GCM_CONN_ALARM: 6m 8s 587ms (239 times) realtime", + " Wake lock 1000 *alarm*: 5m 11s 316ms (1469 times) realtime", + " Wake lock u10 xxx: 4m 11s 316ms (1469 times) realtime", + " Wake lock u30 cst: 2m 11s 316ms (1469 times) realtime", + " ", + " All wakeup reasons:", + " Wakeup reason 200:qcom,smd-rpm:222:fc4: 11m 49s 332ms (0 times) realtime", + " Wakeup reason 200:qcom,smd-rpm: 48s 45ms (0 times) realtime", + " Wakeup reason 2:qcom,smd-rpm:2:f0.qm,mm:22:fc4mi: 3s 417ms (0 times) realtime", + " Wakeup reason 188:qcom,smd-adsp:200:qcom,smd-rpm: 1s 656ms (0 times) realtime", + " Wakeup reason 58:qcom,smsm-modem:2:qcom,smd-rpm: 6m 16s 1ms (5 times) realtime", + " Wakeup reason 57:qcom,smd-modem:200:qcom,smd-rpm: 40s 995ms (0 times) realtime", + " Wakeup reason unknown: 8s 455ms (0 times) realtime", + " Wakeup reason 9:bcmsdh_sdmmc:2:qcomd-rpm:240:mso: 8m 5s 9ms (0 times) realtime", + " ", + " 0:", + " User activity: 2 other", + " Wake lock SCREEN_FROZEN realtime", + " Sensor 0: 9s 908ms realtime (1 times)", + " Sensor 1: 9s 997ms realtime (1 times)", + " Foreground for: 2h 21m 5s 622ms", + " Apk android:", + " 24 wakeup alarms", + " u0a9:", + " Mobile network: 8.1KB received, 1.6KB sent (packets 291 received, 342 sent)", + " Mobile radio active: 3m 43s 890ms (34.2%) 39x @ 354 mspp", + " Sensor 2: 12m 13s 15ms realtime (5 times)", + " Sensor 32: (not used)", + " Sensor 35: (not used)", + "DUMP OF SERVICE procstats:", + "COMMITTED STATS FROM 2015-03-20-02-01-02 (checked in):", + " * com.android.systemui / u0a22 / v22:", + " TOTAL: 100% (159MB-160MB-161MB/153MB-153MB-154MB over 13)", + " Persistent: 100% (159MB-160MB-161MB/153MB-153MB-154MB over 13)", + " * com.google.process.gapps / u0a9 / v22:", + " TOTAL: 100% (22MB-24MB-25MB/18MB-19MB-20MB over 13)", + " Imp Fg: 100% (22MB-24MB-25MB/18MB-19MB-20MB over 13)"); - DumpsysItem item = new DumpsysParser().parse(inputBlock); - - assertNotNull(item.getBatteryInfo()); - assertEquals(2, item.getBatteryInfo().getWakeLocks( - WakeLockCategory.LAST_CHARGE_WAKELOCK).size()); - assertEquals(2, item.getBatteryInfo().getWakeLocks( - WakeLockCategory.LAST_CHARGE_KERNEL_WAKELOCK).size()); - } - - /** - * Test that an empty input returns {@code null}. - */ - public void testEmptyInput() { - DumpsysItem item = new DumpsysParser().parse(Arrays.asList("")); - assertNull(item); + DumpsysItem dumpsys = new DumpsysParser().parse(inputBlock); + assertNotNull(dumpsys.getBatteryStats()); + assertNotNull(dumpsys.getProcStats()); } } + diff --git a/tests/src/com/android/loganalysis/parser/DumpsysProcStatsParserTest.java b/tests/src/com/android/loganalysis/parser/DumpsysProcStatsParserTest.java new file mode 100644 index 0000000..27bb7a4 --- /dev/null +++ b/tests/src/com/android/loganalysis/parser/DumpsysProcStatsParserTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.DumpsysProcStatsItem; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.List; + +/** + * Unit tests for {@link DumpsysProcStatsParser} + */ +public class DumpsysProcStatsParserTest extends TestCase { + + /** + * Test that normal input is parsed. + */ + public void testDumpsysProcStatsParser() { + List<String> inputBlock = Arrays.asList( + "COMMITTED STATS FROM 2015-03-20-02-01-02 (checked in):", + " * com.android.systemui / u0a22 / v22:", + " TOTAL: 100% (159MB-160MB-161MB/153MB-153MB-154MB over 13)", + " Persistent: 100% (159MB-160MB-161MB/153MB-153MB-154MB over 13)", + " * com.google.process.gapps / u0a9 / v22:", + " TOTAL: 100% (22MB-24MB-25MB/18MB-19MB-20MB over 13)", + " Imp Fg: 100% (22MB-24MB-25MB/18MB-19MB-20MB over 13)"); + + DumpsysProcStatsItem procstats = new DumpsysProcStatsParser().parse(inputBlock); + assertEquals("com.android.systemui", procstats.get("u0a22")); + assertEquals("com.google.process.gapps", procstats.get("u0a9")); + } +} + diff --git a/tests/src/com/android/loganalysis/parser/InterruptParserTest.java b/tests/src/com/android/loganalysis/parser/InterruptParserTest.java new file mode 100644 index 0000000..d45f3af --- /dev/null +++ b/tests/src/com/android/loganalysis/parser/InterruptParserTest.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.parser; + + + +import com.android.loganalysis.item.InterruptItem; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.List; + +/** + * Unit tests for {@link InterruptParser} + */ +public class InterruptParserTest extends TestCase { + + /** + * Test that normal input is parsed. + */ + public void testInterruptParser() { + List<String> inputBlock = Arrays.asList( + " Wakeup reason 200:qcom,smd-rpm:222:fc4: 11m 49s 332ms (0 times) realtime", + " Wakeup reason 200:qcom,smd-rpm: 48s 45ms (0 times) realtime", + " Wakeup reason 2:qcom,smd-rpm:2:f0.qm,mm:22:fc4mi: 3s 417ms (0 times) realtime", + " Wakeup reason 188:qcom,smd-adsp:200:qcom,smd-rpm: 1s 656ms (0 times) realtime", + " Wakeup reason 58:qcom,smsm-modem:2:qcom,smd-rpm: 6m 16s 1ms (5 times) realtime", + " Wakeup reason 57:qcom,smd-modem:200:qcom,smd-rpm: 40s 995ms (0 times) realtime", + " Wakeup reason unknown: 8s 455ms (0 times) realtime", + " Wakeup reason 9:bcmsdh_sdmmc:2:qcomd-rpm:240:mso: 8m 5s 9ms (0 times) realtime"); + + InterruptItem interrupt = new InterruptParser().parse(inputBlock); + + assertEquals(1, interrupt.getInterrupts( + InterruptItem.InterruptCategory.WIFI_INTERRUPT).size()); + + assertEquals("9:bcmsdh_sdmmc:2:qcomd-rpm:240:mso", interrupt.getInterrupts( + InterruptItem.InterruptCategory.WIFI_INTERRUPT).get(0).getName()); + + assertEquals(2, interrupt.getInterrupts( + InterruptItem.InterruptCategory.MODEM_INTERRUPT).size()); + + assertEquals(5, interrupt.getInterrupts(InterruptItem.InterruptCategory.MODEM_INTERRUPT). + get(0).getInterruptCount()); + + } +} + diff --git a/tests/src/com/android/loganalysis/parser/ProcessUsageParserTest.java b/tests/src/com/android/loganalysis/parser/ProcessUsageParserTest.java new file mode 100644 index 0000000..e87b4a1 --- /dev/null +++ b/tests/src/com/android/loganalysis/parser/ProcessUsageParserTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.ProcessUsageItem; +import com.android.loganalysis.item.ProcessUsageItem.ProcessUsageInfoItem; +import com.android.loganalysis.item.ProcessUsageItem.SensorInfoItem; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * Unit tests for {@link ProcessUsageParser} + */ +public class ProcessUsageParserTest extends TestCase { + + /** + * Test that normal input is parsed. + */ + public void testProcessUsageParser() { + List<String> inputBlock = Arrays.asList( + " 0:", + " Mobile network: 173.70KB received, 102.55KB sent (packets 129)", + " Mobile radio active: 6m 5s 80ms (14.9%) 80x @ 139 mspp", + " 1000:", + " Mobile network: 16.43KB received, 26.26KB sent", + " Mobile radio active: 1m 17s 489ms (3.2%) 61x @ 179 mspp", + " Sensor 44: 27m 18s 207ms realtime (22 times)", + " Sensor 36: 6s 483ms realtime (3 times)", + " Proc servicemanager:", + " CPU: 2s 20ms usr + 4s 60ms krn ; 0ms fg", + " Apk android:", + " 266 wakeup alarms", + " u0a2:", + " Mobile network: 16.43KB received, 26.26KB sent", + " Mobile radio active: 1m 17s 489ms (3.2%) 61x @ 179 mspp", + " Sensor 0: 5s 207ms realtime (2 times)", + " Proc servicemanager:", + " CPU: 2s 20ms usr + 4s 60ms krn ; 0ms fg", + " Apk android:", + " 2 wakeup alarms"); + + ProcessUsageItem processUsage = new ProcessUsageParser().parse(inputBlock); + + assertEquals(2, processUsage.getProcessUsage().size()); + + LinkedList<ProcessUsageInfoItem> processUsageInfo = + (LinkedList<ProcessUsageInfoItem>)processUsage.getProcessUsage(); + + assertEquals("1000", processUsageInfo.get(1).getProcessUID()); + assertEquals(266, processUsageInfo.get(1).getAlarmWakeups()); + + LinkedList<SensorInfoItem> sensor = processUsageInfo.get(1).getSensorUsage(); + assertEquals("44", sensor.get(0).getSensorName()); + assertEquals("36", sensor.get(1).getSensorName()); + } +} + diff --git a/tests/src/com/android/loganalysis/parser/WakelockParserTest.java b/tests/src/com/android/loganalysis/parser/WakelockParserTest.java new file mode 100644 index 0000000..bed831b --- /dev/null +++ b/tests/src/com/android/loganalysis/parser/WakelockParserTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.loganalysis.parser; + +import com.android.loganalysis.item.WakelockItem; +import com.android.loganalysis.item.WakelockItem.WakeLockCategory; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.List; + +/** + * Unit tests for {@link WakelockParser} + */ +public class WakelockParserTest extends TestCase { + + /** + * Test that normal input is parsed. + */ + public void testWakelockParser() { + List<String> inputBlock = Arrays.asList( + " All kernel wake locks:", + " Kernel Wake lock PowerManagerService.WakeLocks: 1h 3m 50s 5ms (8 times) realtime", + " Kernel Wake lock event0-2656 : 3m 49s 268ms (2399 times) realtime", + " Kernel Wake lock wlan_wd_wake: 3m 34s 639ms (1751 times) realtime", + " Kernel Wake lock wlan_rx_wake: 3m 19s 887ms (225 times) realtime", + " Kernel Wake lock wlan_tx_wake: 2m 19s 887ms (225 times) realtime", + " Kernel Wake lock tx_wake: 1m 19s 887ms (225 times) realtime", + " ", + " All partial wake locks:", + " Wake lock u0a7 NlpWakeLock: 8m 13s 203ms (1479 times) realtime", + " Wake lock u0a7 NlpCollectorWakeLock: 6m 29s 18ms (238 times) realtime", + " Wake lock u0a7 GCM_CONN_ALARM: 6m 8s 587ms (239 times) realtime", + " Wake lock 1000 *alarm*: 5m 11s 316ms (1469 times) realtime", + " Wake lock u10 xxx: 4m 11s 316ms (1469 times) realtime", + " Wake lock u30 cst: 2m 11s 316ms (1469 times) realtime"); + + WakelockItem wakelock = new WakelockParser().parse(inputBlock); + + assertEquals(WakelockParser.TOP_WAKELOCK_COUNT, + wakelock.getWakeLocks(WakeLockCategory.KERNEL_WAKELOCK).size()); + assertEquals("event0-2656 ", + wakelock.getWakeLocks(WakeLockCategory.KERNEL_WAKELOCK).get(0).getName()); + assertEquals(229268, wakelock.getWakeLocks(WakeLockCategory.KERNEL_WAKELOCK). + get(0).getHeldTime()); + assertEquals(2399, wakelock.getWakeLocks(WakeLockCategory.KERNEL_WAKELOCK). + get(0).getLockedCount()); + + assertEquals(WakelockParser.TOP_WAKELOCK_COUNT, + wakelock.getWakeLocks(WakeLockCategory.PARTIAL_WAKELOCK).size()); + assertEquals("NlpWakeLock", wakelock.getWakeLocks(WakeLockCategory.PARTIAL_WAKELOCK). + get(0).getName()); + assertEquals("u0a7", wakelock.getWakeLocks(WakeLockCategory.PARTIAL_WAKELOCK). + get(0).getProcessUID()); + assertEquals(493203, wakelock.getWakeLocks(WakeLockCategory.PARTIAL_WAKELOCK). + get(0).getHeldTime()); + assertEquals(1479, wakelock.getWakeLocks(WakeLockCategory.PARTIAL_WAKELOCK). + get(0).getLockedCount()); + } +} + |