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