1 /* 2 * Copyright (C) 2016 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 17 package com.android.loganalysis.parser; 18 19 import com.android.loganalysis.item.DmesgActionInfoItem; 20 import com.android.loganalysis.item.DmesgItem; 21 import com.android.loganalysis.item.DmesgServiceInfoItem; 22 import com.android.loganalysis.item.DmesgStageInfoItem; 23 24 import com.google.common.annotations.VisibleForTesting; 25 26 import java.io.BufferedReader; 27 import java.io.IOException; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.regex.Matcher; 31 import java.util.regex.Pattern; 32 33 34 /** 35 * Parse the dmesg logs. </p> 36 */ 37 public class DmesgParser implements IParser { 38 39 private static final String SERVICENAME = "SERVICENAME"; 40 private static final String TIMESTAMP = "TIMESTAMP"; 41 private static final String STAGE = "STAGE"; 42 private static final String ACTION = "ACTION"; 43 private static final String SOURCE = "SOURCE"; 44 private static final String DURATION = "DURATION"; 45 private static final String UEVENTD = "ueventd"; 46 private static final String INIT = "init"; 47 48 // Matches: [ 14.822691] init: 49 private static final String SERVICE_PREFIX = String.format("^\\[\\s+(?<%s>.*)\\] init:\\s+", 50 TIMESTAMP); 51 // Matches: [ 3.791635] ueventd: 52 private static final String UEVENTD_PREFIX = String.format("^\\[\\s+(?<%s>.*)\\] ueventd:\\s+", 53 TIMESTAMP); 54 55 // Matches: starting service 'ueventd'... 56 private static final String START_SERVICE_SUFFIX = String.format("starting service " 57 + "\\'(?<%s>.*)\\'...", SERVICENAME); 58 // Matches: Service 'ueventd' (pid 439) exited with status 0 59 private static final String EXIT_SERVICE_SUFFIX = String.format("Service \\'(?<%s>.*)\\'\\s+" 60 + "\\((?<PID>.*)\\) exited with status 0.*", SERVICENAME); 61 62 private static final Pattern START_SERVICE = Pattern.compile( 63 String.format("%s%s", SERVICE_PREFIX, START_SERVICE_SUFFIX)); 64 private static final Pattern EXIT_SERVICE = Pattern.compile( 65 String.format("%s%s", SERVICE_PREFIX, EXIT_SERVICE_SUFFIX)); 66 67 // Matches: init first stage started! 68 // Matches: init second stage started! 69 private static final String START_STAGE_PREFIX = String.format("init (?<%s>.*) stage started!", 70 STAGE); 71 72 // Matches: [ 13.647997] init: init first stage started! 73 private static final Pattern START_STAGE = Pattern.compile( 74 String.format("%s%s", SERVICE_PREFIX, START_STAGE_PREFIX)); 75 76 // Matches: init: processing action (early-init) from (/init.rc:14) 77 private static final String START_PROCESSING_ACTION_PREFIX = 78 String.format("processing action \\((?<%s>[^)]*)\\)( from \\((?<%s>.*)\\)|.*)$", 79 ACTION, SOURCE); 80 81 // Matches: init: processing action (early-init) from (/init.rc:14) 82 private static final Pattern START_PROCESSING_ACTION = 83 Pattern.compile(String.format("%s%s", SERVICE_PREFIX, START_PROCESSING_ACTION_PREFIX)); 84 85 // Matches: [ 3.791635] ueventd: Coldboot took 0.695055 seconds 86 private static final String STAGE_SUFFIX= String.format( 87 "(?<%s>.*)\\s+took\\s+(?<%s>.*)\\s+seconds$", STAGE, DURATION); 88 private static final Pattern UEVENTD_STAGE_INFO = Pattern.compile( 89 String.format("%s%s", UEVENTD_PREFIX, STAGE_SUFFIX)); 90 91 private static final String PROPERTY_SUFFIX= String.format( 92 "(?<%s>.*)\\s+took\\s+(?<%s>.*)ms$", STAGE, DURATION); 93 // Matches [ 7.270487] init: Wait for property 'apexd.status=ready' took 230ms 94 private static final Pattern WAIT_FOR_PROPERTY_INFO = Pattern.compile( 95 String.format("%s%s", SERVICE_PREFIX, PROPERTY_SUFFIX)); 96 97 private DmesgItem mDmesgItem = new DmesgItem(); 98 99 /** 100 * {@inheritDoc} 101 */ 102 @Override parse(List<String> lines)103 public DmesgItem parse(List<String> lines) { 104 for (String line : lines) { 105 parse(line); 106 } 107 108 return mDmesgItem; 109 } 110 111 /** 112 * Parse the kernel log till EOF to retrieve the duration of the service calls, start times of 113 * different boot stages and actions taken. Besides, while parsing these informations are stored 114 * in the intermediate {@link DmesgServiceInfoItem}, {@link DmesgStageInfoItem} and 115 * {@link DmesgActionInfoItem} objects 116 * 117 * @param input dmesg log 118 * @throws IOException 119 */ parseInfo(BufferedReader bufferedLog)120 public DmesgItem parseInfo(BufferedReader bufferedLog) throws IOException { 121 String line; 122 while ((line = bufferedLog.readLine()) != null) { 123 parse(line); 124 } 125 126 return mDmesgItem; 127 } 128 129 /** 130 * <p> 131 * Parse single line of the dmesg log to retrieve the duration of the service calls or start 132 * times of different boot stages or start times of actions taken based on the info contained in 133 * the line. 134 * </p> 135 * <p> 136 * Besides, while parsing these informations are stored in the intermediate 137 * {@link DmesgServiceInfoItem}, {@link DmesgStageInfoItem} and {@link DmesgActionInfoItem} 138 * objects 139 * </p> 140 * 141 * @param line individual line of the dmesg log 142 */ parse(String line)143 private void parse(String line) { 144 145 if (parseServiceInfo(line)) { 146 return; 147 } 148 if (parseStageInfo(line)) { 149 return; 150 } 151 if (parseActionInfo(line)) { 152 return; 153 } 154 } 155 156 /** 157 * Parse init services {@code start time} and {@code end time} from each {@code line} of dmesg 158 * log and store the {@code duration} it took to complete the service if the end time stamp is 159 * available in {@link DmesgServiceInfoItem}. 160 * 161 * @param individual line of the dmesg log 162 * @return {@code true}, if the {@code line} indicates start or end of a service, 163 * {@code false}, otherwise 164 */ 165 @VisibleForTesting parseServiceInfo(String line)166 boolean parseServiceInfo(String line) { 167 Matcher match = null; 168 if ((match = matches(START_SERVICE, line)) != null) { 169 DmesgServiceInfoItem serviceItem = new DmesgServiceInfoItem(); 170 serviceItem.setServiceName(match.group(SERVICENAME)); 171 serviceItem.setStartTime((long) (Double.parseDouble( 172 match.group(TIMESTAMP)) * 1000)); 173 getServiceInfoItems().put(match.group(SERVICENAME), serviceItem); 174 return true; 175 } else if ((match = matches(EXIT_SERVICE, line)) != null) { 176 if (getServiceInfoItems().containsKey(match.group(SERVICENAME))) { 177 DmesgServiceInfoItem serviceItem = getServiceInfoItems().get( 178 match.group(SERVICENAME)); 179 serviceItem.setEndTime((long) (Double.parseDouble( 180 match.group(TIMESTAMP)) * 1000)); 181 } 182 return true; 183 } 184 return false; 185 } 186 187 /** 188 * Parse init stages log from each {@code line} of dmesg log and 189 * store the stage name, start time and duration if available in a 190 * {@link DmesgStageInfoItem} object 191 * 192 * @param individual line of the dmesg log 193 * @return {@code true}, if the {@code line} indicates start of a boot stage, 194 * {@code false}, otherwise 195 */ 196 @VisibleForTesting parseStageInfo(String line)197 boolean parseStageInfo(String line) { 198 Matcher match = null; 199 if ((match = matches(START_STAGE, line)) != null) { 200 DmesgStageInfoItem stageInfoItem = new DmesgStageInfoItem(); 201 stageInfoItem.setStageName(match.group(STAGE)); 202 stageInfoItem.setStartTime((long) (Double.parseDouble( 203 match.group(TIMESTAMP)) * 1000)); 204 mDmesgItem.addStageInfoItem(stageInfoItem); 205 return true; 206 } 207 if((match = matches(UEVENTD_STAGE_INFO, line)) != null) { 208 DmesgStageInfoItem stageInfoItem = new DmesgStageInfoItem(); 209 stageInfoItem.setStageName(String.format("%s_%s", UEVENTD, match.group(STAGE))); 210 stageInfoItem.setDuration((long) (Double.parseDouble( 211 match.group(DURATION)) * 1000)); 212 mDmesgItem.addStageInfoItem(stageInfoItem); 213 return true; 214 } 215 if((match = matches(WAIT_FOR_PROPERTY_INFO, line)) != null) { 216 DmesgStageInfoItem stageInfoItem = new DmesgStageInfoItem(); 217 stageInfoItem.setStageName(String.format("%s_%s", INIT, match.group(STAGE))); 218 stageInfoItem.setDuration((long) Double.parseDouble(match.group(DURATION))); 219 mDmesgItem.addStageInfoItem(stageInfoItem); 220 return true; 221 } 222 223 return false; 224 } 225 226 /** 227 * Parse action from each {@code line} of dmesg log and store the action name and start time 228 * in {@link DmesgActionInfoItem} object 229 * 230 * @param individual {@code line} of the dmesg log 231 * @return {@code true}, if {@code line} indicates starting of processing of action 232 * {@code false}, otherwise 233 */ 234 @VisibleForTesting parseActionInfo(String line)235 boolean parseActionInfo(String line) { 236 Matcher match = null; 237 if ((match = matches(START_PROCESSING_ACTION, line)) != null) { 238 DmesgActionInfoItem actionInfoItem = new DmesgActionInfoItem(); 239 if (match.group(SOURCE) != null) { 240 actionInfoItem.setSourceName(match.group(SOURCE)); 241 } 242 actionInfoItem.setActionName(match.group(ACTION)); 243 actionInfoItem.setStartTime((long) (Double.parseDouble( 244 match.group(TIMESTAMP)) * 1000)); 245 mDmesgItem.addActionInfoItem(actionInfoItem); 246 return true; 247 } 248 return false; 249 } 250 251 /** 252 * Checks whether {@code line} matches the given {@link Pattern}. 253 * 254 * @return The resulting {@link Matcher} obtained by matching the {@code line} against 255 * {@code pattern}, or null if the {@code line} does not match. 256 */ matches(Pattern pattern, String line)257 private static Matcher matches(Pattern pattern, String line) { 258 Matcher ret = pattern.matcher(line); 259 return ret.matches() ? ret : null; 260 } 261 getServiceInfoItems()262 public Map<String, DmesgServiceInfoItem> getServiceInfoItems() { 263 return mDmesgItem.getServiceInfoItems(); 264 } 265 setServiceInfoItems(Map<String, DmesgServiceInfoItem> serviceInfoItems)266 public void setServiceInfoItems(Map<String, DmesgServiceInfoItem> serviceInfoItems) { 267 for(String key : serviceInfoItems.keySet()) { 268 mDmesgItem.addServiceInfoItem(key, serviceInfoItems.get(key)); 269 } 270 } 271 getStageInfoItems()272 public List<DmesgStageInfoItem> getStageInfoItems() { 273 return mDmesgItem.getStageInfoItems(); 274 } 275 getActionInfoItems()276 public List<DmesgActionInfoItem> getActionInfoItems() { 277 return mDmesgItem.getActionInfoItems(); 278 } 279 280 }