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