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