1 /*
2  * Copyright (C) 2017 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 #include <string>
18 
19 #include "android-base/stringprintf.h"
20 #include "android-base/strings.h"
21 #include "compiler_filter.h"
22 #include "dex_file.h"
23 #include "noop_compiler_callbacks.h"
24 #include "oat_file_assistant.h"
25 #include "os.h"
26 #include "runtime.h"
27 #include "thread-inl.h"
28 #include "utils.h"
29 
30 namespace art {
31 
32 // See OatFileAssistant docs for the meaning of the valid return codes.
33 enum ReturnCodes {
34   kNoDexOptNeeded = 0,
35   kDex2OatFromScratch = 1,
36   kDex2OatForBootImageOat = 2,
37   kDex2OatForFilterOat = 3,
38   kDex2OatForRelocationOat = 4,
39   kDex2OatForBootImageOdex = 5,
40   kDex2OatForFilterOdex = 6,
41   kDex2OatForRelocationOdex = 7,
42 
43   kErrorInvalidArguments = 101,
44   kErrorCannotCreateRuntime = 102,
45   kErrorUnknownDexOptNeeded = 103
46 };
47 
48 static int original_argc;
49 static char** original_argv;
50 
CommandLine()51 static std::string CommandLine() {
52   std::vector<std::string> command;
53   for (int i = 0; i < original_argc; ++i) {
54     command.push_back(original_argv[i]);
55   }
56   return android::base::Join(command, ' ');
57 }
58 
UsageErrorV(const char * fmt,va_list ap)59 static void UsageErrorV(const char* fmt, va_list ap) {
60   std::string error;
61   android::base::StringAppendV(&error, fmt, ap);
62   LOG(ERROR) << error;
63 }
64 
UsageError(const char * fmt,...)65 static void UsageError(const char* fmt, ...) {
66   va_list ap;
67   va_start(ap, fmt);
68   UsageErrorV(fmt, ap);
69   va_end(ap);
70 }
71 
Usage(const char * fmt,...)72 NO_RETURN static void Usage(const char *fmt, ...) {
73   va_list ap;
74   va_start(ap, fmt);
75   UsageErrorV(fmt, ap);
76   va_end(ap);
77 
78   UsageError("Command: %s", CommandLine().c_str());
79   UsageError("  Performs a dexopt analysis on the given dex file and returns whether or not");
80   UsageError("  the dex file needs to be dexopted.");
81   UsageError("Usage: dexoptanalyzer [options]...");
82   UsageError("");
83   UsageError("  --dex-file=<filename>: the dex file which should be analyzed.");
84   UsageError("");
85   UsageError("  --isa=<string>: the instruction set for which the analysis should be performed.");
86   UsageError("");
87   UsageError("  --compiler-filter=<string>: the target compiler filter to be used as reference");
88   UsageError("       when deciding if the dex file needs to be optimized.");
89   UsageError("");
90   UsageError("  --assume-profile-changed: assumes the profile information has changed");
91   UsageError("       when deciding if the dex file needs to be optimized.");
92   UsageError("");
93   UsageError("  --image=<filename>: optional, the image to be used to decide if the associated");
94   UsageError("       oat file is up to date. Defaults to $ANDROID_ROOT/framework/boot.art.");
95   UsageError("       Example: --image=/system/framework/boot.art");
96   UsageError("");
97   UsageError("  --android-data=<directory>: optional, the directory which should be used as");
98   UsageError("       android-data. By default ANDROID_DATA env variable is used.");
99   UsageError("");
100   UsageError("Return code:");
101   UsageError("  To make it easier to integrate with the internal tools this command will make");
102   UsageError("    available its result (dexoptNeeded) as the exit/return code. i.e. it will not");
103   UsageError("    return 0 for success and a non zero values for errors as the conventional");
104   UsageError("    commands. The following return codes are possible:");
105   UsageError("        kNoDexOptNeeded = 0");
106   UsageError("        kDex2OatFromScratch = 1");
107   UsageError("        kDex2OatForBootImageOat = 2");
108   UsageError("        kDex2OatForFilterOat = 3");
109   UsageError("        kDex2OatForRelocationOat = 4");
110   UsageError("        kDex2OatForBootImageOdex = 5");
111   UsageError("        kDex2OatForFilterOdex = 6");
112   UsageError("        kDex2OatForRelocationOdex = 7");
113 
114   UsageError("        kErrorInvalidArguments = 101");
115   UsageError("        kErrorCannotCreateRuntime = 102");
116   UsageError("        kErrorUnknownDexOptNeeded = 103");
117   UsageError("");
118 
119   exit(kErrorInvalidArguments);
120 }
121 
122 class DexoptAnalyzer FINAL {
123  public:
DexoptAnalyzer()124   DexoptAnalyzer() : assume_profile_changed_(false) {}
125 
ParseArgs(int argc,char ** argv)126   void ParseArgs(int argc, char **argv) {
127     original_argc = argc;
128     original_argv = argv;
129 
130     InitLogging(argv, Runtime::Aborter);
131     // Skip over the command name.
132     argv++;
133     argc--;
134 
135     if (argc == 0) {
136       Usage("No arguments specified");
137     }
138 
139     for (int i = 0; i < argc; ++i) {
140       const StringPiece option(argv[i]);
141       if (option == "--assume-profile-changed") {
142         assume_profile_changed_ = true;
143       } else if (option.starts_with("--dex-file=")) {
144         dex_file_ = option.substr(strlen("--dex-file=")).ToString();
145       } else if (option.starts_with("--compiler-filter=")) {
146         std::string filter_str = option.substr(strlen("--compiler-filter=")).ToString();
147         if (!CompilerFilter::ParseCompilerFilter(filter_str.c_str(), &compiler_filter_)) {
148           Usage("Invalid compiler filter '%s'", option.data());
149         }
150       } else if (option.starts_with("--isa=")) {
151         std::string isa_str = option.substr(strlen("--isa=")).ToString();
152         isa_ = GetInstructionSetFromString(isa_str.c_str());
153         if (isa_ == kNone) {
154           Usage("Invalid isa '%s'", option.data());
155         }
156       } else if (option.starts_with("--image=")) {
157         image_ = option.substr(strlen("--image=")).ToString();
158       } else if (option.starts_with("--android-data=")) {
159         // Overwrite android-data if needed (oat file assistant relies on a valid directory to
160         // compute dalvik-cache folder). This is mostly used in tests.
161         std::string new_android_data = option.substr(strlen("--android-data=")).ToString();
162         setenv("ANDROID_DATA", new_android_data.c_str(), 1);
163       } else {
164         Usage("Unknown argument '%s'", option.data());
165       }
166     }
167 
168     if (image_.empty()) {
169       // If we don't receive the image, try to use the default one.
170       // Tests may specify a different image (e.g. core image).
171       std::string error_msg;
172       image_ = GetDefaultBootImageLocation(&error_msg);
173 
174       if (image_.empty()) {
175         LOG(ERROR) << error_msg;
176         Usage("--image unspecified and ANDROID_ROOT not set or image file does not exist.");
177       }
178     }
179   }
180 
CreateRuntime()181   bool CreateRuntime() {
182     RuntimeOptions options;
183     // The image could be custom, so make sure we explicitly pass it.
184     std::string img = "-Ximage:" + image_;
185     options.push_back(std::make_pair(img.c_str(), nullptr));
186     // The instruction set of the image should match the instruction set we will test.
187     const void* isa_opt = reinterpret_cast<const void*>(GetInstructionSetString(isa_));
188     options.push_back(std::make_pair("imageinstructionset", isa_opt));
189      // Disable libsigchain. We don't don't need it to evaluate DexOptNeeded status.
190     options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
191     // Pretend we are a compiler so that we can re-use the same infrastructure to load a different
192     // ISA image and minimize the amount of things that get started.
193     NoopCompilerCallbacks callbacks;
194     options.push_back(std::make_pair("compilercallbacks", &callbacks));
195     // Make sure we don't attempt to relocate. The tool should only retrieve the DexOptNeeded
196     // status and not attempt to relocate the boot image.
197     options.push_back(std::make_pair("-Xnorelocate", nullptr));
198 
199     if (!Runtime::Create(options, false)) {
200       LOG(ERROR) << "Unable to initialize runtime";
201       return false;
202     }
203     // Runtime::Create acquired the mutator_lock_ that is normally given away when we
204     // Runtime::Start. Give it away now.
205     Thread::Current()->TransitionFromRunnableToSuspended(kNative);
206 
207     return true;
208   }
209 
GetDexOptNeeded()210   int GetDexOptNeeded() {
211     // If the file does not exist there's nothing to do.
212     // This is a fast path to avoid creating the runtime (b/34385298).
213     if (!OS::FileExists(dex_file_.c_str())) {
214       return kNoDexOptNeeded;
215     }
216     if (!CreateRuntime()) {
217       return kErrorCannotCreateRuntime;
218     }
219     OatFileAssistant oat_file_assistant(dex_file_.c_str(), isa_, /*load_executable*/ false);
220     // Always treat elements of the bootclasspath as up-to-date.
221     // TODO(calin): this check should be in OatFileAssistant.
222     if (oat_file_assistant.IsInBootClassPath()) {
223       return kNoDexOptNeeded;
224     }
225     int dexoptNeeded = oat_file_assistant.GetDexOptNeeded(
226         compiler_filter_, assume_profile_changed_);
227 
228     // Convert OatFileAssitant codes to dexoptanalyzer codes.
229     switch (dexoptNeeded) {
230       case OatFileAssistant::kNoDexOptNeeded: return kNoDexOptNeeded;
231       case OatFileAssistant::kDex2OatFromScratch: return kDex2OatFromScratch;
232       case OatFileAssistant::kDex2OatForBootImage: return kDex2OatForBootImageOat;
233       case OatFileAssistant::kDex2OatForFilter: return kDex2OatForFilterOat;
234       case OatFileAssistant::kDex2OatForRelocation: return kDex2OatForRelocationOat;
235 
236       case -OatFileAssistant::kDex2OatForBootImage: return kDex2OatForBootImageOdex;
237       case -OatFileAssistant::kDex2OatForFilter: return kDex2OatForFilterOdex;
238       case -OatFileAssistant::kDex2OatForRelocation: return kDex2OatForRelocationOdex;
239       default:
240         LOG(ERROR) << "Unknown dexoptNeeded " << dexoptNeeded;
241         return kErrorUnknownDexOptNeeded;
242     }
243   }
244 
245  private:
246   std::string dex_file_;
247   InstructionSet isa_;
248   CompilerFilter::Filter compiler_filter_;
249   bool assume_profile_changed_;
250   std::string image_;
251 };
252 
dexoptAnalyze(int argc,char ** argv)253 static int dexoptAnalyze(int argc, char** argv) {
254   DexoptAnalyzer analyzer;
255 
256   // Parse arguments. Argument mistakes will lead to exit(kErrorInvalidArguments) in UsageError.
257   analyzer.ParseArgs(argc, argv);
258   return analyzer.GetDexOptNeeded();
259 }
260 
261 }  // namespace art
262 
main(int argc,char ** argv)263 int main(int argc, char **argv) {
264   return art::dexoptAnalyze(argc, argv);
265 }
266