blob: 90be30bae4c0ee7aa2ba7a91807bdea741eea5f5 [file] [log] [blame]
Igor Murashkin37743352014-11-13 14:38:00 -08001/*
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>
Vladimir Marko8581e2a2019-02-06 15:54:55 +000026#include <string_view>
Igor Murashkin37743352014-11-13 14:38:00 -080027
Andreas Gampe46ee31b2016-12-14 10:11:49 -080028#include "android-base/stringprintf.h"
29
David Sehr891a50e2017-10-27 17:01:07 -070030#include "base/file_utils.h"
Andreas Gampe46ee31b2016-12-14 10:11:49 -080031#include "base/logging.h"
David Sehrc431b9d2018-03-02 12:01:51 -080032#include "base/mutex.h"
Vladimir Marko8581e2a2019-02-06 15:54:55 +000033#include "base/string_view_cpp20.h"
Igor Murashkin37743352014-11-13 14:38:00 -080034#include "noop_compiler_callbacks.h"
Andreas Gampe46ee31b2016-12-14 10:11:49 -080035#include "runtime.h"
Igor Murashkin37743352014-11-13 14:38:00 -080036
37#if !defined(NDEBUG)
38#define DBG_LOG LOG(INFO)
39#else
40#define DBG_LOG LOG(DEBUG)
41#endif
42
43namespace art {
44
45// TODO: Move to <runtime/utils.h> and remove all copies of this function.
46static 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 {
Andreas Gampe8fae4b52017-09-27 20:04:47 -070084 *filename = system_filename;
Igor Murashkin37743352014-11-13 14:38:00 -080085 return false;
86 }
87}
88
Vladimir Marko813b9142018-11-29 11:20:07 +000089static Runtime* StartRuntime(const char* boot_image_location,
90 InstructionSet instruction_set,
91 const std::vector<const char*>& runtime_args) {
Igor Murashkin37743352014-11-13 14:38:00 -080092 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;
Vladimir Marko813b9142018-11-29 11:20:07 +0000107 options.push_back(std::make_pair(boot_image_option, nullptr));
Igor Murashkin37743352014-11-13 14:38:00 -0800108 }
109
110 // Instruction set.
111 options.push_back(
112 std::make_pair("imageinstructionset",
113 reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
Vladimir Marko813b9142018-11-29 11:20:07 +0000114
115 // Explicit runtime args.
116 for (const char* runtime_arg : runtime_args) {
117 options.push_back(std::make_pair(runtime_arg, nullptr));
118 }
119
Calin Juravle01aaf6e2015-06-19 22:05:39 +0100120 // 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));
Igor Murashkin37743352014-11-13 14:38:00 -0800123 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
135struct 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++) {
Vladimir Marko8581e2a2019-02-06 15:54:55 +0000155 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);
Vladimir Marko33bff252017-11-01 14:35:42 +0000162 if (instruction_set_ == InstructionSet::kNone) {
Vladimir Marko8581e2a2019-02-06 15:54:55 +0000163 fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str);
Igor Murashkin37743352014-11-13 14:38:00 -0800164 PrintUsage();
165 return false;
166 }
Vladimir Marko813b9142018-11-29 11:20:07 +0000167 } 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]);
Vladimir Marko8581e2a2019-02-06 15:54:55 +0000175 } else if (StartsWith(option, "--output=")) {
176 output_name_ = std::string(option.substr(strlen("--output=")));
Igor Murashkin37743352014-11-13 14:38:00 -0800177 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 {
Vladimir Marko8581e2a2019-02-06 15:54:55 +0000186 ParseStatus parse_status = ParseCustom(raw_option, option.length(), &error_msg);
Igor Murashkin37743352014-11-13 14:38:00 -0800187
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"
Kevin Brodsky64fff412015-11-24 14:24:34 +0000221 " (specifies /system/framework/<arch>/boot.art as the image file)\n"
Igor Murashkin37743352014-11-13 14:38:00 -0800222 "\n";
Andreas Gampe46ee31b2016-12-14 10:11:49 -0800223 usage += android::base::StringPrintf( // Optional.
Andreas Gampe57b34292015-01-14 15:45:59 -0800224 " --instruction-set=(arm|arm64|mips|mips64|x86|x86_64): for locating the image\n"
Igor Murashkin37743352014-11-13 14:38:00 -0800225 " file based on the image location set.\n"
226 " Example: --instruction-set=x86\n"
227 " Default: %s\n"
228 "\n",
229 GetInstructionSetString(kRuntimeISA));
Vladimir Marko813b9142018-11-29 11:20:07 +0000230 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";
Igor Murashkin37743352014-11-13 14:38:00 -0800236 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.
Andreas Gampe8fae4b52017-09-27 20:04:47 -0700247 InstructionSet instruction_set_ = InstructionSet::kNone;
Vladimir Marko813b9142018-11-29 11:20:07 +0000248 // Runtime arguments specified by --runtime-arg.
249 std::vector<const char*> runtime_args_;
Igor Murashkin37743352014-11-13 14:38:00 -0800250 // 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
Andreas Gampec24f3992014-12-17 20:40:11 -0800257 bool ParseCheckBootImage(std::string* error_msg) {
Igor Murashkin37743352014-11-13 14:38:00 -0800258 if (boot_image_location_ == nullptr) {
259 *error_msg = "--boot-image must be specified";
Andreas Gampec24f3992014-12-17 20:40:11 -0800260 return false;
Igor Murashkin37743352014-11-13 14:38:00 -0800261 }
Andreas Gampe8fae4b52017-09-27 20:04:47 -0700262 if (instruction_set_ == InstructionSet::kNone) {
263 LOG(WARNING) << "No instruction set given, assuming " << GetInstructionSetString(kRuntimeISA);
264 instruction_set_ = kRuntimeISA;
265 }
Igor Murashkin37743352014-11-13 14:38:00 -0800266
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_;
Andreas Gampeca620d72016-11-08 08:09:33 -0800272 size_t file_name_idx = boot_image_location.rfind('/');
Igor Murashkin37743352014-11-13 14:38:00 -0800273 if (file_name_idx == std::string::npos) { // Prevent a InsertIsaDirectory check failure.
274 *error_msg = "Boot image location must have a / in it";
Andreas Gampec24f3992014-12-17 20:40:11 -0800275 return false;
Igor Murashkin37743352014-11-13 14:38:00 -0800276 }
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);
Andreas Gampeca620d72016-11-08 08:09:33 -0800282 size_t ancestor_dirs_idx = no_file_name.rfind('/');
Igor Murashkin37743352014-11-13 14:38:00 -0800283
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
Vladimir Marko33bff252017-11-01 14:35:42 +0000293 if (GetInstructionSetFromString(parent_dir_name.c_str()) != InstructionSet::kNone) {
Igor Murashkin37743352014-11-13 14:38:00 -0800294 *error_msg = "Do not specify the architecture as part of the boot image location";
Andreas Gampec24f3992014-12-17 20:40:11 -0800295 return false;
Igor Murashkin37743352014-11-13 14:38:00 -0800296 }
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)) {
Andreas Gampe8fae4b52017-09-27 20:04:47 -0700302 *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());
Andreas Gampec24f3992014-12-17 20:40:11 -0800306 return false;
Igor Murashkin37743352014-11-13 14:38:00 -0800307 }
308
309 DBG_LOG << "boot_image_filename does exist: " << file_name;
310 }
311
Andreas Gampec24f3992014-12-17 20:40:11 -0800312 return true;
Igor Murashkin37743352014-11-13 14:38:00 -0800313 }
314
Igor Murashkin37743352014-11-13 14:38:00 -0800315 void PrintUsage() {
316 fprintf(stderr, "%s", GetUsage().c_str());
317 }
Andreas Gampec24f3992014-12-17 20:40:11 -0800318
319 protected:
Vladimir Marko8581e2a2019-02-06 15:54:55 +0000320 virtual ParseStatus ParseCustom(const char* raw_option ATTRIBUTE_UNUSED,
321 size_t raw_option_length ATTRIBUTE_UNUSED,
Andreas Gampec24f3992014-12-17 20:40:11 -0800322 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 }
Igor Murashkin37743352014-11-13 14:38:00 -0800329};
330
331template <typename Args = CmdlineArgs>
332struct CmdlineMain {
333 int Main(int argc, char** argv) {
David Sehrc431b9d2018-03-02 12:01:51 -0800334 Locks::Init();
Andreas Gampe51d80cc2017-06-21 21:05:13 -0700335 InitLogging(argv, Runtime::Abort);
Igor Murashkin37743352014-11-13 14:38:00 -0800336 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
Igor Murashkin37743352014-11-13 14:38:00 -0800345 bool needs_runtime = NeedsRuntime();
Andreas Gampec24f3992014-12-17 20:40:11 -0800346 std::unique_ptr<Runtime> runtime;
347
Igor Murashkin37743352014-11-13 14:38:00 -0800348
349 if (needs_runtime) {
Andreas Gampec24f3992014-12-17 20:40:11 -0800350 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 }
Igor Murashkin37743352014-11-13 14:38:00 -0800360 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:
Andreas Gampec24f3992014-12-17 20:40:11 -0800410 Runtime* CreateRuntime(CmdlineArgs* args) {
Igor Murashkin37743352014-11-13 14:38:00 -0800411 CHECK(args != nullptr);
412
Vladimir Marko813b9142018-11-29 11:20:07 +0000413 return StartRuntime(args->boot_image_location_, args->instruction_set_, args_->runtime_args_);
Igor Murashkin37743352014-11-13 14:38:00 -0800414 }
415};
416} // namespace art
417
418#endif // ART_CMDLINE_CMDLINE_H_