Home | History | Annotate | Download | only in dexoptanalyzer
      1 /*
      2  * Copyright (C) 2017 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 #include <string>
     18 
     19 #include "android-base/stringprintf.h"
     20 #include "android-base/strings.h"
     21 #include "compiler_filter.h"
     22 #include "dex_file.h"
     23 #include "noop_compiler_callbacks.h"
     24 #include "oat_file_assistant.h"
     25 #include "os.h"
     26 #include "runtime.h"
     27 #include "thread-inl.h"
     28 #include "utils.h"
     29 
     30 namespace art {
     31 
     32 // See OatFileAssistant docs for the meaning of the valid return codes.
     33 enum ReturnCodes {
     34   kNoDexOptNeeded = 0,
     35   kDex2OatFromScratch = 1,
     36   kDex2OatForBootImageOat = 2,
     37   kDex2OatForFilterOat = 3,
     38   kDex2OatForRelocationOat = 4,
     39   kDex2OatForBootImageOdex = 5,
     40   kDex2OatForFilterOdex = 6,
     41   kDex2OatForRelocationOdex = 7,
     42 
     43   kErrorInvalidArguments = 101,
     44   kErrorCannotCreateRuntime = 102,
     45   kErrorUnknownDexOptNeeded = 103
     46 };
     47 
     48 static int original_argc;
     49 static char** original_argv;
     50 
     51 static std::string CommandLine() {
     52   std::vector<std::string> command;
     53   for (int i = 0; i < original_argc; ++i) {
     54     command.push_back(original_argv[i]);
     55   }
     56   return android::base::Join(command, ' ');
     57 }
     58 
     59 static void UsageErrorV(const char* fmt, va_list ap) {
     60   std::string error;
     61   android::base::StringAppendV(&error, fmt, ap);
     62   LOG(ERROR) << error;
     63 }
     64 
     65 static void UsageError(const char* fmt, ...) {
     66   va_list ap;
     67   va_start(ap, fmt);
     68   UsageErrorV(fmt, ap);
     69   va_end(ap);
     70 }
     71 
     72 NO_RETURN static void Usage(const char *fmt, ...) {
     73   va_list ap;
     74   va_start(ap, fmt);
     75   UsageErrorV(fmt, ap);
     76   va_end(ap);
     77 
     78   UsageError("Command: %s", CommandLine().c_str());
     79   UsageError("  Performs a dexopt analysis on the given dex file and returns whether or not");
     80   UsageError("  the dex file needs to be dexopted.");
     81   UsageError("Usage: dexoptanalyzer [options]...");
     82   UsageError("");
     83   UsageError("  --dex-file=<filename>: the dex file which should be analyzed.");
     84   UsageError("");
     85   UsageError("  --isa=<string>: the instruction set for which the analysis should be performed.");
     86   UsageError("");
     87   UsageError("  --compiler-filter=<string>: the target compiler filter to be used as reference");
     88   UsageError("       when deciding if the dex file needs to be optimized.");
     89   UsageError("");
     90   UsageError("  --assume-profile-changed: assumes the profile information has changed");
     91   UsageError("       when deciding if the dex file needs to be optimized.");
     92   UsageError("");
     93   UsageError("  --image=<filename>: optional, the image to be used to decide if the associated");
     94   UsageError("       oat file is up to date. Defaults to $ANDROID_ROOT/framework/boot.art.");
     95   UsageError("       Example: --image=/system/framework/boot.art");
     96   UsageError("");
     97   UsageError("  --android-data=<directory>: optional, the directory which should be used as");
     98   UsageError("       android-data. By default ANDROID_DATA env variable is used.");
     99   UsageError("");
    100   UsageError("  --downgrade: optional, if the purpose of dexopt is to downgrade the dex file");
    101   UsageError("       By default, dexopt considers upgrade case.");
    102   UsageError("");
    103   UsageError("Return code:");
    104   UsageError("  To make it easier to integrate with the internal tools this command will make");
    105   UsageError("    available its result (dexoptNeeded) as the exit/return code. i.e. it will not");
    106   UsageError("    return 0 for success and a non zero values for errors as the conventional");
    107   UsageError("    commands. The following return codes are possible:");
    108   UsageError("        kNoDexOptNeeded = 0");
    109   UsageError("        kDex2OatFromScratch = 1");
    110   UsageError("        kDex2OatForBootImageOat = 2");
    111   UsageError("        kDex2OatForFilterOat = 3");
    112   UsageError("        kDex2OatForRelocationOat = 4");
    113   UsageError("        kDex2OatForBootImageOdex = 5");
    114   UsageError("        kDex2OatForFilterOdex = 6");
    115   UsageError("        kDex2OatForRelocationOdex = 7");
    116 
    117   UsageError("        kErrorInvalidArguments = 101");
    118   UsageError("        kErrorCannotCreateRuntime = 102");
    119   UsageError("        kErrorUnknownDexOptNeeded = 103");
    120   UsageError("");
    121 
    122   exit(kErrorInvalidArguments);
    123 }
    124 
    125 class DexoptAnalyzer FINAL {
    126  public:
    127   DexoptAnalyzer() :
    128       assume_profile_changed_(false),
    129       downgrade_(false) {}
    130 
    131   void ParseArgs(int argc, char **argv) {
    132     original_argc = argc;
    133     original_argv = argv;
    134 
    135     InitLogging(argv, Runtime::Abort);
    136     // Skip over the command name.
    137     argv++;
    138     argc--;
    139 
    140     if (argc == 0) {
    141       Usage("No arguments specified");
    142     }
    143 
    144     for (int i = 0; i < argc; ++i) {
    145       const StringPiece option(argv[i]);
    146       if (option == "--assume-profile-changed") {
    147         assume_profile_changed_ = true;
    148       } else if (option.starts_with("--dex-file=")) {
    149         dex_file_ = option.substr(strlen("--dex-file=")).ToString();
    150       } else if (option.starts_with("--compiler-filter=")) {
    151         std::string filter_str = option.substr(strlen("--compiler-filter=")).ToString();
    152         if (!CompilerFilter::ParseCompilerFilter(filter_str.c_str(), &compiler_filter_)) {
    153           Usage("Invalid compiler filter '%s'", option.data());
    154         }
    155       } else if (option.starts_with("--isa=")) {
    156         std::string isa_str = option.substr(strlen("--isa=")).ToString();
    157         isa_ = GetInstructionSetFromString(isa_str.c_str());
    158         if (isa_ == kNone) {
    159           Usage("Invalid isa '%s'", option.data());
    160         }
    161       } else if (option.starts_with("--image=")) {
    162         image_ = option.substr(strlen("--image=")).ToString();
    163       } else if (option.starts_with("--android-data=")) {
    164         // Overwrite android-data if needed (oat file assistant relies on a valid directory to
    165         // compute dalvik-cache folder). This is mostly used in tests.
    166         std::string new_android_data = option.substr(strlen("--android-data=")).ToString();
    167         setenv("ANDROID_DATA", new_android_data.c_str(), 1);
    168       } else if (option.starts_with("--downgrade")) {
    169         downgrade_ = true;
    170       } else { Usage("Unknown argument '%s'", option.data()); }
    171     }
    172 
    173     if (image_.empty()) {
    174       // If we don't receive the image, try to use the default one.
    175       // Tests may specify a different image (e.g. core image).
    176       std::string error_msg;
    177       image_ = GetDefaultBootImageLocation(&error_msg);
    178 
    179       if (image_.empty()) {
    180         LOG(ERROR) << error_msg;
    181         Usage("--image unspecified and ANDROID_ROOT not set or image file does not exist.");
    182       }
    183     }
    184   }
    185 
    186   bool CreateRuntime() {
    187     RuntimeOptions options;
    188     // The image could be custom, so make sure we explicitly pass it.
    189     std::string img = "-Ximage:" + image_;
    190     options.push_back(std::make_pair(img.c_str(), nullptr));
    191     // The instruction set of the image should match the instruction set we will test.
    192     const void* isa_opt = reinterpret_cast<const void*>(GetInstructionSetString(isa_));
    193     options.push_back(std::make_pair("imageinstructionset", isa_opt));
    194      // Disable libsigchain. We don't don't need it to evaluate DexOptNeeded status.
    195     options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
    196     // Pretend we are a compiler so that we can re-use the same infrastructure to load a different
    197     // ISA image and minimize the amount of things that get started.
    198     NoopCompilerCallbacks callbacks;
    199     options.push_back(std::make_pair("compilercallbacks", &callbacks));
    200     // Make sure we don't attempt to relocate. The tool should only retrieve the DexOptNeeded
    201     // status and not attempt to relocate the boot image.
    202     options.push_back(std::make_pair("-Xnorelocate", nullptr));
    203 
    204     if (!Runtime::Create(options, false)) {
    205       LOG(ERROR) << "Unable to initialize runtime";
    206       return false;
    207     }
    208     // Runtime::Create acquired the mutator_lock_ that is normally given away when we
    209     // Runtime::Start. Give it away now.
    210     Thread::Current()->TransitionFromRunnableToSuspended(kNative);
    211 
    212     return true;
    213   }
    214 
    215   int GetDexOptNeeded() {
    216     // If the file does not exist there's nothing to do.
    217     // This is a fast path to avoid creating the runtime (b/34385298).
    218     if (!OS::FileExists(dex_file_.c_str())) {
    219       return kNoDexOptNeeded;
    220     }
    221     if (!CreateRuntime()) {
    222       return kErrorCannotCreateRuntime;
    223     }
    224     std::unique_ptr<Runtime> runtime(Runtime::Current());
    225 
    226     OatFileAssistant oat_file_assistant(dex_file_.c_str(), isa_, /*load_executable*/ false);
    227     // Always treat elements of the bootclasspath as up-to-date.
    228     // TODO(calin): this check should be in OatFileAssistant.
    229     if (oat_file_assistant.IsInBootClassPath()) {
    230       return kNoDexOptNeeded;
    231     }
    232 
    233     // TODO(calin): Pass the class loader context as an argument to dexoptanalyzer. b/62269291.
    234     int dexoptNeeded = oat_file_assistant.GetDexOptNeeded(
    235         compiler_filter_, assume_profile_changed_, downgrade_);
    236 
    237     // Convert OatFileAssitant codes to dexoptanalyzer codes.
    238     switch (dexoptNeeded) {
    239       case OatFileAssistant::kNoDexOptNeeded: return kNoDexOptNeeded;
    240       case OatFileAssistant::kDex2OatFromScratch: return kDex2OatFromScratch;
    241       case OatFileAssistant::kDex2OatForBootImage: return kDex2OatForBootImageOat;
    242       case OatFileAssistant::kDex2OatForFilter: return kDex2OatForFilterOat;
    243       case OatFileAssistant::kDex2OatForRelocation: return kDex2OatForRelocationOat;
    244 
    245       case -OatFileAssistant::kDex2OatForBootImage: return kDex2OatForBootImageOdex;
    246       case -OatFileAssistant::kDex2OatForFilter: return kDex2OatForFilterOdex;
    247       case -OatFileAssistant::kDex2OatForRelocation: return kDex2OatForRelocationOdex;
    248       default:
    249         LOG(ERROR) << "Unknown dexoptNeeded " << dexoptNeeded;
    250         return kErrorUnknownDexOptNeeded;
    251     }
    252   }
    253 
    254  private:
    255   std::string dex_file_;
    256   InstructionSet isa_;
    257   CompilerFilter::Filter compiler_filter_;
    258   bool assume_profile_changed_;
    259   bool downgrade_;
    260   std::string image_;
    261 };
    262 
    263 static int dexoptAnalyze(int argc, char** argv) {
    264   DexoptAnalyzer analyzer;
    265 
    266   // Parse arguments. Argument mistakes will lead to exit(kErrorInvalidArguments) in UsageError.
    267   analyzer.ParseArgs(argc, argv);
    268   return analyzer.GetDexOptNeeded();
    269 }
    270 
    271 }  // namespace art
    272 
    273 int main(int argc, char **argv) {
    274   return art::dexoptAnalyze(argc, argv);
    275 }
    276