blob: 604ec1aa5b461eec9ca189bc05717c4ccc982176 [file] [log] [blame]
/*
* 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.AnrItem;
import com.android.loganalysis.item.BugreportItem;
import com.android.loganalysis.item.BugreportItem.CommandLineItem;
import com.android.loganalysis.item.DumpsysItem;
import com.android.loganalysis.item.IItem;
import com.android.loganalysis.item.KernelLogItem;
import com.android.loganalysis.item.LogcatItem;
import com.android.loganalysis.item.MemInfoItem;
import com.android.loganalysis.item.MiscKernelLogItem;
import com.android.loganalysis.item.MiscLogcatItem;
import com.android.loganalysis.item.ProcrankItem;
import com.android.loganalysis.item.SystemPropsItem;
import com.android.loganalysis.item.TopItem;
import com.android.loganalysis.item.TracesItem;
import java.io.BufferedReader;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A {@link IParser} to parse Android bugreports.
*/
public class BugreportParser extends AbstractSectionParser {
private static final String MEM_INFO_SECTION_REGEX = "------ MEMORY INFO .*";
private static final String PROCRANK_SECTION_REGEX = "------ PROCRANK .*";
private static final String KERNEL_LOG_SECTION_REGEX = "------ KERNEL LOG .*";
private static final String LAST_KMSG_SECTION_REGEX = "------ LAST KMSG .*";
private static final String TOP_SECTION_REGEX = "------ CPU INFO .*";
private static final String SYSTEM_PROP_SECTION_REGEX = "------ SYSTEM PROPERTIES .*";
private static final String SYSTEM_LOG_SECTION_REGEX =
"------ (SYSTEM|MAIN|MAIN AND SYSTEM) LOG .*";
private static final String ANR_TRACES_SECTION_REGEX = "------ VM TRACES AT LAST ANR .*";
private static final String DUMPSYS_SECTION_REGEX = "------ DUMPSYS .*";
private static final String NOOP_SECTION_REGEX = "------ .*";
private static final String BOOTREASON = "androidboot.bootreason";
/**
* Matches: == dumpstate: 2012-04-26 12:13:14
*/
private static final Pattern DATE = Pattern.compile(
"^== dumpstate: (\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2})$");
/**
* Matches: Command line: key=value key=value
*/
private static final Pattern COMMAND_LINE = Pattern.compile(
"Command line:((\\s+[^\\s=]+=[^\\s]*)*)\\s*");
private IParser mBugreportParser = new IParser() {
@Override
public BugreportItem parse(List<String> lines) {
BugreportItem bugreport = null;
for (String line : lines) {
if (bugreport == null && !"".equals(line.trim())) {
bugreport = new BugreportItem();
}
Matcher m = DATE.matcher(line);
if (m.matches()) {
bugreport.setTime(parseTime(m.group(1)));
}
m = COMMAND_LINE.matcher(line);
if (m.matches()) {
String argString = m.group(1).trim();
if (!argString.isEmpty()) {
String[] pairs = argString.split("\\s+");
for (String pair : pairs) {
System.out.println(pair);
String[] keyValue = pair.split("=", 2);
mCommandLine.put(keyValue[0], keyValue[1]);
}
}
}
}
return bugreport;
}
};
private MemInfoParser mMemInfoParser = new MemInfoParser();
private ProcrankParser mProcrankParser = new ProcrankParser();
private TopParser mTopParser = new TopParser();
private SystemPropsParser mSystemPropsParser = new SystemPropsParser();
private TracesParser mTracesParser = new TracesParser();
private KernelLogParser mKernelLogParser = new KernelLogParser();
private KernelLogParser mLastKmsgParser = new KernelLogParser();
private LogcatParser mLogcatParser = new LogcatParser();
private DumpsysParser mDumpsysParser = new DumpsysParser();
private BugreportItem mBugreport = null;
private CommandLineItem mCommandLine = new CommandLineItem();
private boolean mParsedInput = false;
/**
* Parse a bugreport from a {@link BufferedReader} into an {@link BugreportItem} object.
*
* @param input a {@link BufferedReader}.
* @return The {@link BugreportItem}.
* @see #parse(List)
*/
public BugreportItem parse(BufferedReader input) throws IOException {
String line;
setup();
while ((line = input.readLine()) != null) {
if (!mParsedInput && !"".equals(line.trim())) {
mParsedInput = true;
}
parseLine(line);
}
commit();
return mBugreport;
}
/**
* {@inheritDoc}
*
* @return The {@link BugreportItem}.
*/
@Override
public BugreportItem parse(List<String> lines) {
setup();
for (String line : lines) {
if (!mParsedInput && !"".equals(line.trim())) {
mParsedInput = true;
}
parseLine(line);
}
commit();
return mBugreport;
}
/**
* Sets up the parser by adding the section parsers and adding an initial {@link IParser} to
* parse the bugreport header.
*/
protected void setup() {
// Set the initial parser explicitly since the header isn't part of a section.
setParser(mBugreportParser);
addSectionParser(mMemInfoParser, MEM_INFO_SECTION_REGEX);
addSectionParser(mProcrankParser, PROCRANK_SECTION_REGEX);
addSectionParser(mTopParser, TOP_SECTION_REGEX);
addSectionParser(mSystemPropsParser, SYSTEM_PROP_SECTION_REGEX);
addSectionParser(mTracesParser, ANR_TRACES_SECTION_REGEX);
addSectionParser(mLogcatParser, SYSTEM_LOG_SECTION_REGEX);
addSectionParser(mKernelLogParser, KERNEL_LOG_SECTION_REGEX);
addSectionParser(mLastKmsgParser, LAST_KMSG_SECTION_REGEX);
addSectionParser(mDumpsysParser, DUMPSYS_SECTION_REGEX);
addSectionParser(new NoopParser(), NOOP_SECTION_REGEX);
}
/**
* {@inheritDoc}
*/
@Override
protected void commit() {
// signal EOF
super.commit();
if (mParsedInput && mBugreport == null) {
mBugreport = new BugreportItem();
}
if (mBugreport != null) {
mBugreport.setCommandLine(mCommandLine);
mBugreport.setMemInfo((MemInfoItem) getSection(mMemInfoParser));
mBugreport.setProcrank((ProcrankItem) getSection(mProcrankParser));
mBugreport.setTop((TopItem) getSection(mTopParser));
mBugreport.setSystemLog((LogcatItem) getSection(mLogcatParser));
mBugreport.setKernelLog((KernelLogItem) getSection(mKernelLogParser));
mBugreport.setLastKmsg((KernelLogItem) getSection(mLastKmsgParser));
mBugreport.setSystemProps((SystemPropsItem) getSection(mSystemPropsParser));
mBugreport.setDumpsys((DumpsysItem) getSection(mDumpsysParser));
if (mBugreport.getSystemLog() != null && mBugreport.getProcrank() != null) {
for (IItem item : mBugreport.getSystemLog().getEvents()) {
if (item instanceof MiscLogcatItem &&
((MiscLogcatItem) item).getApp() == null) {
MiscLogcatItem logcatItem = (MiscLogcatItem) item;
logcatItem.setApp(mBugreport.getProcrank().getProcessName(
logcatItem.getPid()));
}
}
}
TracesItem traces = (TracesItem) getSection(mTracesParser);
if (traces != null && traces.getApp() != null && traces.getStack() != null &&
mBugreport.getSystemLog() != null) {
addAnrTrace(mBugreport.getSystemLog().getAnrs(), traces.getApp(),
traces.getStack());
}
if (mCommandLine.containsKey(BOOTREASON)) {
String bootreason = mCommandLine.get(BOOTREASON);
Matcher m = KernelLogParser.BAD_BOOTREASONS.matcher(bootreason);
if (m.matches()) {
if (mBugreport.getLastKmsg() == null) {
mBugreport.setLastKmsg(new KernelLogItem());
}
MiscKernelLogItem item = new MiscKernelLogItem();
item.setStack("Last boot reason: " + bootreason.trim());
item.setCategory(KernelLogParser.KERNEL_RESET);
mBugreport.getLastKmsg().addEvent(item);
}
}
}
}
/**
* Add the trace from {@link TracesItem} to the last seen {@link AnrItem} matching a given app.
*/
private void addAnrTrace(List<AnrItem> anrs, String app, String trace) {
ListIterator<AnrItem> li = anrs.listIterator(anrs.size());
while (li.hasPrevious()) {
AnrItem anr = li.previous();
if (app.equals(anr.getApp())) {
anr.setTrace(trace);
return;
}
}
}
/**
* Set the {@link BugreportItem} and the year of the {@link LogcatParser} from the bugreport
* header.
*/
@Override
protected void onSwitchParser() {
if (mBugreport == null) {
mBugreport = (BugreportItem) getSection(mBugreportParser);
if (mBugreport != null && mBugreport.getTime() != null) {
mLogcatParser.setYear(new SimpleDateFormat("yyyy").format(mBugreport.getTime()));
}
}
}
/**
* Converts a {@link String} into a {@link Date}.
*/
private static Date parseTime(String timeStr) {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return formatter.parse(timeStr);
} catch (ParseException e) {
// CLog.e("Could not parse time string %s", timeStr);
return null;
}
}
}