1 /* 2 * Copyright (C) 2011 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.ActivityServiceItem; 19 import com.android.loganalysis.item.AnrItem; 20 import com.android.loganalysis.item.BugreportItem; 21 import com.android.loganalysis.item.BugreportItem.CommandLineItem; 22 import com.android.loganalysis.item.DumpsysItem; 23 import com.android.loganalysis.item.IItem; 24 import com.android.loganalysis.item.KernelLogItem; 25 import com.android.loganalysis.item.LogcatItem; 26 import com.android.loganalysis.item.MemInfoItem; 27 import com.android.loganalysis.item.MiscKernelLogItem; 28 import com.android.loganalysis.item.MiscLogcatItem; 29 import com.android.loganalysis.item.ProcrankItem; 30 import com.android.loganalysis.item.SystemPropsItem; 31 import com.android.loganalysis.item.TopItem; 32 import com.android.loganalysis.item.TracesItem; 33 34 import java.io.BufferedReader; 35 import java.io.IOException; 36 import java.text.DateFormat; 37 import java.text.ParseException; 38 import java.text.SimpleDateFormat; 39 import java.util.Date; 40 import java.util.List; 41 import java.util.ListIterator; 42 import java.util.regex.Matcher; 43 import java.util.regex.Pattern; 44 45 /** 46 * A {@link IParser} to parse Android bugreports. 47 */ 48 public class BugreportParser extends AbstractSectionParser { 49 private static final String MEM_INFO_SECTION_REGEX = "------ MEMORY INFO .*"; 50 private static final String PROCRANK_SECTION_REGEX = "------ PROCRANK .*"; 51 private static final String KERNEL_LOG_SECTION_REGEX = "------ KERNEL LOG .*"; 52 private static final String LAST_KMSG_SECTION_REGEX = "------ LAST KMSG .*"; 53 private static final String TOP_SECTION_REGEX = "------ CPU INFO .*"; 54 private static final String SYSTEM_PROP_SECTION_REGEX = "------ SYSTEM PROPERTIES .*"; 55 private static final String SYSTEM_LOG_SECTION_REGEX = 56 "------ (SYSTEM|MAIN|MAIN AND SYSTEM) LOG .*"; 57 private static final String ANR_TRACES_SECTION_REGEX = "------ VM TRACES AT LAST ANR .*"; 58 private static final String DUMPSYS_SECTION_REGEX = "------ DUMPSYS .*"; 59 private static final String ACTIVITY_SERVICE_SECTION_REGEX = 60 "^------ APP SERVICES \\(dumpsys activity service all\\) ------$"; 61 private static final String NOOP_SECTION_REGEX = "------ .* ------"; 62 63 private static final String BOOTREASON_PROP = "ro.boot.bootreason"; 64 private static final String BOOTREASON_KERNEL = "androidboot.bootreason"; 65 66 /** 67 * Matches: == dumpstate: 2012-04-26 12:13:14 68 */ 69 private static final Pattern DATE = Pattern.compile( 70 "^== dumpstate: (\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2})$"); 71 72 /** 73 * Matches: Command line: key=value key=value 74 */ 75 private static final Pattern COMMAND_LINE = Pattern.compile( 76 "Command line: (.*)"); 77 78 private IParser mBugreportParser = new IParser() { 79 @Override 80 public BugreportItem parse(List<String> lines) { 81 BugreportItem bugreport = null; 82 for (String line : lines) { 83 if (bugreport == null && !"".equals(line.trim())) { 84 bugreport = new BugreportItem(); 85 } 86 Matcher m = DATE.matcher(line); 87 if (m.matches()) { 88 bugreport.setTime(parseTime(m.group(1))); 89 } 90 m = COMMAND_LINE.matcher(line); 91 if (m.matches()) { 92 String argString = m.group(1).trim(); 93 if (!argString.isEmpty()) { 94 String[] args = argString.split("\\s+"); 95 for (String arg : args) { 96 String[] keyValue = arg.split("=", 2); 97 if (keyValue.length == 2) { 98 mCommandLine.put(keyValue[0], keyValue[1]); 99 } else { 100 mCommandLine.put(keyValue[0], null); 101 } 102 } 103 } 104 } 105 } 106 return bugreport; 107 } 108 }; 109 private MemInfoParser mMemInfoParser = new MemInfoParser(); 110 private ProcrankParser mProcrankParser = new ProcrankParser(); 111 private TopParser mTopParser = new TopParser(); 112 private SystemPropsParser mSystemPropsParser = new SystemPropsParser(); 113 private TracesParser mTracesParser = new TracesParser(); 114 private KernelLogParser mKernelLogParser = new KernelLogParser(); 115 private KernelLogParser mLastKmsgParser = new KernelLogParser(); 116 private LogcatParser mLogcatParser = new LogcatParser(); 117 private DumpsysParser mDumpsysParser = new DumpsysParser(); 118 private ActivityServiceParser mActivityServiceParser = new ActivityServiceParser(); 119 120 private BugreportItem mBugreport = null; 121 private CommandLineItem mCommandLine = new CommandLineItem(); 122 123 private boolean mParsedInput = false; 124 125 /** 126 * Parse a bugreport from a {@link BufferedReader} into an {@link BugreportItem} object. 127 * 128 * @param input a {@link BufferedReader}. 129 * @return The {@link BugreportItem}. 130 * @see #parse(List) 131 */ 132 public BugreportItem parse(BufferedReader input) throws IOException { 133 String line; 134 135 setup(); 136 while ((line = input.readLine()) != null) { 137 if (!mParsedInput && !"".equals(line.trim())) { 138 mParsedInput = true; 139 } 140 parseLine(line); 141 } 142 commit(); 143 144 return mBugreport; 145 } 146 147 /** 148 * {@inheritDoc} 149 * 150 * @return The {@link BugreportItem}. 151 */ 152 @Override 153 public BugreportItem parse(List<String> lines) { 154 setup(); 155 for (String line : lines) { 156 if (!mParsedInput && !"".equals(line.trim())) { 157 mParsedInput = true; 158 } 159 parseLine(line); 160 } 161 commit(); 162 163 return mBugreport; 164 } 165 166 /** 167 * Sets up the parser by adding the section parsers and adding an initial {@link IParser} to 168 * parse the bugreport header. 169 */ 170 protected void setup() { 171 // Set the initial parser explicitly since the header isn't part of a section. 172 setParser(mBugreportParser); 173 addSectionParser(mMemInfoParser, MEM_INFO_SECTION_REGEX); 174 addSectionParser(mProcrankParser, PROCRANK_SECTION_REGEX); 175 addSectionParser(mTopParser, TOP_SECTION_REGEX); 176 addSectionParser(mSystemPropsParser, SYSTEM_PROP_SECTION_REGEX); 177 addSectionParser(mTracesParser, ANR_TRACES_SECTION_REGEX); 178 addSectionParser(mLogcatParser, SYSTEM_LOG_SECTION_REGEX); 179 addSectionParser(mKernelLogParser, KERNEL_LOG_SECTION_REGEX); 180 addSectionParser(mLastKmsgParser, LAST_KMSG_SECTION_REGEX); 181 addSectionParser(mDumpsysParser, DUMPSYS_SECTION_REGEX); 182 addSectionParser(mActivityServiceParser, ACTIVITY_SERVICE_SECTION_REGEX); 183 addSectionParser(new NoopParser(), NOOP_SECTION_REGEX); 184 mKernelLogParser.setAddUnknownBootreason(false); 185 mLastKmsgParser.setAddUnknownBootreason(false); 186 } 187 188 /** 189 * {@inheritDoc} 190 */ 191 @Override 192 protected void commit() { 193 // signal EOF 194 super.commit(); 195 196 if (mParsedInput && mBugreport == null) { 197 mBugreport = new BugreportItem(); 198 } 199 200 if (mBugreport != null) { 201 mBugreport.setCommandLine(mCommandLine); 202 mBugreport.setMemInfo((MemInfoItem) getSection(mMemInfoParser)); 203 mBugreport.setProcrank((ProcrankItem) getSection(mProcrankParser)); 204 mBugreport.setTop((TopItem) getSection(mTopParser)); 205 mBugreport.setSystemLog((LogcatItem) getSection(mLogcatParser)); 206 mBugreport.setKernelLog((KernelLogItem) getSection(mKernelLogParser)); 207 mBugreport.setLastKmsg((KernelLogItem) getSection(mLastKmsgParser)); 208 mBugreport.setSystemProps((SystemPropsItem) getSection(mSystemPropsParser)); 209 mBugreport.setDumpsys((DumpsysItem) getSection(mDumpsysParser)); 210 mBugreport.setActivityService((ActivityServiceItem) getSection(mActivityServiceParser)); 211 212 if (mBugreport.getSystemLog() != null && mBugreport.getProcrank() != null) { 213 for (IItem item : mBugreport.getSystemLog().getEvents()) { 214 if (item instanceof MiscLogcatItem && 215 ((MiscLogcatItem) item).getApp() == null) { 216 MiscLogcatItem logcatItem = (MiscLogcatItem) item; 217 logcatItem.setApp(mBugreport.getProcrank().getProcessName( 218 logcatItem.getPid())); 219 } 220 } 221 } 222 223 TracesItem traces = (TracesItem) getSection(mTracesParser); 224 if (traces != null && traces.getApp() != null && traces.getStack() != null && 225 mBugreport.getSystemLog() != null) { 226 addAnrTrace(mBugreport.getSystemLog().getAnrs(), traces.getApp(), 227 traces.getStack()); 228 } 229 230 KernelLogItem lastKmsg = mBugreport.getLastKmsg(); 231 if (lastKmsg == null) { 232 lastKmsg = new KernelLogItem(); 233 mBugreport.setLastKmsg(lastKmsg); 234 } 235 String bootreason = null; 236 if (mBugreport.getSystemProps() != null && 237 mBugreport.getSystemProps().containsKey(BOOTREASON_PROP)) { 238 bootreason = mBugreport.getSystemProps().get(BOOTREASON_PROP); 239 } else if (mCommandLine.containsKey(BOOTREASON_KERNEL)) { 240 bootreason = mCommandLine.get(BOOTREASON_KERNEL); 241 } 242 if (bootreason != null) { 243 Matcher m = KernelLogParser.BAD_BOOTREASONS.matcher(bootreason); 244 if (m.matches()) { 245 MiscKernelLogItem item = new MiscKernelLogItem(); 246 item.setStack("Last boot reason: " + bootreason.trim()); 247 item.setCategory(KernelLogParser.KERNEL_RESET); 248 item.setPreamble(""); 249 item.setEventTime(0.0); 250 lastKmsg.addEvent(item); 251 } 252 m = KernelLogParser.GOOD_BOOTREASONS.matcher(bootreason); 253 if (m.matches()) { 254 MiscKernelLogItem item = new MiscKernelLogItem(); 255 item.setStack("Last boot reason: " + bootreason.trim()); 256 item.setCategory(KernelLogParser.NORMAL_REBOOT); 257 lastKmsg.addEvent(item); 258 } 259 } 260 261 if (lastKmsg.getMiscEvents(KernelLogParser.KERNEL_RESET).isEmpty() && 262 lastKmsg.getMiscEvents(KernelLogParser.NORMAL_REBOOT).isEmpty()) { 263 MiscKernelLogItem unknownReset = new MiscKernelLogItem(); 264 unknownReset.setStack("Unknown reason"); 265 unknownReset.setCategory(KernelLogParser.KERNEL_RESET); 266 unknownReset.setPreamble(""); 267 unknownReset.setEventTime(0.0); 268 lastKmsg.addEvent(unknownReset); 269 } 270 } 271 } 272 273 /** 274 * Add the trace from {@link TracesItem} to the last seen {@link AnrItem} matching a given app. 275 */ 276 private void addAnrTrace(List<AnrItem> anrs, String app, String trace) { 277 ListIterator<AnrItem> li = anrs.listIterator(anrs.size()); 278 279 while (li.hasPrevious()) { 280 AnrItem anr = li.previous(); 281 if (app.equals(anr.getApp())) { 282 anr.setTrace(trace); 283 return; 284 } 285 } 286 } 287 288 /** 289 * Set the {@link BugreportItem} and the year of the {@link LogcatParser} from the bugreport 290 * header. 291 */ 292 @Override 293 protected void onSwitchParser() { 294 if (mBugreport == null) { 295 mBugreport = (BugreportItem) getSection(mBugreportParser); 296 if (mBugreport != null && mBugreport.getTime() != null) { 297 mLogcatParser.setYear(new SimpleDateFormat("yyyy").format(mBugreport.getTime())); 298 } 299 } 300 } 301 302 /** 303 * Converts a {@link String} into a {@link Date}. 304 */ 305 private static Date parseTime(String timeStr) { 306 DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 307 try { 308 return formatter.parse(timeStr); 309 } catch (ParseException e) { 310 // CLog.e("Could not parse time string %s", timeStr); 311 return null; 312 } 313 } 314 } 315 316