1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.loganalysis.parser; 17 18 import com.android.loganalysis.item.KernelLogItem; 19 import com.android.loganalysis.item.LowMemoryKillerItem; 20 import com.android.loganalysis.item.MiscKernelLogItem; 21 import com.android.loganalysis.item.PageAllocationFailureItem; 22 import com.android.loganalysis.item.SELinuxItem; 23 import com.android.loganalysis.util.LogPatternUtil; 24 import com.android.loganalysis.util.LogTailUtil; 25 26 import java.io.BufferedReader; 27 import java.io.IOException; 28 import java.util.List; 29 import java.util.regex.Matcher; 30 import java.util.regex.Pattern; 31 32 /** 33 * A {@link IParser} to parse {@code /proc/last_kmsg}, {@code /proc/kmsg}, and the output from 34 * {@code dmesg}. 35 */ 36 public class KernelLogParser implements IParser { 37 public static final String KERNEL_RESET = "KERNEL_RESET"; 38 public static final String KERNEL_ERROR = "KERNEL_ERROR"; 39 public static final String SELINUX_DENIAL = "SELINUX_DENIAL"; 40 public static final String NORMAL_REBOOT = "NORMAL_REBOOT"; 41 public static final String PAGE_ALLOC_FAILURE = "PAGE_ALLOC_FAILURE"; 42 public static final String LOW_MEMORY_KILLER = "LOW_MEMORY_KILLER"; 43 44 /** 45 * Matches: [ 0.000000] Message<br /> 46 * Matches: <3>[ 0.000000] Message 47 */ 48 private static final Pattern LOG_LINE = Pattern.compile( 49 "^(<\\d+>)?\\[\\s*(\\d+\\.\\d{6})\\] (.*)$"); 50 private static final Pattern SELINUX_DENIAL_PATTERN = Pattern.compile( 51 ".*avc:\\s.*scontext=\\w*:\\w*:([\\w\\s]*):\\w*\\s.*"); 52 // Matches: page allocation failure: order:3, mode:0x10c0d0 53 private static final Pattern PAGE_ALLOC_FAILURE_PATTERN = Pattern.compile( 54 ".*page\\s+allocation\\s+failure:\\s+order:(\\d+).*"); 55 // Matches: Killing '.qcrilmsgtunnel' (3699), adj 100, 56 private static final Pattern LOW_MEMORY_KILLER_PATTERN = Pattern.compile( 57 ".*Killing\\s+'(.*)'\\s+\\((\\d+)\\),.*adj\\s+(\\d+).*"); 58 59 /** 60 * Regular expression representing all known bootreasons which are bad. 61 */ 62 public static final Pattern BAD_BOOTREASONS = Pattern.compile( 63 "(?:kernel_panic.*|rpm_err|hw_reset(?:$|\\n)|wdog_.*|tz_err|adsp_err|modem_err|mba_err|" 64 + "watchdog.*|Watchdog|Panic|srto:.*|oemerr.*)"); 65 66 /** 67 * Regular expression representing all known bootreasons which are good. 68 */ 69 public static final Pattern GOOD_BOOTREASONS = Pattern.compile( 70 "(?:PowerKey|normal|recovery|reboot.*)"); 71 72 private boolean mAddUnknownBootreason = true; 73 74 private KernelLogItem mKernelLog = null; 75 private Double mStartTime = null; 76 private Double mStopTime = null; 77 78 private LogPatternUtil mPatternUtil = new LogPatternUtil(); 79 private LogTailUtil mPreambleUtil = new LogTailUtil(500, 50, 50); 80 private boolean mBootreasonFound = false; 81 KernelLogParser()82 public KernelLogParser() { 83 initPatterns(); 84 } 85 setAddUnknownBootreason(boolean enable)86 public void setAddUnknownBootreason(boolean enable) { 87 mAddUnknownBootreason = enable; 88 } 89 90 /** 91 * Parse a kernel log from a {@link BufferedReader} into an {@link KernelLogItem} object. 92 * 93 * @return The {@link KernelLogItem}. 94 * @see #parse(List) 95 */ parse(BufferedReader input)96 public KernelLogItem parse(BufferedReader input) throws IOException { 97 String line; 98 while ((line = input.readLine()) != null) { 99 parseLine(line); 100 } 101 commit(); 102 103 return mKernelLog; 104 } 105 106 /** 107 * {@inheritDoc} 108 * 109 * @return The {@link KernelLogItem}. 110 */ 111 @Override parse(List<String> lines)112 public KernelLogItem parse(List<String> lines) { 113 mBootreasonFound = false; 114 for (String line : lines) { 115 parseLine(line); 116 } 117 commit(); 118 119 return mKernelLog; 120 } 121 122 /** 123 * Parse a line of input. 124 * 125 * @param line The line to parse 126 */ parseLine(String line)127 private void parseLine(String line) { 128 if ("".equals(line.trim())) { 129 return; 130 } 131 if (mKernelLog == null) { 132 mKernelLog = new KernelLogItem(); 133 } 134 Matcher m = LOG_LINE.matcher(line); 135 if (m.matches()) { 136 Double time = Double.parseDouble(m.group(2)); 137 String msg = m.group(3); 138 139 if (mStartTime == null) { 140 mStartTime = time; 141 } 142 mStopTime = time; 143 144 checkAndAddKernelEvent(msg); 145 146 mPreambleUtil.addLine(null, line); 147 } else { 148 checkAndAddKernelEvent(line); 149 } 150 } 151 152 /** 153 * Checks if a kernel log message matches a pattern and add a kernel event if it does. 154 */ checkAndAddKernelEvent(String message)155 private void checkAndAddKernelEvent(String message) { 156 String category = mPatternUtil.checkMessage(message); 157 if (category == null) { 158 return; 159 } 160 161 if (category.equals(KERNEL_RESET) || category.equals(NORMAL_REBOOT)) { 162 mBootreasonFound = true; 163 } 164 165 if (category.equals(NORMAL_REBOOT)) { 166 return; 167 } 168 169 MiscKernelLogItem kernelLogItem; 170 if (category.equals(SELINUX_DENIAL)) { 171 SELinuxItem selinuxItem = new SELinuxItem(); 172 Matcher m = SELINUX_DENIAL_PATTERN.matcher(message); 173 if (m.matches()) { 174 selinuxItem.setSContext(m.group(1)); 175 } 176 kernelLogItem = selinuxItem; 177 } else if (category.equals(PAGE_ALLOC_FAILURE)) { 178 PageAllocationFailureItem allocItem = new PageAllocationFailureItem(); 179 Matcher m = PAGE_ALLOC_FAILURE_PATTERN.matcher(message); 180 if (m.matches()) { 181 allocItem.setOrder(Integer.parseInt(m.group(1))); 182 } 183 kernelLogItem = allocItem; 184 } else if (category.equals(LOW_MEMORY_KILLER)) { 185 LowMemoryKillerItem lmkItem = new LowMemoryKillerItem(); 186 Matcher m = LOW_MEMORY_KILLER_PATTERN.matcher(message); 187 if (m.matches()) { 188 lmkItem.setProcessName(m.group(1)); 189 lmkItem.setPid(Integer.parseInt(m.group(2))); 190 lmkItem.setAdjustment(Integer.parseInt(m.group(3))); 191 } 192 kernelLogItem = lmkItem; 193 } else { 194 kernelLogItem = new MiscKernelLogItem(); 195 } 196 kernelLogItem.setEventTime(mStopTime); 197 kernelLogItem.setPreamble(mPreambleUtil.getLastTail()); 198 kernelLogItem.setStack(message); 199 kernelLogItem.setCategory(category); 200 mKernelLog.addEvent(kernelLogItem); 201 } 202 203 /** 204 * Signal that the input has finished. 205 */ commit()206 private void commit() { 207 if (mKernelLog == null) { 208 return; 209 } 210 mKernelLog.setStartTime(mStartTime); 211 mKernelLog.setStopTime(mStopTime); 212 213 if (mAddUnknownBootreason && !mBootreasonFound) { 214 MiscKernelLogItem unknownReset = new MiscKernelLogItem(); 215 unknownReset.setEventTime(mStopTime); 216 unknownReset.setPreamble(mPreambleUtil.getLastTail()); 217 unknownReset.setCategory(KERNEL_RESET); 218 unknownReset.setStack("Unknown reason"); 219 mKernelLog.addEvent(unknownReset); 220 } 221 } 222 initPatterns()223 private void initPatterns() { 224 // Kernel resets 225 // TODO: Separate out device specific patterns 226 final String[] kernelResets = { 227 "smem: DIAG.*", 228 "smsm: AMSS FATAL ERROR.*", 229 "kernel BUG at .*", 230 "BUG: failure at .*", 231 "PVR_K:\\(Fatal\\): Debug assertion failed! \\[.*\\]", 232 "Kernel panic.*", 233 "Unable to handle kernel paging request.*", 234 "BP panicked", 235 "WROTE DSP RAMDUMP", 236 "tegra_wdt: last reset due to watchdog timeout.*", 237 "tegra_wdt tegra_wdt.0: last reset is due to watchdog timeout.*", 238 "Last reset was MPU Watchdog Timer reset.*", 239 "\\[MODEM_IF\\].*CRASH.*", 240 "Last boot reason: " + BAD_BOOTREASONS, 241 "Last reset was system watchdog timer reset.*", 242 }; 243 final String[] goodSignatures = { 244 "Restarting system.*", 245 "Power down.*", 246 "Last boot reason: " + GOOD_BOOTREASONS, 247 }; 248 for (String pattern : kernelResets) { 249 mPatternUtil.addPattern(Pattern.compile(pattern), KERNEL_RESET); 250 } 251 for (String pattern : goodSignatures) { 252 mPatternUtil.addPattern(Pattern.compile(pattern), NORMAL_REBOOT); 253 } 254 255 mPatternUtil.addPattern(Pattern.compile("Internal error:.*"), KERNEL_ERROR); 256 257 // SELINUX denials 258 mPatternUtil.addPattern(SELINUX_DENIAL_PATTERN, SELINUX_DENIAL); 259 // Page allocation failures 260 mPatternUtil.addPattern(PAGE_ALLOC_FAILURE_PATTERN, PAGE_ALLOC_FAILURE); 261 // Lowmemorykiller kills 262 mPatternUtil.addPattern(LOW_MEMORY_KILLER_PATTERN, LOW_MEMORY_KILLER); 263 } 264 265 /** 266 * Get the internal {@link LogPatternUtil}. Exposed for unit testing. 267 */ getLogPatternUtil()268 LogPatternUtil getLogPatternUtil() { 269 return mPatternUtil; 270 } 271 } 272