blob: cc39eb9d0025f3bef2b4b49d003140b72297a9f9 [file] [log] [blame]
// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.profile.art;
import com.android.tools.r8.TextInputStream;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.profile.art.diagnostic.HumanReadableArtProfileParserErrorDiagnostic;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.MethodReferenceUtils;
import com.android.tools.r8.utils.Reporter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
public class HumanReadableArtProfileParser {
private final ArtProfileBuilder profileBuilder;
private final ArtProfileRulePredicate rulePredicate;
private final Reporter reporter;
HumanReadableArtProfileParser(
ArtProfileBuilder profileBuilder, ArtProfileRulePredicate rulePredicate, Reporter reporter) {
this.profileBuilder = profileBuilder;
this.rulePredicate = rulePredicate;
this.reporter = reporter;
}
public static Builder builder() {
return new Builder();
}
public void parse(TextInputStream textInputStream, Origin origin) {
try {
try (InputStreamReader inputStreamReader =
new InputStreamReader(
textInputStream.getInputStream(), textInputStream.getCharset());
BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) {
int lineNumber = 1;
while (bufferedReader.ready()) {
String rule = bufferedReader.readLine();
if (!parseRule(rule)) {
parseError(rule, lineNumber, origin);
}
lineNumber++;
}
}
if (reporter != null) {
reporter.failIfPendingErrors();
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private void parseError(String rule, int lineNumber, Origin origin) {
if (reporter != null) {
reporter.error(new HumanReadableArtProfileParserErrorDiagnostic(rule, lineNumber, origin));
}
}
public boolean parseRule(String rule) {
try {
ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder =
ArtProfileMethodRuleInfoImpl.builder();
rule = parseFlag(rule, 'H', methodRuleInfoBuilder::setIsHot);
rule = parseFlag(rule, 'S', methodRuleInfoBuilder::setIsStartup);
rule = parseFlag(rule, 'P', methodRuleInfoBuilder::setIsPostStartup);
return parseClassOrMethodRule(rule, methodRuleInfoBuilder.build());
} catch (Throwable t) {
return false;
}
}
private static String parseFlag(String rule, char c, Action action) {
if (!rule.isEmpty() && rule.charAt(0) == c) {
action.execute();
return rule.substring(1);
}
return rule;
}
private boolean parseClassOrMethodRule(String rule, ArtProfileMethodRuleInfoImpl methodRuleInfo) {
int arrowStartIndex = rule.indexOf("->");
if (arrowStartIndex >= 0) {
return parseMethodRule(rule, methodRuleInfo, arrowStartIndex);
} else if (methodRuleInfo.isEmpty()) {
return parseClassRule(rule);
} else {
return false;
}
}
private boolean parseClassRule(String descriptor) {
TypeReference typeReference = Reference.typeFromDescriptor(descriptor);
if (typeReference == null) {
return false;
}
if (typeReference.isArray()) {
typeReference = typeReference.asArray().getBaseType();
}
if (typeReference.isPrimitive()) {
return false;
}
assert typeReference.isClass();
ClassReference classReference = typeReference.asClass();
if (rulePredicate.testClassRule(classReference, ArtProfileClassRuleInfoImpl.empty())) {
profileBuilder.addClassRule(
classRuleBuilder -> classRuleBuilder.setClassReference(classReference));
}
return true;
}
private boolean parseMethodRule(
String rule, ArtProfileMethodRuleInfoImpl methodRuleInfo, int arrowStartIndex) {
int inlineCacheStartIndex = rule.indexOf('+', arrowStartIndex + 2);
String descriptor = inlineCacheStartIndex > 0 ? rule.substring(0, inlineCacheStartIndex) : rule;
MethodReference methodReference =
MethodReferenceUtils.parseSmaliString(descriptor, arrowStartIndex);
if (methodReference == null) {
return false;
}
if (rulePredicate.testMethodRule(methodReference, methodRuleInfo)) {
profileBuilder.addMethodRule(
methodRuleBuilder ->
methodRuleBuilder
.setMethodReference(methodReference)
.setMethodRuleInfo(
methodRuleInfoBuilder ->
methodRuleInfoBuilder
.setIsHot(methodRuleInfo.isHot())
.setIsStartup(methodRuleInfo.isStartup())
.setIsPostStartup(methodRuleInfo.isPostStartup())));
}
return true;
}
public static class Builder implements HumanReadableArtProfileParserBuilder {
private ArtProfileBuilder profileBuilder;
private ArtProfileRulePredicate rulePredicate = new AlwaysTrueArtProfileRulePredicate();
private Reporter reporter;
public Builder setReporter(Reporter reporter) {
this.reporter = reporter;
return this;
}
@Override
public Builder setRulePredicate(ArtProfileRulePredicate rulePredicate) {
this.rulePredicate = rulePredicate;
return this;
}
public Builder setProfileBuilder(ArtProfileBuilder profileBuilder) {
this.profileBuilder = profileBuilder;
return this;
}
public HumanReadableArtProfileParser build() {
return new HumanReadableArtProfileParser(profileBuilder, rulePredicate, reporter);
}
}
}