| // 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.VDexReader; |
| import com.android.tools.r8.dex.VDexParser; |
| 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.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 Marker extractMarkerFromDexFile(Path file) |
| throws IOException, ExecutionException, ResourceException { |
| AndroidApp.Builder appBuilder = AndroidApp.builder(); |
| addDexResources(appBuilder, file); |
| return extractMarker(appBuilder.build()); |
| } |
| |
| public static int extractDexSize(Path file) |
| throws IOException, ExecutionException, 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 Marker extractMarkerFromDexProgramData(byte[] data) |
| throws IOException, ExecutionException { |
| AndroidApp app = AndroidApp.builder().addDexProgramData(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); |
| VDexReader vdexReader = new VDexReader(vdexOrigin, Files.newInputStream(file)); |
| 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 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.extractMarker(); |
| } |
| |
| 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; |
| } |
| |
| // 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()) { |
| try { |
| Marker marker = extractMarkerFromDexFile(programFile); |
| 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.print(", " + extractDexSize(programFile) + " bytes"); |
| } |
| System.out.println(); |
| } catch (CompilationError e) { |
| System.out.println( |
| "Failed to read dex/vdex file `" + programFile +"`: '" + e.getMessage() + "'"); |
| } |
| } |
| 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)); |
| } |
| } |
| } |