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