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