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