blob: 2bafd30cfe804894b04c52c549469394f5d97742 [file] [log] [blame]
/*
* Copyright (C) 2019 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.IItem;
import com.android.loganalysis.item.SystemServicesTimingItem;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* An {@link IParser} to parse boot metrics from logcat. It currently assumes "threadtime" format of
* logcat. It will parse duration metrics for some system services like System Server, Zygote,
* System UI, e.t.c.
*
* <p>TODO(b/133166326): Add support for parsing thread time info from log lines, and also support
* dynamically adding new log line patterns.
*/
public class TimingsLogParser implements IParser {
private static final String SYSTEM_SERVICES_TIME_PREFIX =
"^\\d*-\\d*\\s*\\d*:\\d*:\\d*.\\d*\\s*"
+ "\\d*\\s*\\d*\\s*D\\s*(?<componentname>.*):\\s*(?<subname>\\S*)\\s*";
private static final String SYSTEM_SERVICES_TIME_SUFFIX = ":\\s*(?<time>.*)ms\\s*$";
/**
* Match the line with system services duration info like:
*
* <p>03-10 21:43:40.328 1005 1005 D SystemServerTiming:
* StartKeyAttestationApplicationIdProviderService took to complete: 3474ms
*/
private static final Pattern SYSTEM_SERVICES_DURATION =
Pattern.compile(
String.format(
"%stook to complete%s",
SYSTEM_SERVICES_TIME_PREFIX, SYSTEM_SERVICES_TIME_SUFFIX));
/**
* Match the line with system services start time info like:
*
* <p>01-10 01:25:57.249 989 989 D BootAnimation: BootAnimationStartTiming start time: 8343ms
*/
private static final Pattern SYSTEM_SERVICES_START_TIME =
Pattern.compile(
String.format(
"%sstart time%s",
SYSTEM_SERVICES_TIME_PREFIX, SYSTEM_SERVICES_TIME_SUFFIX));
@Override
public IItem parse(List<String> lines) {
throw new UnsupportedOperationException(
"Method has not been implemented in lieu of others");
}
/**
* A method that parses the logcat input for system services timing information. It will ignore
* duplicated log lines and will keep multiple values for the same timing metric generated at
* different time in the log
*
* @param input Logcat input as a {@link BufferedReader}
* @return a list of {@link SystemServicesTimingItem}
* @throws IOException
*/
public List<SystemServicesTimingItem> parseSystemServicesTimingItems(BufferedReader input)
throws IOException {
Set<String> matchedLines = new HashSet<>();
List<SystemServicesTimingItem> items = new ArrayList<>();
String line;
while ((line = input.readLine()) != null) {
if (matchedLines.contains(line)) {
continue;
}
SystemServicesTimingItem item = parseSystemServicesTimingItem(line);
if (item == null) {
continue;
}
items.add(item);
matchedLines.add(line);
}
return items;
}
/**
* Parse a particular log line to see if it matches the system service timing pattern and return
* a {@link SystemServicesTimingItem} if matches, otherwise return null.
*
* @param line a single log line
* @return a {@link SystemServicesTimingItem}
*/
private SystemServicesTimingItem parseSystemServicesTimingItem(String line) {
Matcher matcher = SYSTEM_SERVICES_DURATION.matcher(line);
boolean durationMatched = matcher.matches();
if (!durationMatched) {
matcher = SYSTEM_SERVICES_START_TIME.matcher(line);
}
if (!matcher.matches()) {
return null;
}
SystemServicesTimingItem item = new SystemServicesTimingItem();
item.setComponent(matcher.group("componentname").trim());
item.setSubcomponent(matcher.group("subname").trim());
if (durationMatched) {
item.setDuration(Double.parseDouble(matcher.group("time")));
} else {
item.setStartTime(Double.parseDouble(matcher.group("time")));
}
return item;
}
}