Home | History | Annotate | Download | only in parser
      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