Home | History | Annotate | Download | only in cmdline
      1 /*
      2  * Copyright (C) 2014 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 #ifndef ART_CMDLINE_CMDLINE_H_
     18 #define ART_CMDLINE_CMDLINE_H_
     19 
     20 #include <stdio.h>
     21 #include <stdlib.h>
     22 
     23 #include <fstream>
     24 #include <iostream>
     25 #include <string>
     26 
     27 #include "android-base/stringprintf.h"
     28 
     29 #include "base/logging.h"
     30 #include "base/stringpiece.h"
     31 #include "noop_compiler_callbacks.h"
     32 #include "runtime.h"
     33 
     34 #if !defined(NDEBUG)
     35 #define DBG_LOG LOG(INFO)
     36 #else
     37 #define DBG_LOG LOG(DEBUG)
     38 #endif
     39 
     40 namespace art {
     41 
     42 // TODO: Move to <runtime/utils.h> and remove all copies of this function.
     43 static bool LocationToFilename(const std::string& location, InstructionSet isa,
     44                                std::string* filename) {
     45   bool has_system = false;
     46   bool has_cache = false;
     47   // image_location = /system/framework/boot.art
     48   // system_image_filename = /system/framework/<image_isa>/boot.art
     49   std::string system_filename(GetSystemImageFilename(location.c_str(), isa));
     50   if (OS::FileExists(system_filename.c_str())) {
     51     has_system = true;
     52   }
     53 
     54   bool have_android_data = false;
     55   bool dalvik_cache_exists = false;
     56   bool is_global_cache = false;
     57   std::string dalvik_cache;
     58   GetDalvikCache(GetInstructionSetString(isa), false, &dalvik_cache,
     59                  &have_android_data, &dalvik_cache_exists, &is_global_cache);
     60 
     61   std::string cache_filename;
     62   if (have_android_data && dalvik_cache_exists) {
     63     // Always set output location even if it does not exist,
     64     // so that the caller knows where to create the image.
     65     //
     66     // image_location = /system/framework/boot.art
     67     // *image_filename = /data/dalvik-cache/<image_isa>/boot.art
     68     std::string error_msg;
     69     if (GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(),
     70                                &cache_filename, &error_msg)) {
     71       has_cache = true;
     72     }
     73   }
     74   if (has_system) {
     75     *filename = system_filename;
     76     return true;
     77   } else if (has_cache) {
     78     *filename = cache_filename;
     79     return true;
     80   } else {
     81     return false;
     82   }
     83 }
     84 
     85 static Runtime* StartRuntime(const char* boot_image_location, InstructionSet instruction_set) {
     86   CHECK(boot_image_location != nullptr);
     87 
     88   RuntimeOptions options;
     89 
     90   // We are more like a compiler than a run-time. We don't want to execute code.
     91   {
     92     static NoopCompilerCallbacks callbacks;
     93     options.push_back(std::make_pair("compilercallbacks", &callbacks));
     94   }
     95 
     96   // Boot image location.
     97   {
     98     std::string boot_image_option;
     99     boot_image_option += "-Ximage:";
    100     boot_image_option += boot_image_location;
    101     options.push_back(std::make_pair(boot_image_option.c_str(), nullptr));
    102   }
    103 
    104   // Instruction set.
    105   options.push_back(
    106       std::make_pair("imageinstructionset",
    107                      reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
    108   // None of the command line tools need sig chain. If this changes we'll need
    109   // to upgrade this option to a proper parameter.
    110   options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
    111   if (!Runtime::Create(options, false)) {
    112     fprintf(stderr, "Failed to create runtime\n");
    113     return nullptr;
    114   }
    115 
    116   // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
    117   // give it away now and then switch to a more manageable ScopedObjectAccess.
    118   Thread::Current()->TransitionFromRunnableToSuspended(kNative);
    119 
    120   return Runtime::Current();
    121 }
    122 
    123 struct CmdlineArgs {
    124   enum ParseStatus {
    125     kParseOk,               // Parse successful. Do not set the error message.
    126     kParseUnknownArgument,  // Unknown argument. Do not set the error message.
    127     kParseError,            // Parse ok, but failed elsewhere. Print the set error message.
    128   };
    129 
    130   bool Parse(int argc, char** argv) {
    131     // Skip over argv[0].
    132     argv++;
    133     argc--;
    134 
    135     if (argc == 0) {
    136       fprintf(stderr, "No arguments specified\n");
    137       PrintUsage();
    138       return false;
    139     }
    140 
    141     std::string error_msg;
    142     for (int i = 0; i < argc; i++) {
    143       const StringPiece option(argv[i]);
    144       if (option.starts_with("--boot-image=")) {
    145         boot_image_location_ = option.substr(strlen("--boot-image=")).data();
    146       } else if (option.starts_with("--instruction-set=")) {
    147         StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
    148         instruction_set_ = GetInstructionSetFromString(instruction_set_str.data());
    149         if (instruction_set_ == kNone) {
    150           fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str.data());
    151           PrintUsage();
    152           return false;
    153         }
    154       } else if (option.starts_with("--output=")) {
    155         output_name_ = option.substr(strlen("--output=")).ToString();
    156         const char* filename = output_name_.c_str();
    157         out_.reset(new std::ofstream(filename));
    158         if (!out_->good()) {
    159           fprintf(stderr, "Failed to open output filename %s\n", filename);
    160           PrintUsage();
    161           return false;
    162         }
    163         os_ = out_.get();
    164       } else {
    165         ParseStatus parse_status = ParseCustom(option, &error_msg);
    166 
    167         if (parse_status == kParseUnknownArgument) {
    168           fprintf(stderr, "Unknown argument %s\n", option.data());
    169         }
    170 
    171         if (parse_status != kParseOk) {
    172           fprintf(stderr, "%s\n", error_msg.c_str());
    173           PrintUsage();
    174           return false;
    175         }
    176       }
    177     }
    178 
    179     DBG_LOG << "will call parse checks";
    180 
    181     {
    182       ParseStatus checks_status = ParseChecks(&error_msg);
    183       if (checks_status != kParseOk) {
    184           fprintf(stderr, "%s\n", error_msg.c_str());
    185           PrintUsage();
    186           return false;
    187       }
    188     }
    189 
    190     return true;
    191   }
    192 
    193   virtual std::string GetUsage() const {
    194     std::string usage;
    195 
    196     usage +=  // Required.
    197         "  --boot-image=<file.art>: provide the image location for the boot class path.\n"
    198         "      Do not include the arch as part of the name, it is added automatically.\n"
    199         "      Example: --boot-image=/system/framework/boot.art\n"
    200         "               (specifies /system/framework/<arch>/boot.art as the image file)\n"
    201         "\n";
    202     usage += android::base::StringPrintf(  // Optional.
    203         "  --instruction-set=(arm|arm64|mips|mips64|x86|x86_64): for locating the image\n"
    204         "      file based on the image location set.\n"
    205         "      Example: --instruction-set=x86\n"
    206         "      Default: %s\n"
    207         "\n",
    208         GetInstructionSetString(kRuntimeISA));
    209     usage +=  // Optional.
    210         "  --output=<file> may be used to send the output to a file.\n"
    211         "      Example: --output=/tmp/oatdump.txt\n"
    212         "\n";
    213 
    214     return usage;
    215   }
    216 
    217   // Specified by --boot-image.
    218   const char* boot_image_location_ = nullptr;
    219   // Specified by --instruction-set.
    220   InstructionSet instruction_set_ = kRuntimeISA;
    221   // Specified by --output.
    222   std::ostream* os_ = &std::cout;
    223   std::unique_ptr<std::ofstream> out_;  // If something besides cout is used
    224   std::string output_name_;
    225 
    226   virtual ~CmdlineArgs() {}
    227 
    228   bool ParseCheckBootImage(std::string* error_msg) {
    229     if (boot_image_location_ == nullptr) {
    230       *error_msg = "--boot-image must be specified";
    231       return false;
    232     }
    233 
    234     DBG_LOG << "boot image location: " << boot_image_location_;
    235 
    236     // Checks for --boot-image location.
    237     {
    238       std::string boot_image_location = boot_image_location_;
    239       size_t file_name_idx = boot_image_location.rfind('/');
    240       if (file_name_idx == std::string::npos) {  // Prevent a InsertIsaDirectory check failure.
    241         *error_msg = "Boot image location must have a / in it";
    242         return false;
    243       }
    244 
    245       // Don't let image locations with the 'arch' in it through, since it's not a location.
    246       // This prevents a common error "Could not create an image space..." when initing the Runtime.
    247       if (file_name_idx != std::string::npos) {
    248         std::string no_file_name = boot_image_location.substr(0, file_name_idx);
    249         size_t ancestor_dirs_idx = no_file_name.rfind('/');
    250 
    251         std::string parent_dir_name;
    252         if (ancestor_dirs_idx != std::string::npos) {
    253           parent_dir_name = no_file_name.substr(ancestor_dirs_idx + 1);
    254         } else {
    255           parent_dir_name = no_file_name;
    256         }
    257 
    258         DBG_LOG << "boot_image_location parent_dir_name was " << parent_dir_name;
    259 
    260         if (GetInstructionSetFromString(parent_dir_name.c_str()) != kNone) {
    261           *error_msg = "Do not specify the architecture as part of the boot image location";
    262           return false;
    263         }
    264       }
    265 
    266       // Check that the boot image location points to a valid file name.
    267       std::string file_name;
    268       if (!LocationToFilename(boot_image_location, instruction_set_, &file_name)) {
    269         *error_msg = android::base::StringPrintf("No corresponding file for location '%s' exists",
    270                                                  boot_image_location.c_str());
    271         return false;
    272       }
    273 
    274       DBG_LOG << "boot_image_filename does exist: " << file_name;
    275     }
    276 
    277     return true;
    278   }
    279 
    280   void PrintUsage() {
    281     fprintf(stderr, "%s", GetUsage().c_str());
    282   }
    283 
    284  protected:
    285   virtual ParseStatus ParseCustom(const StringPiece& option ATTRIBUTE_UNUSED,
    286                                   std::string* error_msg ATTRIBUTE_UNUSED) {
    287     return kParseUnknownArgument;
    288   }
    289 
    290   virtual ParseStatus ParseChecks(std::string* error_msg ATTRIBUTE_UNUSED) {
    291     return kParseOk;
    292   }
    293 };
    294 
    295 template <typename Args = CmdlineArgs>
    296 struct CmdlineMain {
    297   int Main(int argc, char** argv) {
    298     InitLogging(argv, Runtime::Aborter);
    299     std::unique_ptr<Args> args = std::unique_ptr<Args>(CreateArguments());
    300     args_ = args.get();
    301 
    302     DBG_LOG << "Try to parse";
    303 
    304     if (args_ == nullptr || !args_->Parse(argc, argv)) {
    305       return EXIT_FAILURE;
    306     }
    307 
    308     bool needs_runtime = NeedsRuntime();
    309     std::unique_ptr<Runtime> runtime;
    310 
    311 
    312     if (needs_runtime) {
    313       std::string error_msg;
    314       if (!args_->ParseCheckBootImage(&error_msg)) {
    315         fprintf(stderr, "%s\n", error_msg.c_str());
    316         args_->PrintUsage();
    317         return EXIT_FAILURE;
    318       }
    319       runtime.reset(CreateRuntime(args.get()));
    320       if (runtime == nullptr) {
    321         return EXIT_FAILURE;
    322       }
    323       if (!ExecuteWithRuntime(runtime.get())) {
    324         return EXIT_FAILURE;
    325       }
    326     } else {
    327       if (!ExecuteWithoutRuntime()) {
    328         return EXIT_FAILURE;
    329       }
    330     }
    331 
    332     if (!ExecuteCommon()) {
    333       return EXIT_FAILURE;
    334     }
    335 
    336     return EXIT_SUCCESS;
    337   }
    338 
    339   // Override this function to create your own arguments.
    340   // Usually will want to return a subtype of CmdlineArgs.
    341   virtual Args* CreateArguments() {
    342     return new Args();
    343   }
    344 
    345   // Override this function to do something else with the runtime.
    346   virtual bool ExecuteWithRuntime(Runtime* runtime) {
    347     CHECK(runtime != nullptr);
    348     // Do nothing
    349     return true;
    350   }
    351 
    352   // Does the code execution need a runtime? Sometimes it doesn't.
    353   virtual bool NeedsRuntime() {
    354     return true;
    355   }
    356 
    357   // Do execution without having created a runtime.
    358   virtual bool ExecuteWithoutRuntime() {
    359     return true;
    360   }
    361 
    362   // Continue execution after ExecuteWith[out]Runtime
    363   virtual bool ExecuteCommon() {
    364     return true;
    365   }
    366 
    367   virtual ~CmdlineMain() {}
    368 
    369  protected:
    370   Args* args_ = nullptr;
    371 
    372  private:
    373   Runtime* CreateRuntime(CmdlineArgs* args) {
    374     CHECK(args != nullptr);
    375 
    376     return StartRuntime(args->boot_image_location_, args->instruction_set_);
    377   }
    378 };
    379 }  // namespace art
    380 
    381 #endif  // ART_CMDLINE_CMDLINE_H_
    382