• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <memory>
26 #include <string>
27 #include <string_view>
28 #include <vector>
29 
30 #include "android-base/stringprintf.h"
31 #include "android-base/strings.h"
32 #include "base/file_utils.h"
33 #include "base/logging.h"
34 #include "base/mutex.h"
35 #include "base/utils.h"
36 #include "noop_compiler_callbacks.h"
37 #include "oat/oat_file_assistant_context.h"
38 #include "runtime.h"
39 
40 #if !defined(NDEBUG)
41 #define DBG_LOG LOG(INFO)
42 #else
43 #define DBG_LOG LOG(DEBUG)
44 #endif
45 
46 namespace art {
47 
StartRuntime(const std::vector<std::string> & boot_image_locations,InstructionSet instruction_set,const std::vector<const char * > & runtime_args)48 static Runtime* StartRuntime(const std::vector<std::string>& boot_image_locations,
49                              InstructionSet instruction_set,
50                              const std::vector<const char*>& runtime_args) {
51   RuntimeOptions options;
52 
53   // We are more like a compiler than a run-time. We don't want to execute code.
54   {
55     static NoopCompilerCallbacks callbacks;
56     options.push_back(std::make_pair("compilercallbacks", &callbacks));
57   }
58 
59   // Boot image location.
60   {
61     std::string boot_image_option = "-Ximage:";
62     if (!boot_image_locations.empty()) {
63       boot_image_option += android::base::Join(boot_image_locations, ':');
64     } else {
65       boot_image_option += GetJitZygoteBootImageLocation();
66     }
67     options.push_back(std::make_pair(boot_image_option, nullptr));
68   }
69 
70   // Instruction set.
71   options.push_back(
72       std::make_pair("imageinstructionset",
73                      reinterpret_cast<const void*>(GetInstructionSetString(instruction_set))));
74 
75   // Explicit runtime args.
76   for (const char* runtime_arg : runtime_args) {
77     options.push_back(std::make_pair(runtime_arg, nullptr));
78   }
79 
80   // None of the command line tools need sig chain. If this changes we'll need
81   // to upgrade this option to a proper parameter.
82   options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
83   if (!Runtime::Create(options, false)) {
84     fprintf(stderr, "Failed to create runtime\n");
85     return nullptr;
86   }
87 
88   // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start,
89   // give it away now and then switch to a more manageable ScopedObjectAccess.
90   Thread::Current()->TransitionFromRunnableToSuspended(ThreadState::kNative);
91 
92   return Runtime::Current();
93 }
94 
95 struct CmdlineArgs {
96   enum ParseStatus {
97     kParseOk,               // Parse successful. Do not set the error message.
98     kParseUnknownArgument,  // Unknown argument. Do not set the error message.
99     kParseError,            // Parse ok, but failed elsewhere. Print the set error message.
100   };
101 
ParseCmdlineArgs102   bool Parse(int argc, char** argv) {
103     // Skip over argv[0].
104     argv++;
105     argc--;
106 
107     if (argc == 0) {
108       fprintf(stderr, "No arguments specified\n");
109       PrintUsage();
110       return false;
111     }
112 
113     std::string error_msg;
114     for (int i = 0; i < argc; i++) {
115       const char* const raw_option = argv[i];
116       const std::string_view option(raw_option);
117       if (option.starts_with("--boot-image=")) {
118         Split(raw_option + strlen("--boot-image="), ':', &boot_image_locations_);
119       } else if (option.starts_with("--instruction-set=")) {
120         const char* const instruction_set_str = raw_option + strlen("--instruction-set=");
121         instruction_set_ = GetInstructionSetFromString(instruction_set_str);
122         if (instruction_set_ == InstructionSet::kNone) {
123           fprintf(stderr, "Unsupported instruction set %s\n", instruction_set_str);
124           PrintUsage();
125           return false;
126         }
127       } else if (option == "--runtime-arg") {
128         if (i + 1 == argc) {
129           fprintf(stderr, "Missing argument for --runtime-arg\n");
130           PrintUsage();
131           return false;
132         }
133         ++i;
134         runtime_args_.push_back(argv[i]);
135       } else if (option.starts_with("--output=")) {
136         output_name_ = std::string(option.substr(strlen("--output=")));
137         const char* filename = output_name_.c_str();
138         out_.reset(new std::ofstream(filename));
139         if (!out_->good()) {
140           fprintf(stderr, "Failed to open output filename %s\n", filename);
141           PrintUsage();
142           return false;
143         }
144         os_ = out_.get();
145       } else {
146         ParseStatus parse_status = ParseCustom(raw_option, option.length(), &error_msg);
147 
148         if (parse_status == kParseUnknownArgument) {
149           fprintf(stderr, "Unknown argument %s\n", option.data());
150         }
151 
152         if (parse_status != kParseOk) {
153           fprintf(stderr, "%s\n", error_msg.c_str());
154           PrintUsage();
155           return false;
156         }
157       }
158     }
159 
160     if (instruction_set_ == InstructionSet::kNone) {
161       LOG(WARNING) << "No instruction set given, assuming " << GetInstructionSetString(kRuntimeISA);
162       instruction_set_ = kRuntimeISA;
163     }
164 
165     DBG_LOG << "will call parse checks";
166 
167     {
168       ParseStatus checks_status = ParseChecks(&error_msg);
169       if (checks_status != kParseOk) {
170           fprintf(stderr, "%s\n", error_msg.c_str());
171           PrintUsage();
172           return false;
173       }
174     }
175 
176     return true;
177   }
178 
GetUsageCmdlineArgs179   virtual std::string GetUsage() const {
180     std::string usage;
181 
182     usage +=  // Required.
183         "  --boot-image=<file.art>: provide the image location for the boot class path.\n"
184         "      Do not include the arch as part of the name, it is added automatically.\n"
185         "      Example: --boot-image=/system/framework/boot.art\n"
186         "               (specifies /system/framework/<arch>/boot.art as the image file)\n"
187         "\n";
188     usage += android::base::StringPrintf(  // Optional.
189         "  --instruction-set=(arm|arm64|x86|x86_64): for locating the image\n"
190         "      file based on the image location set.\n"
191         "      Example: --instruction-set=x86\n"
192         "      Default: %s\n"
193         "\n",
194         GetInstructionSetString(kRuntimeISA));
195     usage +=
196         "  --runtime-arg <argument> used to specify various arguments for the runtime\n"
197         "      such as initial heap size, maximum heap size, and verbose output.\n"
198         "      Use a separate --runtime-arg switch for each argument.\n"
199         "      Example: --runtime-arg -Xms256m\n"
200         "\n";
201     usage +=  // Optional.
202         "  --output=<file> may be used to send the output to a file.\n"
203         "      Example: --output=/tmp/oatdump.txt\n"
204         "\n";
205 
206     return usage;
207   }
208 
209   // Specified by --runtime-arg -Xbootclasspath or default.
210   std::vector<std::string> boot_class_path_;
211   // Specified by --runtime-arg -Xbootclasspath-locations or default.
212   std::vector<std::string> boot_class_path_locations_;
213   // True if `boot_class_path_` is the default one.
214   bool is_default_boot_class_path_ = false;
215   // Specified by --boot-image or inferred.
216   std::vector<std::string> boot_image_locations_;
217   // Specified by --instruction-set.
218   InstructionSet instruction_set_ = InstructionSet::kNone;
219   // Runtime arguments specified by --runtime-arg.
220   std::vector<const char*> runtime_args_;
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 
~CmdlineArgsCmdlineArgs226   virtual ~CmdlineArgs() {}
227 
228   // Checks for --boot-image location.
ParseCheckBootImageCmdlineArgs229   bool ParseCheckBootImage(std::string* error_msg) {
230     if (boot_image_locations_.empty()) {
231       LOG(WARNING) << "--boot-image not specified. Starting runtime in imageless mode";
232       return true;
233     }
234 
235     const std::string& boot_image_location = boot_image_locations_[0];
236     size_t file_name_idx = boot_image_location.rfind('/');
237     if (file_name_idx == std::string::npos) {  // Prevent a InsertIsaDirectory check failure.
238       *error_msg = "Boot image location must have a / in it";
239       return false;
240     }
241 
242     // Don't let image locations with the 'arch' in it through, since it's not a location.
243     // This prevents a common error "Could not create an image space..." when initing the Runtime.
244     if (file_name_idx > 0) {
245       size_t ancestor_dirs_idx = boot_image_location.rfind('/', file_name_idx - 1);
246 
247       std::string parent_dir_name;
248       if (ancestor_dirs_idx != std::string::npos) {
249           parent_dir_name = boot_image_location.substr(/*pos=*/ancestor_dirs_idx + 1,
250                                                        /*n=*/file_name_idx - ancestor_dirs_idx - 1);
251       } else {
252           parent_dir_name = boot_image_location.substr(/*pos=*/0,
253                                                        /*n=*/file_name_idx);
254       }
255 
256       DBG_LOG << "boot_image_location parent_dir_name was " << parent_dir_name;
257 
258       if (GetInstructionSetFromString(parent_dir_name.c_str()) != InstructionSet::kNone) {
259           *error_msg = "Do not specify the architecture as part of the boot image location";
260           return false;
261       }
262     }
263 
264     return true;
265   }
266 
PrintUsageCmdlineArgs267   void PrintUsage() { fprintf(stderr, "%s", GetUsage().c_str()); }
268 
GetOatFileAssistantContextCmdlineArgs269   std::unique_ptr<OatFileAssistantContext> GetOatFileAssistantContext(std::string* error_msg) {
270     if (boot_class_path_.empty()) {
271       *error_msg = "Boot classpath is empty";
272       return nullptr;
273     }
274 
275     CHECK(!boot_class_path_locations_.empty());
276 
277     return std::make_unique<OatFileAssistantContext>(
278         std::make_unique<OatFileAssistantContext::RuntimeOptions>(
279             OatFileAssistantContext::RuntimeOptions{
280                 .image_locations = boot_image_locations_,
281                 .boot_class_path = boot_class_path_,
282                 .boot_class_path_locations = boot_class_path_locations_,
283             }));
284   }
285 
286  protected:
ParseCustomCmdlineArgs287   virtual ParseStatus ParseCustom([[maybe_unused]] const char* raw_option,
288                                   [[maybe_unused]] size_t raw_option_length,
289                                   [[maybe_unused]] std::string* error_msg) {
290     return kParseUnknownArgument;
291   }
292 
ParseChecksCmdlineArgs293   virtual ParseStatus ParseChecks([[maybe_unused]] std::string* error_msg) {
294     ParseBootclasspath();
295     if (boot_image_locations_.empty()) {
296       InferBootImage();
297     }
298     return kParseOk;
299   }
300 
301  private:
ParseBootclasspathCmdlineArgs302   void ParseBootclasspath() {
303     std::optional<std::string_view> bcp_str = std::nullopt;
304     std::optional<std::string_view> bcp_location_str = std::nullopt;
305     for (std::string_view arg : runtime_args_) {
306       if (arg.starts_with("-Xbootclasspath:")) {
307           bcp_str = arg.substr(strlen("-Xbootclasspath:"));
308       }
309       if (arg.starts_with("-Xbootclasspath-locations:")) {
310           bcp_location_str = arg.substr(strlen("-Xbootclasspath-locations:"));
311       }
312     }
313 
314     if (bcp_str.has_value() && bcp_location_str.has_value()) {
315       Split(*bcp_str, ':', &boot_class_path_);
316       Split(*bcp_location_str, ':', &boot_class_path_locations_);
317     } else if (bcp_str.has_value()) {
318       Split(*bcp_str, ':', &boot_class_path_);
319       boot_class_path_locations_ = boot_class_path_;
320     } else {
321       // Try the default.
322       const char* env_value = getenv("BOOTCLASSPATH");
323       if (env_value != nullptr && strlen(env_value) > 0) {
324           Split(env_value, ':', &boot_class_path_);
325           boot_class_path_locations_ = boot_class_path_;
326           is_default_boot_class_path_ = true;
327       }
328     }
329   }
330 
331   // Infers the boot image on a best-effort basis.
332   // The inference logic aligns with installd/artd + dex2oat.
InferBootImageCmdlineArgs333   void InferBootImage() {
334     // The boot image inference only makes sense on device.
335     if (!kIsTargetAndroid) {
336       return;
337     }
338 
339     // The inferred boot image can only be used with the default bootclasspath.
340     if (boot_class_path_.empty() || !is_default_boot_class_path_) {
341       return;
342     }
343 
344     std::string error_msg;
345     std::string boot_image = GetBootImageLocationForDefaultBcpRespectingSysProps(&error_msg);
346     if (boot_image.empty()) {
347       LOG(WARNING) << "Failed to infer boot image: " << error_msg;
348       return;
349     }
350 
351     LOG(INFO) << "Inferred boot image: " << boot_image;
352     Split(boot_image, ':', &boot_image_locations_);
353 
354     // Verify the inferred boot image.
355     std::unique_ptr<OatFileAssistantContext> ofa_context = GetOatFileAssistantContext(&error_msg);
356     CHECK_NE(ofa_context, nullptr);
357     size_t verified_boot_image_count = ofa_context->GetBootImageInfoList(instruction_set_).size();
358     if (verified_boot_image_count != boot_image_locations_.size()) {
359       LOG(WARNING) << "Failed to verify inferred boot image";
360       boot_image_locations_.resize(verified_boot_image_count);
361     }
362   }
363 };
364 
365 template <typename Args = CmdlineArgs>
366 struct CmdlineMain {
MainCmdlineMain367   int Main(int argc, char** argv) {
368     Locks::Init();
369     InitLogging(argv, Runtime::Abort);
370     std::unique_ptr<Args> args = std::unique_ptr<Args>(CreateArguments());
371     args_ = args.get();
372 
373     DBG_LOG << "Try to parse";
374 
375     if (args_ == nullptr || !args_->Parse(argc, argv)) {
376       return EXIT_FAILURE;
377     }
378 
379     bool needs_runtime = NeedsRuntime();
380     std::unique_ptr<Runtime> runtime;
381 
382 
383     if (needs_runtime) {
384       std::string error_msg;
385       if (!args_->ParseCheckBootImage(&error_msg)) {
386         fprintf(stderr, "%s\n", error_msg.c_str());
387         args_->PrintUsage();
388         return EXIT_FAILURE;
389       }
390       runtime.reset(CreateRuntime(args.get()));
391       if (runtime == nullptr) {
392         return EXIT_FAILURE;
393       }
394       if (!ExecuteWithRuntime(runtime.get())) {
395         return EXIT_FAILURE;
396       }
397     } else {
398       if (!ExecuteWithoutRuntime()) {
399         return EXIT_FAILURE;
400       }
401     }
402 
403     if (!ExecuteCommon()) {
404       return EXIT_FAILURE;
405     }
406 
407     return EXIT_SUCCESS;
408   }
409 
410   // Override this function to create your own arguments.
411   // Usually will want to return a subtype of CmdlineArgs.
CreateArgumentsCmdlineMain412   virtual Args* CreateArguments() {
413     return new Args();
414   }
415 
416   // Override this function to do something else with the runtime.
ExecuteWithRuntimeCmdlineMain417   virtual bool ExecuteWithRuntime(Runtime* runtime) {
418     CHECK(runtime != nullptr);
419     // Do nothing
420     return true;
421   }
422 
423   // Does the code execution need a runtime? Sometimes it doesn't.
NeedsRuntimeCmdlineMain424   virtual bool NeedsRuntime() {
425     return true;
426   }
427 
428   // Do execution without having created a runtime.
ExecuteWithoutRuntimeCmdlineMain429   virtual bool ExecuteWithoutRuntime() {
430     return true;
431   }
432 
433   // Continue execution after ExecuteWith[out]Runtime
ExecuteCommonCmdlineMain434   virtual bool ExecuteCommon() {
435     return true;
436   }
437 
~CmdlineMainCmdlineMain438   virtual ~CmdlineMain() {}
439 
440  protected:
441   Args* args_ = nullptr;
442 
443  private:
CreateRuntimeCmdlineMain444   Runtime* CreateRuntime(CmdlineArgs* args) {
445     CHECK(args != nullptr);
446 
447     return StartRuntime(args->boot_image_locations_, args->instruction_set_, args_->runtime_args_);
448   }
449 };
450 }  // namespace art
451 
452 #endif  // ART_CMDLINE_CMDLINE_H_
453