blob: 9492e12402e439403da35851698314d1cd459b55 [file] [log] [blame]
// Copyright (c) 2017, the Rex 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;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.VDexParser;
import com.android.tools.r8.dex.VDexReader;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import com.google.common.io.ByteStreams;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.concurrent.ExecutionException;
public class ExtractMarker {
public static class VdexOrigin extends Origin {
private final int index;
public VdexOrigin(Origin vdexOrigin, int index) {
super(vdexOrigin);
this.index = index;
}
@Override
public String part() {
return Integer.toString(index);
}
}
public static Collection<Marker> extractMarkerFromDexFile(Path file)
throws IOException, ExecutionException, ResourceException {
AndroidApp.Builder appBuilder = AndroidApp.builder();
addDexResources(appBuilder, file);
return extractMarker(appBuilder.build());
}
public static Collection<Marker> extractMarkerFromJarFile(Path file)
throws IOException, ExecutionException {
return extractMarker(AndroidApp.builder().addProgramFile(file).build());
}
public static int extractDexSize(Path file) throws IOException, ResourceException {
AndroidApp.Builder appBuilder = AndroidApp.builder();
addDexResources(appBuilder, file);
int size = 0;
for (ProgramResource resource : appBuilder.build().computeAllProgramResources()) {
if (resource.getKind() == Kind.DEX) {
try (InputStream input = resource.getByteStream()) {
size += ByteStreams.toByteArray(input).length;
}
}
}
return size;
}
public static Collection<Marker> extractMarkerFromDexProgramData(byte[] data)
throws IOException, ExecutionException {
AndroidApp app = AndroidApp.builder().addDexProgramData(data, Origin.unknown()).build();
return extractMarker(app);
}
public static Collection<Marker> extractMarkerFromClassProgramData(byte[] data)
throws IOException, ExecutionException {
AndroidApp app = AndroidApp.builder().addClassProgramData(data, Origin.unknown()).build();
return extractMarker(app);
}
private static void addDexResources(AndroidApp.Builder appBuilder, Path file)
throws IOException, ResourceException {
if (FileUtils.isVDexFile(file)) {
PathOrigin vdexOrigin = new PathOrigin(file);
try (InputStream fileInputStream = Files.newInputStream(file)) {
VDexReader vdexReader = new VDexReader(vdexOrigin, fileInputStream);
VDexParser vDexParser = new VDexParser(vdexReader);
int index = 0;
for (byte[] bytes : vDexParser.getDexFiles()) {
appBuilder.addDexProgramData(bytes, new VdexOrigin(vdexOrigin, index));
index++;
}
}
} else {
appBuilder.addProgramFiles(file);
}
}
private static Collection<Marker> extractMarker(AndroidApp app)
throws IOException, ExecutionException {
InternalOptions options = new InternalOptions();
options.skipReadingDexCode = true;
options.minApiLevel = AndroidApiLevel.P.getLevel();
DexApplication dexApp = new ApplicationReader(app, options, new Timing("ExtractMarker")).read();
return dexApp.dexItemFactory.extractMarkers();
}
public static void main(String[] args)
throws IOException, ExecutionException, ResourceException {
ExtractMarkerCommand.Builder builder = ExtractMarkerCommand.parse(args);
ExtractMarkerCommand command = builder.build();
if (command.isPrintHelp()) {
System.out.println(ExtractMarkerCommand.USAGE_MESSAGE);
return;
}
Path cwd = Paths.get(System.getProperty("user.dir"));
// Dex code is not needed for getting the marker. VDex files typically contains quickened byte
// codes which cannot be read, and we want to get the marker from vdex files as well.
int d8Count = 0;
int r8Count = 0;
int otherCount = 0;
for (Path programFile : command.getProgramFiles()) {
Collection<Marker> markers;
try {
markers = extractMarkerFromDexFile(programFile);
} catch (CompilationError e) {
System.out.println(
"Failed to read dex/vdex file `" + programFile + "`: '" + e.getMessage() + "'");
continue;
}
System.out.print("In file: " + cwd.toAbsolutePath().relativize(programFile.toAbsolutePath()));
System.out.println(", " + extractDexSize(programFile) + " bytes:");
for (Marker marker : markers) {
if (marker == null) {
otherCount++;
if (!command.getIncludeOther()) {
continue;
}
} else {
if (marker.isD8()) {
d8Count++;
} else {
r8Count++;
}
}
if (command.getCSV()) {
System.out.print("\"" + programFile + "\"");
System.out.print(", ");
if (marker == null) {
System.out.print("\"no marker\"");
} else {
System.out.print("\"" + (marker.isD8() ? "D8" : "R8") + "\"");
}
System.out.print(", ");
System.out.print(extractDexSize(programFile));
} else {
if (command.getVerbose()) {
System.out.print(programFile);
System.out.print(": ");
}
System.out.print(marker == null ? "D8/R8 marker not found" : marker);
}
System.out.println();
}
}
if (command.getSummary()) {
System.out.println("D8: " + d8Count);
System.out.println("R8: " + r8Count);
System.out.println("Other: " + otherCount);
System.out.println("Total: " + (d8Count + r8Count + otherCount));
}
}
}