summaryrefslogtreecommitdiff
path: root/dexoptanalyzer/dexoptanalyzer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'dexoptanalyzer/dexoptanalyzer.cc')
-rw-r--r--dexoptanalyzer/dexoptanalyzer.cc265
1 files changed, 265 insertions, 0 deletions
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
new file mode 100644
index 0000000000..965e4073ea
--- /dev/null
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
+#include "compiler_filter.h"
+#include "dex_file.h"
+#include "noop_compiler_callbacks.h"
+#include "oat_file_assistant.h"
+#include "os.h"
+#include "runtime.h"
+#include "thread-inl.h"
+#include "utils.h"
+
+namespace art {
+
+// See OatFileAssistant docs for the meaning of the valid return codes.
+enum ReturnCodes {
+ kNoDexOptNeeded = 0,
+ kDex2OatFromScratch = 1,
+ kDex2OatForBootImageOat = 2,
+ kDex2OatForFilterOat = 3,
+ kDex2OatForRelocationOat = 4,
+ kDex2OatForBootImageOdex = 5,
+ kDex2OatForFilterOdex = 6,
+ kDex2OatForRelocationOdex = 7,
+
+ kErrorInvalidArguments = 101,
+ kErrorCannotCreateRuntime = 102,
+ kErrorUnknownDexOptNeeded = 103
+};
+
+static int original_argc;
+static char** original_argv;
+
+static std::string CommandLine() {
+ std::vector<std::string> command;
+ for (int i = 0; i < original_argc; ++i) {
+ command.push_back(original_argv[i]);
+ }
+ return android::base::Join(command, ' ');
+}
+
+static void UsageErrorV(const char* fmt, va_list ap) {
+ std::string error;
+ android::base::StringAppendV(&error, fmt, ap);
+ LOG(ERROR) << error;
+}
+
+static void UsageError(const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ UsageErrorV(fmt, ap);
+ va_end(ap);
+}
+
+NO_RETURN static void Usage(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ UsageErrorV(fmt, ap);
+ va_end(ap);
+
+ UsageError("Command: %s", CommandLine().c_str());
+ UsageError(" Performs a dexopt analysis on the given dex file and returns whether or not");
+ UsageError(" the dex file needs to be dexopted.");
+ UsageError("Usage: dexoptanalyzer [options]...");
+ UsageError("");
+ UsageError(" --dex-file=<filename>: the dex file which should be analyzed.");
+ UsageError("");
+ UsageError(" --isa=<string>: the instruction set for which the analysis should be performed.");
+ UsageError("");
+ UsageError(" --compiler-filter=<string>: the target compiler filter to be used as reference");
+ UsageError(" when deciding if the dex file needs to be optimized.");
+ UsageError("");
+ UsageError(" --assume-profile-changed: assumes the profile information has changed");
+ UsageError(" when deciding if the dex file needs to be optimized.");
+ UsageError("");
+ UsageError(" --image=<filename>: optional, the image to be used to decide if the associated");
+ UsageError(" oat file is up to date. Defaults to $ANDROID_ROOT/framework/boot.art.");
+ UsageError(" Example: --image=/system/framework/boot.art");
+ UsageError("");
+ UsageError(" --android-data=<directory>: optional, the directory which should be used as");
+ UsageError(" android-data. By default ANDROID_DATA env variable is used.");
+ UsageError("");
+ UsageError("Return code:");
+ UsageError(" To make it easier to integrate with the internal tools this command will make");
+ UsageError(" available its result (dexoptNeeded) as the exit/return code. i.e. it will not");
+ UsageError(" return 0 for success and a non zero values for errors as the conventional");
+ UsageError(" commands. The following return codes are possible:");
+ UsageError(" kNoDexOptNeeded = 0");
+ UsageError(" kDex2OatFromScratch = 1");
+ UsageError(" kDex2OatForBootImageOat = 2");
+ UsageError(" kDex2OatForFilterOat = 3");
+ UsageError(" kDex2OatForRelocationOat = 4");
+ UsageError(" kDex2OatForBootImageOdex = 5");
+ UsageError(" kDex2OatForFilterOdex = 6");
+ UsageError(" kDex2OatForRelocationOdex = 7");
+
+ UsageError(" kErrorInvalidArguments = 101");
+ UsageError(" kErrorCannotCreateRuntime = 102");
+ UsageError(" kErrorUnknownDexOptNeeded = 103");
+ UsageError("");
+
+ exit(kErrorInvalidArguments);
+}
+
+class DexoptAnalyzer FINAL {
+ public:
+ DexoptAnalyzer() : assume_profile_changed_(false) {}
+
+ void ParseArgs(int argc, char **argv) {
+ original_argc = argc;
+ original_argv = argv;
+
+ InitLogging(argv, Runtime::Aborter);
+ // Skip over the command name.
+ argv++;
+ argc--;
+
+ if (argc == 0) {
+ Usage("No arguments specified");
+ }
+
+ for (int i = 0; i < argc; ++i) {
+ const StringPiece option(argv[i]);
+ if (option == "--assume-profile-changed") {
+ assume_profile_changed_ = true;
+ } else if (option.starts_with("--dex-file=")) {
+ dex_file_ = option.substr(strlen("--dex-file=")).ToString();
+ } else if (option.starts_with("--compiler-filter=")) {
+ std::string filter_str = option.substr(strlen("--compiler-filter=")).ToString();
+ if (!CompilerFilter::ParseCompilerFilter(filter_str.c_str(), &compiler_filter_)) {
+ Usage("Invalid compiler filter '%s'", option.data());
+ }
+ } else if (option.starts_with("--isa=")) {
+ std::string isa_str = option.substr(strlen("--isa=")).ToString();
+ isa_ = GetInstructionSetFromString(isa_str.c_str());
+ if (isa_ == kNone) {
+ Usage("Invalid isa '%s'", option.data());
+ }
+ } else if (option.starts_with("--image=")) {
+ image_ = option.substr(strlen("--image=")).ToString();
+ } else if (option.starts_with("--android-data=")) {
+ // Overwrite android-data if needed (oat file assistant relies on a valid directory to
+ // compute dalvik-cache folder). This is mostly used in tests.
+ std::string new_android_data = option.substr(strlen("--android-data=")).ToString();
+ setenv("ANDROID_DATA", new_android_data.c_str(), 1);
+ } else {
+ Usage("Unknown argument '%s'", option.data());
+ }
+ }
+
+ if (image_.empty()) {
+ // If we don't receive the image, try to use the default one.
+ // Tests may specify a different image (e.g. core image).
+ std::string error_msg;
+ image_ = GetDefaultBootImageLocation(&error_msg);
+
+ if (image_.empty()) {
+ LOG(ERROR) << error_msg;
+ Usage("--image unspecified and ANDROID_ROOT not set or image file does not exist.");
+ }
+ }
+ }
+
+ bool CreateRuntime() {
+ RuntimeOptions options;
+ // The image could be custom, so make sure we explicitly pass it.
+ std::string img = "-Ximage:" + image_;
+ options.push_back(std::make_pair(img.c_str(), nullptr));
+ // The instruction set of the image should match the instruction set we will test.
+ const void* isa_opt = reinterpret_cast<const void*>(GetInstructionSetString(isa_));
+ options.push_back(std::make_pair("imageinstructionset", isa_opt));
+ // Disable libsigchain. We don't don't need it to evaluate DexOptNeeded status.
+ options.push_back(std::make_pair("-Xno-sig-chain", nullptr));
+ // Pretend we are a compiler so that we can re-use the same infrastructure to load a different
+ // ISA image and minimize the amount of things that get started.
+ NoopCompilerCallbacks callbacks;
+ options.push_back(std::make_pair("compilercallbacks", &callbacks));
+ // Make sure we don't attempt to relocate. The tool should only retrieve the DexOptNeeded
+ // status and not attempt to relocate the boot image.
+ options.push_back(std::make_pair("-Xnorelocate", nullptr));
+
+ if (!Runtime::Create(options, false)) {
+ LOG(ERROR) << "Unable to initialize runtime";
+ return false;
+ }
+ // Runtime::Create acquired the mutator_lock_ that is normally given away when we
+ // Runtime::Start. Give it away now.
+ Thread::Current()->TransitionFromRunnableToSuspended(kNative);
+
+ return true;
+ }
+
+ int GetDexOptNeeded() {
+ // If the file does not exist there's nothing to do.
+ // This is a fast path to avoid creating the runtime (b/34385298).
+ if (!OS::FileExists(dex_file_.c_str())) {
+ return kNoDexOptNeeded;
+ }
+ if (!CreateRuntime()) {
+ return kErrorCannotCreateRuntime;
+ }
+ OatFileAssistant oat_file_assistant(dex_file_.c_str(), isa_, /*load_executable*/ false);
+ // Always treat elements of the bootclasspath as up-to-date.
+ // TODO(calin): this check should be in OatFileAssistant.
+ if (oat_file_assistant.IsInBootClassPath()) {
+ return kNoDexOptNeeded;
+ }
+ int dexoptNeeded = oat_file_assistant.GetDexOptNeeded(
+ compiler_filter_, assume_profile_changed_);
+
+ // Convert OatFileAssitant codes to dexoptanalyzer codes.
+ switch (dexoptNeeded) {
+ case OatFileAssistant::kNoDexOptNeeded: return kNoDexOptNeeded;
+ case OatFileAssistant::kDex2OatFromScratch: return kDex2OatFromScratch;
+ case OatFileAssistant::kDex2OatForBootImage: return kDex2OatForBootImageOat;
+ case OatFileAssistant::kDex2OatForFilter: return kDex2OatForFilterOat;
+ case OatFileAssistant::kDex2OatForRelocation: return kDex2OatForRelocationOat;
+
+ case -OatFileAssistant::kDex2OatForBootImage: return kDex2OatForBootImageOdex;
+ case -OatFileAssistant::kDex2OatForFilter: return kDex2OatForFilterOdex;
+ case -OatFileAssistant::kDex2OatForRelocation: return kDex2OatForRelocationOdex;
+ default:
+ LOG(ERROR) << "Unknown dexoptNeeded " << dexoptNeeded;
+ return kErrorUnknownDexOptNeeded;
+ }
+ }
+
+ private:
+ std::string dex_file_;
+ InstructionSet isa_;
+ CompilerFilter::Filter compiler_filter_;
+ bool assume_profile_changed_;
+ std::string image_;
+};
+
+static int dexoptAnalyze(int argc, char** argv) {
+ DexoptAnalyzer analyzer;
+
+ // Parse arguments. Argument mistakes will lead to exit(kErrorInvalidArguments) in UsageError.
+ analyzer.ParseArgs(argc, argv);
+ return analyzer.GetDexOptNeeded();
+}
+
+} // namespace art
+
+int main(int argc, char **argv) {
+ return art::dexoptAnalyze(argc, argv);
+}