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      */
parse(BufferedReader input)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
parse(List<String> lines)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      */
setup()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
commit()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      */
addAnrTrace(List<AnrItem> anrs, String app, String trace)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
onSwitchParser()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      */
parseTime(String timeStr)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