blob: 81a21793d9e627e1f8abbc0fc1ba9da43ef9464c [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>
26
Andreas Gampe46ee31b2016-12-14 10:11:49 -080027#include "android-base/stringprintf.h"
28
David Sehr891a50e2017-10-27 17:01:07 -070029#include "base/file_utils.h"
Andreas Gampe46ee31b2016-12-14 10:11:49 -080030#include "base/logging.h"
David Sehrc431b9d2018-03-02 12:01:51 -080031#include "base/mutex.h"
Igor Murashkin37743352014-11-13 14:38:00 -080032#include "base/stringpiece.h"
33#include "noop_compiler_callbacks.h"
Andreas Gampe46ee31b2016-12-14 10:11:49 -080034#include "runtime.h"
Igor Murashkin37743352014-11-13 14:38:00 -080035
36#if !defined(NDEBUG)
37#define DBG_LOG LOG(INFO)
38#else
39#define DBG_LOG LOG(DEBUG)
40#endif
41
42namespace art {
43
44// TODO: Move to <runtime/utils.h> and remove all copies of this function.
45static 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 {
Andreas Gampe8fae4b52017-09-27 20:04:47 -070083 *filename = system_filename;
Igor Murashkin37743352014-11-13 14:38:00 -080084 return false;
85 }
86}
87
Vladimir Marko813b9142018-11-29 11:20:07 +000088static Runtime* StartRuntime(const char* boot_image_location,
89 InstructionSet instruction_set,
90 const std::vector<const char*>& runtime_args) {
Igor Murashkin37743352014-11-13 14:38:00 -080091 CHECK(boot_image_location != nullptr);
92
93 RuntimeOptions options;
94
95 // We are more like a compiler than a run-time. We don't want to execute code.
96 {
97 static NoopCompilerCallbacks callbacks;
98 options.push_back(std::make_pair("compilercallbacks", &callbacks));
99 }
100
101 // Boot image location.
102 {
103 std::string boot_image_option;
104 boot_image_option += "-Ximage:";
105 boot_image_option += boot_image_location;
Vladimir Marko813b9142018-11-29 11:20:07 +0000106 options.push_back(std::make_pair(boot_image_option, nullptr));
Igor Murashkin37743352014-11-13 14:38:00 -0800107 }
108
109 // Instruction set.
110 options.push_back(
111 std::make_pair("imageinstructionset",
112 reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
Vladimir Marko813b9142018-11-29 11:20:07 +0000113
114 // Explicit runtime args.
115 for (const char* runtime_arg : runtime_args) {
116 options.push_back(std::make_pair(runtime_arg, nullptr));
117 }
118
Calin Juravle01aaf6e2015-06-19 22:05:39 +0100119 // None of the command line tools need sig chain. If this changes we'll need
120 // to upgrade this option to a proper parameter.
121 options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
Igor Murashkin37743352014-11-13 14:38:00 -0800122 if (!Runtime::Create(options, false)) {
123 fprintf(stderr, "Failed to create runtime\n");
124 return nullptr;
125 }
126
127 // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
128 // give it away now and then switch to a more manageable ScopedObjectAccess.
129 Thread::Current()->TransitionFromRunnableToSuspended(kNative);
130
131 return Runtime::Current();
132}
133
134struct CmdlineArgs {
135 enum ParseStatus {
136 kParseOk, // Parse successful. Do not set the error message.
137 kParseUnknownArgument, // Unknown argument. Do not set the error message.
138 kParseError, // Parse ok, but failed elsewhere. Print the set error message.
139 };
140
141 bool Parse(int argc, char** argv) {
142 // Skip over argv[0].
143 argv++;
144 argc--;
145
146 if (argc == 0) {
147 fprintf(stderr, "No arguments specified\n");
148 PrintUsage();
149 return false;
150 }
151
152 std::string error_msg;
153 for (int i = 0; i < argc; i++) {
154 const StringPiece option(argv[i]);
155 if (option.starts_with("--boot-image=")) {
156 boot_image_location_ = option.substr(strlen("--boot-image=")).data();
157 } else if (option.starts_with("--instruction-set=")) {
158 StringPiece instruction_set_str = option.substr(strlen("--instruction-set=")).data();
159 instruction_set_ = GetInstructionSetFromString(instruction_set_str.data());
Vladimir Marko33bff252017-11-01 14:35:42 +0000160 if (instruction_set_ == InstructionSet::kNone) {
Igor Murashkin37743352014-11-13 14:38:00 -0800161 fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str.data());
162 PrintUsage();
163 return false;
164 }
Vladimir Marko813b9142018-11-29 11:20:07 +0000165 } else if (option == "--runtime-arg") {
166 if (i + 1 == argc) {
167 fprintf(stderr, "Missing argument for --runtime-arg\n");
168 PrintUsage();
169 return false;
170 }
171 ++i;
172 runtime_args_.push_back(argv[i]);
Igor Murashkin37743352014-11-13 14:38:00 -0800173 } else if (option.starts_with("--output=")) {
174 output_name_ = option.substr(strlen("--output=")).ToString();
175 const char* filename = output_name_.c_str();
176 out_.reset(new std::ofstream(filename));
177 if (!out_->good()) {
178 fprintf(stderr, "Failed to open output filename %s\n", filename);
179 PrintUsage();
180 return false;
181 }
182 os_ = out_.get();
183 } else {
184 ParseStatus parse_status = ParseCustom(option, &error_msg);
185
186 if (parse_status == kParseUnknownArgument) {
187 fprintf(stderr, "Unknown argument %s\n", option.data());
188 }
189
190 if (parse_status != kParseOk) {
191 fprintf(stderr, "%s\n", error_msg.c_str());
192 PrintUsage();
193 return false;
194 }
195 }
196 }
197
198 DBG_LOG << "will call parse checks";
199
200 {
201 ParseStatus checks_status = ParseChecks(&error_msg);
202 if (checks_status != kParseOk) {
203 fprintf(stderr, "%s\n", error_msg.c_str());
204 PrintUsage();
205 return false;
206 }
207 }
208
209 return true;
210 }
211
212 virtual std::string GetUsage() const {
213 std::string usage;
214
215 usage += // Required.
216 " --boot-image=<file.art>: provide the image location for the boot class path.\n"
217 " Do not include the arch as part of the name, it is added automatically.\n"
218 " Example: --boot-image=/system/framework/boot.art\n"
Kevin Brodsky64fff412015-11-24 14:24:34 +0000219 " (specifies /system/framework/<arch>/boot.art as the image file)\n"
Igor Murashkin37743352014-11-13 14:38:00 -0800220 "\n";
Andreas Gampe46ee31b2016-12-14 10:11:49 -0800221 usage += android::base::StringPrintf( // Optional.
Andreas Gampe57b34292015-01-14 15:45:59 -0800222 " --instruction-set=(arm|arm64|mips|mips64|x86|x86_64): for locating the image\n"
Igor Murashkin37743352014-11-13 14:38:00 -0800223 " file based on the image location set.\n"
224 " Example: --instruction-set=x86\n"
225 " Default: %s\n"
226 "\n",
227 GetInstructionSetString(kRuntimeISA));
Vladimir Marko813b9142018-11-29 11:20:07 +0000228 usage +=
229 " --runtime-arg <argument> used to specify various arguments for the runtime\n"
230 " such as initial heap size, maximum heap size, and verbose output.\n"
231 " Use a separate --runtime-arg switch for each argument.\n"
232 " Example: --runtime-arg -Xms256m\n"
233 "\n";
Igor Murashkin37743352014-11-13 14:38:00 -0800234 usage += // Optional.
235 " --output=<file> may be used to send the output to a file.\n"
236 " Example: --output=/tmp/oatdump.txt\n"
237 "\n";
238
239 return usage;
240 }
241
242 // Specified by --boot-image.
243 const char* boot_image_location_ = nullptr;
244 // Specified by --instruction-set.
Andreas Gampe8fae4b52017-09-27 20:04:47 -0700245 InstructionSet instruction_set_ = InstructionSet::kNone;
Vladimir Marko813b9142018-11-29 11:20:07 +0000246 // Runtime arguments specified by --runtime-arg.
247 std::vector<const char*> runtime_args_;
Igor Murashkin37743352014-11-13 14:38:00 -0800248 // Specified by --output.
249 std::ostream* os_ = &std::cout;
250 std::unique_ptr<std::ofstream> out_; // If something besides cout is used
251 std::string output_name_;
252
253 virtual ~CmdlineArgs() {}
254
Andreas Gampec24f3992014-12-17 20:40:11 -0800255 bool ParseCheckBootImage(std::string* error_msg) {
Igor Murashkin37743352014-11-13 14:38:00 -0800256 if (boot_image_location_ == nullptr) {
257 *error_msg = "--boot-image must be specified";
Andreas Gampec24f3992014-12-17 20:40:11 -0800258 return false;
Igor Murashkin37743352014-11-13 14:38:00 -0800259 }
Andreas Gampe8fae4b52017-09-27 20:04:47 -0700260 if (instruction_set_ == InstructionSet::kNone) {
261 LOG(WARNING) << "No instruction set given, assuming " << GetInstructionSetString(kRuntimeISA);
262 instruction_set_ = kRuntimeISA;
263 }
Igor Murashkin37743352014-11-13 14:38:00 -0800264
265 DBG_LOG << "boot image location: " << boot_image_location_;
266
267 // Checks for --boot-image location.
268 {
269 std::string boot_image_location = boot_image_location_;
Andreas Gampeca620d72016-11-08 08:09:33 -0800270 size_t file_name_idx = boot_image_location.rfind('/');
Igor Murashkin37743352014-11-13 14:38:00 -0800271 if (file_name_idx == std::string::npos) { // Prevent a InsertIsaDirectory check failure.
272 *error_msg = "Boot image location must have a / in it";
Andreas Gampec24f3992014-12-17 20:40:11 -0800273 return false;
Igor Murashkin37743352014-11-13 14:38:00 -0800274 }
275
276 // Don't let image locations with the 'arch' in it through, since it's not a location.
277 // This prevents a common error "Could not create an image space..." when initing the Runtime.
278 if (file_name_idx != std::string::npos) {
279 std::string no_file_name = boot_image_location.substr(0, file_name_idx);
Andreas Gampeca620d72016-11-08 08:09:33 -0800280 size_t ancestor_dirs_idx = no_file_name.rfind('/');
Igor Murashkin37743352014-11-13 14:38:00 -0800281
282 std::string parent_dir_name;
283 if (ancestor_dirs_idx != std::string::npos) {
284 parent_dir_name = no_file_name.substr(ancestor_dirs_idx + 1);
285 } else {
286 parent_dir_name = no_file_name;
287 }
288
289 DBG_LOG << "boot_image_location parent_dir_name was " << parent_dir_name;
290
Vladimir Marko33bff252017-11-01 14:35:42 +0000291 if (GetInstructionSetFromString(parent_dir_name.c_str()) != InstructionSet::kNone) {
Igor Murashkin37743352014-11-13 14:38:00 -0800292 *error_msg = "Do not specify the architecture as part of the boot image location";
Andreas Gampec24f3992014-12-17 20:40:11 -0800293 return false;
Igor Murashkin37743352014-11-13 14:38:00 -0800294 }
295 }
296
297 // Check that the boot image location points to a valid file name.
298 std::string file_name;
299 if (!LocationToFilename(boot_image_location, instruction_set_, &file_name)) {
Andreas Gampe8fae4b52017-09-27 20:04:47 -0700300 *error_msg = android::base::StringPrintf(
301 "No corresponding file for location '%s' (filename '%s') exists",
302 boot_image_location.c_str(),
303 file_name.c_str());
Andreas Gampec24f3992014-12-17 20:40:11 -0800304 return false;
Igor Murashkin37743352014-11-13 14:38:00 -0800305 }
306
307 DBG_LOG << "boot_image_filename does exist: " << file_name;
308 }
309
Andreas Gampec24f3992014-12-17 20:40:11 -0800310 return true;
Igor Murashkin37743352014-11-13 14:38:00 -0800311 }
312
Igor Murashkin37743352014-11-13 14:38:00 -0800313 void PrintUsage() {
314 fprintf(stderr, "%s", GetUsage().c_str());
315 }
Andreas Gampec24f3992014-12-17 20:40:11 -0800316
317 protected:
318 virtual ParseStatus ParseCustom(const StringPiece& option ATTRIBUTE_UNUSED,
319 std::string* error_msg ATTRIBUTE_UNUSED) {
320 return kParseUnknownArgument;
321 }
322
323 virtual ParseStatus ParseChecks(std::string* error_msg ATTRIBUTE_UNUSED) {
324 return kParseOk;
325 }
Igor Murashkin37743352014-11-13 14:38:00 -0800326};
327
328template <typename Args = CmdlineArgs>
329struct CmdlineMain {
330 int Main(int argc, char** argv) {
David Sehrc431b9d2018-03-02 12:01:51 -0800331 Locks::Init();
Andreas Gampe51d80cc2017-06-21 21:05:13 -0700332 InitLogging(argv, Runtime::Abort);
Igor Murashkin37743352014-11-13 14:38:00 -0800333 std::unique_ptr<Args> args = std::unique_ptr<Args>(CreateArguments());
334 args_ = args.get();
335
336 DBG_LOG << "Try to parse";
337
338 if (args_ == nullptr || !args_->Parse(argc, argv)) {
339 return EXIT_FAILURE;
340 }
341
Igor Murashkin37743352014-11-13 14:38:00 -0800342 bool needs_runtime = NeedsRuntime();
Andreas Gampec24f3992014-12-17 20:40:11 -0800343 std::unique_ptr<Runtime> runtime;
344
Igor Murashkin37743352014-11-13 14:38:00 -0800345
346 if (needs_runtime) {
Andreas Gampec24f3992014-12-17 20:40:11 -0800347 std::string error_msg;
348 if (!args_->ParseCheckBootImage(&error_msg)) {
349 fprintf(stderr, "%s\n", error_msg.c_str());
350 args_->PrintUsage();
351 return EXIT_FAILURE;
352 }
353 runtime.reset(CreateRuntime(args.get()));
354 if (runtime == nullptr) {
355 return EXIT_FAILURE;
356 }
Igor Murashkin37743352014-11-13 14:38:00 -0800357 if (!ExecuteWithRuntime(runtime.get())) {
358 return EXIT_FAILURE;
359 }
360 } else {
361 if (!ExecuteWithoutRuntime()) {
362 return EXIT_FAILURE;
363 }
364 }
365
366 if (!ExecuteCommon()) {
367 return EXIT_FAILURE;
368 }
369
370 return EXIT_SUCCESS;
371 }
372
373 // Override this function to create your own arguments.
374 // Usually will want to return a subtype of CmdlineArgs.
375 virtual Args* CreateArguments() {
376 return new Args();
377 }
378
379 // Override this function to do something else with the runtime.
380 virtual bool ExecuteWithRuntime(Runtime* runtime) {
381 CHECK(runtime != nullptr);
382 // Do nothing
383 return true;
384 }
385
386 // Does the code execution need a runtime? Sometimes it doesn't.
387 virtual bool NeedsRuntime() {
388 return true;
389 }
390
391 // Do execution without having created a runtime.
392 virtual bool ExecuteWithoutRuntime() {
393 return true;
394 }
395
396 // Continue execution after ExecuteWith[out]Runtime
397 virtual bool ExecuteCommon() {
398 return true;
399 }
400
401 virtual ~CmdlineMain() {}
402
403 protected:
404 Args* args_ = nullptr;
405
406 private:
Andreas Gampec24f3992014-12-17 20:40:11 -0800407 Runtime* CreateRuntime(CmdlineArgs* args) {
Igor Murashkin37743352014-11-13 14:38:00 -0800408 CHECK(args != nullptr);
409
Vladimir Marko813b9142018-11-29 11:20:07 +0000410 return StartRuntime(args->boot_image_location_, args->instruction_set_, args_->runtime_args_);
Igor Murashkin37743352014-11-13 14:38:00 -0800411 }
412};
413} // namespace art
414
415#endif // ART_CMDLINE_CMDLINE_H_