Merge "Add dexoptanalyzer tool"
diff --git a/Android.bp b/Android.bp
index b9f1db5..d0e22fb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,6 +27,7 @@
"dexdump",
"dexlayout",
"dexlist",
+ "dexoptanalyzer",
"disassembler",
"imgdiag",
"oatdump",
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index e568ce2..6de5aef 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -109,6 +109,7 @@
ART_CORE_DEBUGGABLE_EXECUTABLES := \
dex2oat \
+ dexoptanalyzer \
imgdiag \
oatdump \
patchoat \
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 0aedf71..bc08384 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -101,6 +101,7 @@
ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
+ART_GTEST_dexoptanalyzer_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS)
ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
ART_GTEST_oat_test_DEX_DEPS := Main
ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
@@ -137,6 +138,12 @@
ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
+ART_GTEST_dexoptanalyzer_test_HOST_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) \
+ $(HOST_OUT_EXECUTABLES)/dexoptanalyzerd
+ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \
+ dexoptanalyzerd
ART_GTEST_dex2oat_test_HOST_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
@@ -220,6 +227,7 @@
art_dexdump_tests \
art_dexlayout_tests \
art_dexlist_tests \
+ art_dexoptanalyzer_tests \
art_imgdiag_tests \
art_oatdump_tests \
art_profman_tests \
@@ -615,6 +623,9 @@
ART_GTEST_oat_file_assistant_test_DEX_DEPS :=
ART_GTEST_oat_file_assistant_test_HOST_DEPS :=
ART_GTEST_oat_file_assistant_test_TARGET_DEPS :=
+ART_GTEST_dexoptanalyzer_test_DEX_DEPS :=
+ART_GTEST_dexoptanalyzer_test_HOST_DEPS :=
+ART_GTEST_dexoptanalyzer_test_TARGET_DEPS :=
ART_GTEST_dex2oat_test_DEX_DEPS :=
ART_GTEST_dex2oat_test_HOST_DEPS :=
ART_GTEST_dex2oat_test_TARGET_DEPS :=
diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp
new file mode 100644
index 0000000..cf4c99e
--- /dev/null
+++ b/dexoptanalyzer/Android.bp
@@ -0,0 +1,68 @@
+//
+// 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.
+//
+
+cc_defaults {
+ name: "dexoptanalyzer-defaults",
+ host_supported: true,
+ defaults: ["art_defaults"],
+ srcs: [
+ "dexoptanalyzer.cc",
+ ],
+
+ target: {
+ android: {
+ compile_multilib: "prefer32",
+ },
+ },
+
+ include_dirs: [
+ "art/cmdline",
+ ],
+
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+art_cc_binary {
+ name: "dexoptanalyzer",
+ defaults: ["dexoptanalyzer-defaults"],
+ shared_libs: [
+ "libart",
+ ],
+}
+
+art_cc_binary {
+ name: "dexoptanalyzerd",
+ defaults: [
+ "dexoptanalyzer-defaults",
+ "art_debug_defaults",
+ ],
+ shared_libs: [
+ "libartd",
+ ],
+}
+
+art_cc_test {
+ name: "art_dexoptanalyzer_tests",
+ defaults: [
+ "art_gtest_defaults",
+ ],
+ shared_libs: [
+ "libbacktrace"
+ ],
+ srcs: ["dexoptanalyzer_test.cc"],
+}
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
new file mode 100644
index 0000000..965e407
--- /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);
+}
diff --git a/dexoptanalyzer/dexoptanalyzer_test.cc b/dexoptanalyzer/dexoptanalyzer_test.cc
new file mode 100644
index 0000000..57d3f1f
--- /dev/null
+++ b/dexoptanalyzer/dexoptanalyzer_test.cc
@@ -0,0 +1,311 @@
+/*
+ * 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 <gtest/gtest.h>
+
+#include "arch/instruction_set.h"
+#include "compiler_filter.h"
+#include "dexopt_test.h"
+
+namespace art {
+
+class DexoptAnalyzerTest : public DexoptTest {
+ protected:
+ std::string GetDexoptAnalyzerCmd() {
+ std::string file_path = GetTestAndroidRoot();
+ file_path += "/bin/dexoptanalyzer";
+ if (kIsDebugBuild) {
+ file_path += "d";
+ }
+ EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
+ return file_path;
+ }
+
+ int Analyze(const std::string& dex_file,
+ CompilerFilter::Filter compiler_filter,
+ bool assume_profile_changed) {
+ std::string dexoptanalyzer_cmd = GetDexoptAnalyzerCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(dexoptanalyzer_cmd);
+ argv_str.push_back("--dex-file=" + dex_file);
+ argv_str.push_back("--isa=" + std::string(GetInstructionSetString(kRuntimeISA)));
+ argv_str.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(compiler_filter));
+ if (assume_profile_changed) {
+ argv_str.push_back("--assume-profile-changed");
+ }
+ argv_str.push_back("--image=" + GetImageLocation());
+ argv_str.push_back("--android-data=" + android_data_);
+
+ std::string error;
+ return ExecAndReturnCode(argv_str, &error);
+ }
+
+ int DexoptanalyzerToOatFileAssistant(int dexoptanalyzerResult) {
+ switch (dexoptanalyzerResult) {
+ case 0: return OatFileAssistant::kNoDexOptNeeded;
+ case 1: return OatFileAssistant::kDex2OatFromScratch;
+ case 2: return OatFileAssistant::kDex2OatForBootImage;
+ case 3: return OatFileAssistant::kDex2OatForFilter;
+ case 4: return OatFileAssistant::kDex2OatForRelocation;
+ case 5: return -OatFileAssistant::kDex2OatForBootImage;
+ case 6: return -OatFileAssistant::kDex2OatForFilter;
+ case 7: return -OatFileAssistant::kDex2OatForRelocation;
+ default: return dexoptanalyzerResult;
+ }
+ }
+
+ // Verify that the output of dexoptanalyzer for the given arguments is the same
+ // as the output of OatFileAssistant::GetDexOptNeeded.
+ void Verify(const std::string& dex_file,
+ CompilerFilter::Filter compiler_filter,
+ bool assume_profile_changed = false) {
+ int dexoptanalyzerResult = Analyze(dex_file, compiler_filter, assume_profile_changed);
+ dexoptanalyzerResult = DexoptanalyzerToOatFileAssistant(dexoptanalyzerResult);
+ OatFileAssistant oat_file_assistant(dex_file.c_str(), kRuntimeISA, /*load_executable*/ false);
+ int assistantResult = oat_file_assistant.GetDexOptNeeded(
+ compiler_filter, assume_profile_changed);
+ EXPECT_EQ(assistantResult, dexoptanalyzerResult);
+ }
+};
+
+// The tests below exercise the same test case from oat_file_assistant_test.cc.
+
+// Case: We have a DEX file, but no OAT file for it.
+TEST_F(DexoptAnalyzerTest, DexNoOat) {
+ std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
+ Copy(GetDexSrc1(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kInterpretOnly);
+ Verify(dex_location, CompilerFilter::kSpeedProfile);
+}
+
+// Case: We have a DEX file and up-to-date OAT file for it.
+TEST_F(DexoptAnalyzerTest, OatUpToDate) {
+ std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+ Verify(dex_location, CompilerFilter::kInterpretOnly);
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kEverything);
+}
+
+// Case: We have a DEX file and speed-profile OAT file for it.
+TEST_F(DexoptAnalyzerTest, ProfileOatUpToDate) {
+ std::string dex_location = GetScratchDir() + "/ProfileOatUpToDate.jar";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeedProfile);
+
+ Verify(dex_location, CompilerFilter::kSpeedProfile, false);
+ Verify(dex_location, CompilerFilter::kInterpretOnly, false);
+ Verify(dex_location, CompilerFilter::kSpeedProfile, true);
+ Verify(dex_location, CompilerFilter::kInterpretOnly, true);
+}
+
+// Case: We have a MultiDEX file and up-to-date OAT file for it.
+TEST_F(DexoptAnalyzerTest, MultiDexOatUpToDate) {
+ std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar";
+ Copy(GetMultiDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+ Verify(dex_location, CompilerFilter::kSpeed, false);
+}
+
+// Case: We have a MultiDEX file where the secondary dex file is out of date.
+TEST_F(DexoptAnalyzerTest, MultiDexSecondaryOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar";
+
+ // Compile code for GetMultiDexSrc1.
+ Copy(GetMultiDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+ // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum
+ // is out of date.
+ Copy(GetMultiDexSrc2(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kSpeed, false);
+}
+
+
+// Case: We have a DEX file and an OAT file out of date with respect to the
+// dex checksum.
+TEST_F(DexoptAnalyzerTest, OatDexOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar";
+
+ // We create a dex, generate an oat for it, then overwrite the dex with a
+ // different dex to make the oat out of date.
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+ Copy(GetDexSrc2(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: We have a DEX file and an OAT file out of date with respect to the
+// boot image.
+TEST_F(DexoptAnalyzerTest, OatImageOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(),
+ CompilerFilter::kSpeed,
+ /*relocate*/true,
+ /*pic*/false,
+ /*with_alternate_image*/true);
+
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kInterpretOnly);
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: We have a DEX file and a verify-at-runtime OAT file out of date with
+// respect to the boot image.
+// It shouldn't matter that the OAT file is out of date, because it is
+// verify-at-runtime.
+TEST_F(DexoptAnalyzerTest, OatVerifyAtRuntimeImageOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/OatVerifyAtRuntimeImageOutOfDate.jar";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(),
+ CompilerFilter::kVerifyAtRuntime,
+ /*relocate*/true,
+ /*pic*/false,
+ /*with_alternate_image*/true);
+
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kInterpretOnly);
+}
+
+// Case: We have a DEX file and an ODEX file, but no OAT file.
+TEST_F(DexoptAnalyzerTest, DexOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
+ std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: We have a stripped DEX file and a PIC ODEX file, but no OAT file.
+TEST_F(DexoptAnalyzerTest, StrippedDexOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/StrippedDexOdexNoOat.jar";
+ std::string odex_location = GetOdexDir() + "/StrippedDexOdexNoOat.odex";
+
+ Copy(GetDexSrc1(), dex_location);
+ GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+
+ // Strip the dex file
+ Copy(GetStrippedDexSrc1(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: We have a stripped DEX file, a PIC ODEX file, and an out-of-date OAT file.
+TEST_F(DexoptAnalyzerTest, StrippedDexOdexOat) {
+ std::string dex_location = GetScratchDir() + "/StrippedDexOdexOat.jar";
+ std::string odex_location = GetOdexDir() + "/StrippedDexOdexOat.odex";
+
+ // Create the oat file from a different dex file so it looks out of date.
+ Copy(GetDexSrc2(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+ // Create the odex file
+ Copy(GetDexSrc1(), dex_location);
+ GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+
+ // Strip the dex file.
+ Copy(GetStrippedDexSrc1(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kSpeed);
+ Verify(dex_location, CompilerFilter::kEverything);
+}
+
+// Case: We have a stripped (or resource-only) DEX file, no ODEX file and no
+// OAT file. Expect: The status is kNoDexOptNeeded.
+TEST_F(DexoptAnalyzerTest, ResourceOnlyDex) {
+ std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar";
+
+ Copy(GetStrippedDexSrc1(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kInterpretOnly);
+}
+
+// Case: We have a DEX file, an ODEX file and an OAT file, where the ODEX and
+// OAT files both have patch delta of 0.
+TEST_F(DexoptAnalyzerTest, OdexOatOverlap) {
+ std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
+ std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
+ std::string oat_location = GetOdexDir() + "/OdexOatOverlap.oat";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+
+ // Create the oat file by copying the odex so they are located in the same
+ // place in memory.
+ Copy(odex_location, oat_location);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: We have a DEX file and a PIC ODEX file, but no OAT file.
+TEST_F(DexoptAnalyzerTest, DexPicOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/DexPicOdexNoOat.jar";
+ std::string odex_location = GetOdexDir() + "/DexPicOdexNoOat.odex";
+
+ Copy(GetDexSrc1(), dex_location);
+ GeneratePicOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+ Verify(dex_location, CompilerFilter::kEverything);
+}
+
+// Case: We have a DEX file and a VerifyAtRuntime ODEX file, but no OAT file..
+TEST_F(DexoptAnalyzerTest, DexVerifyAtRuntimeOdexNoOat) {
+ std::string dex_location = GetScratchDir() + "/DexVerifyAtRuntimeOdexNoOat.jar";
+ std::string odex_location = GetOdexDir() + "/DexVerifyAtRuntimeOdexNoOat.odex";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kVerifyAtRuntime);
+
+ Verify(dex_location, CompilerFilter::kVerifyAtRuntime);
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: Non-standard extension for dex file.
+TEST_F(DexoptAnalyzerTest, LongDexExtension) {
+ std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
+ Copy(GetDexSrc1(), dex_location);
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+// Case: Very short, non-existent Dex location.
+TEST_F(DexoptAnalyzerTest, ShortDexLocation) {
+ std::string dex_location = "/xx";
+
+ Verify(dex_location, CompilerFilter::kSpeed);
+}
+
+} // namespace art
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 81f174e..7f98513 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -474,10 +474,14 @@
art_cc_library {
name: "libart-runtime-gtest",
defaults: ["libart-gtest-defaults"],
- srcs: ["common_runtime_test.cc"],
+ srcs: [
+ "common_runtime_test.cc",
+ "dexopt_test.cc"
+ ],
shared_libs: [
"libartd",
"libbase",
+ "libbacktrace"
],
}
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
new file mode 100644
index 0000000..69c6151
--- /dev/null
+++ b/runtime/dexopt_test.cc
@@ -0,0 +1,236 @@
+/*
+ * 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 <vector>
+
+#include <backtrace/BacktraceMap.h>
+#include <gtest/gtest.h>
+
+#include "common_runtime_test.h"
+#include "compiler_callbacks.h"
+#include "dex2oat_environment_test.h"
+#include "dexopt_test.h"
+#include "gc/space/image_space.h"
+#include "mem_map.h"
+
+namespace art {
+void DexoptTest::SetUp() {
+ ReserveImageSpace();
+ Dex2oatEnvironmentTest::SetUp();
+}
+
+void DexoptTest::PreRuntimeCreate() {
+ std::string error_msg;
+ ASSERT_TRUE(PreRelocateImage(GetImageLocation(), &error_msg)) << error_msg;
+ ASSERT_TRUE(PreRelocateImage(GetImageLocation2(), &error_msg)) << error_msg;
+ UnreserveImageSpace();
+}
+
+void DexoptTest::PostRuntimeCreate() {
+ ReserveImageSpace();
+}
+
+void DexoptTest::GenerateOatForTest(const std::string& dex_location,
+ const std::string& oat_location,
+ CompilerFilter::Filter filter,
+ bool relocate,
+ bool pic,
+ bool with_alternate_image) {
+ std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA));
+ std::string dalvik_cache_tmp = dalvik_cache + ".redirected";
+
+ if (!relocate) {
+ // Temporarily redirect the dalvik cache so dex2oat doesn't find the
+ // relocated image file.
+ ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
+ }
+
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + oat_location);
+ args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
+ args.push_back("--runtime-arg");
+
+ // Use -Xnorelocate regardless of the relocate argument.
+ // We control relocation by redirecting the dalvik cache when needed
+ // rather than use this flag.
+ args.push_back("-Xnorelocate");
+
+ if (pic) {
+ args.push_back("--compile-pic");
+ }
+
+ std::string image_location = GetImageLocation();
+ if (with_alternate_image) {
+ args.push_back("--boot-image=" + GetImageLocation2());
+ }
+
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+ if (!relocate) {
+ // Restore the dalvik cache if needed.
+ ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
+ }
+
+ // Verify the odex file was generated as expected.
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+ oat_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
+ ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
+ EXPECT_EQ(pic, odex_file->IsPic());
+ EXPECT_EQ(filter, odex_file->GetCompilerFilter());
+
+ std::unique_ptr<ImageHeader> image_header(
+ gc::space::ImageSpace::ReadImageHeader(image_location.c_str(),
+ kRuntimeISA,
+ &error_msg));
+ ASSERT_TRUE(image_header != nullptr) << error_msg;
+ const OatHeader& oat_header = odex_file->GetOatHeader();
+ uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
+
+ if (CompilerFilter::DependsOnImageChecksum(filter)) {
+ if (with_alternate_image) {
+ EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+ } else {
+ EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+ }
+ }
+
+ if (!with_alternate_image) {
+ if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
+ if (relocate) {
+ EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+ oat_header.GetImageFileLocationOatDataBegin());
+ EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ } else {
+ EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
+ oat_header.GetImageFileLocationOatDataBegin());
+ EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
+ }
+ }
+ }
+}
+
+void DexoptTest::GenerateOdexForTest(const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter) {
+ GenerateOatForTest(dex_location,
+ odex_location,
+ filter,
+ /*relocate*/false,
+ /*pic*/false,
+ /*with_alternate_image*/false);
+}
+
+void DexoptTest::GeneratePicOdexForTest(const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter) {
+ GenerateOatForTest(dex_location,
+ odex_location,
+ filter,
+ /*relocate*/false,
+ /*pic*/true,
+ /*with_alternate_image*/false);
+}
+
+void DexoptTest::GenerateOatForTest(const char* dex_location,
+ CompilerFilter::Filter filter,
+ bool relocate,
+ bool pic,
+ bool with_alternate_image) {
+ std::string oat_location;
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
+ dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
+ GenerateOatForTest(dex_location,
+ oat_location,
+ filter,
+ relocate,
+ pic,
+ with_alternate_image);
+}
+
+void DexoptTest::GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
+ GenerateOatForTest(dex_location,
+ filter,
+ /*relocate*/true,
+ /*pic*/false,
+ /*with_alternate_image*/false);
+}
+
+bool DexoptTest::PreRelocateImage(const std::string& image_location, std::string* error_msg) {
+ std::string image;
+ if (!GetCachedImageFile(image_location, &image, error_msg)) {
+ return false;
+ }
+
+ std::string patchoat = GetAndroidRoot();
+ patchoat += kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat";
+
+ std::vector<std::string> argv;
+ argv.push_back(patchoat);
+ argv.push_back("--input-image-location=" + image_location);
+ argv.push_back("--output-image-file=" + image);
+ argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)));
+ argv.push_back("--base-offset-delta=0x00008000");
+ return Exec(argv, error_msg);
+}
+
+void DexoptTest::ReserveImageSpace() {
+ MemMap::Init();
+
+ // Ensure a chunk of memory is reserved for the image space.
+ // The reservation_end includes room for the main space that has to come
+ // right after the image in case of the GSS collector.
+ uintptr_t reservation_start = ART_BASE_ADDRESS;
+ uintptr_t reservation_end = ART_BASE_ADDRESS + 384 * MB;
+
+ std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
+ ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map";
+ for (BacktraceMap::const_iterator it = map->begin();
+ reservation_start < reservation_end && it != map->end(); ++it) {
+ ReserveImageSpaceChunk(reservation_start, std::min(it->start, reservation_end));
+ reservation_start = std::max(reservation_start, it->end);
+ }
+ ReserveImageSpaceChunk(reservation_start, reservation_end);
+}
+
+void DexoptTest::ReserveImageSpaceChunk(uintptr_t start, uintptr_t end) {
+ if (start < end) {
+ std::string error_msg;
+ image_reservation_.push_back(std::unique_ptr<MemMap>(
+ MemMap::MapAnonymous("image reservation",
+ reinterpret_cast<uint8_t*>(start), end - start,
+ PROT_NONE, false, false, &error_msg)));
+ ASSERT_TRUE(image_reservation_.back().get() != nullptr) << error_msg;
+ LOG(INFO) << "Reserved space for image " <<
+ reinterpret_cast<void*>(image_reservation_.back()->Begin()) << "-" <<
+ reinterpret_cast<void*>(image_reservation_.back()->End());
+ }
+}
+
+void DexoptTest::UnreserveImageSpace() {
+ image_reservation_.clear();
+}
+
+} // namespace art
diff --git a/runtime/dexopt_test.h b/runtime/dexopt_test.h
new file mode 100644
index 0000000..5f0eafd
--- /dev/null
+++ b/runtime/dexopt_test.h
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_DEXOPT_TEST_H_
+#define ART_RUNTIME_DEXOPT_TEST_H_
+
+#include <string>
+#include <vector>
+
+#include "dex2oat_environment_test.h"
+
+namespace art {
+
+class DexoptTest : public Dex2oatEnvironmentTest {
+ public:
+ virtual void SetUp() OVERRIDE;
+
+ virtual void PreRuntimeCreate();
+
+ virtual void PostRuntimeCreate() OVERRIDE;
+
+ // Generate an oat file for the purposes of test.
+ // The oat file will be generated for dex_location in the given oat_location
+ // with the following configuration:
+ // filter - controls the compilation filter
+ // pic - whether or not the code will be PIC
+ // relocate - if true, the oat file will be relocated with respect to the
+ // boot image. Otherwise the oat file will not be relocated.
+ // with_alternate_image - if true, the oat file will be generated with an
+ // image checksum different than the current image checksum.
+ void GenerateOatForTest(const std::string& dex_location,
+ const std::string& oat_location,
+ CompilerFilter::Filter filter,
+ bool relocate,
+ bool pic,
+ bool with_alternate_image);
+
+ // Generate a non-PIC odex file for the purposes of test.
+ // The generated odex file will be un-relocated.
+ void GenerateOdexForTest(const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter);
+
+ void GeneratePicOdexForTest(const std::string& dex_location,
+ const std::string& odex_location,
+ CompilerFilter::Filter filter);
+
+ // Generate an oat file for the given dex location in its oat location (under
+ // the dalvik cache).
+ void GenerateOatForTest(const char* dex_location,
+ CompilerFilter::Filter filter,
+ bool relocate,
+ bool pic,
+ bool with_alternate_image);
+
+ // Generate a standard oat file in the oat location.
+ void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter);
+
+ private:
+ // Pre-Relocate the image to a known non-zero offset so we don't have to
+ // deal with the runtime randomly relocating the image by 0 and messing up
+ // the expected results of the tests.
+ bool PreRelocateImage(const std::string& image_location, std::string* error_msg);
+
+ // Reserve memory around where the image will be loaded so other memory
+ // won't conflict when it comes time to load the image.
+ // This can be called with an already loaded image to reserve the space
+ // around it.
+ void ReserveImageSpace();
+
+ // Reserve a chunk of memory for the image space in the given range.
+ // Only has effect for chunks with a positive number of bytes.
+ void ReserveImageSpaceChunk(uintptr_t start, uintptr_t end);
+
+ // Unreserve any memory reserved by ReserveImageSpace. This should be called
+ // before the image is loaded.
+ void UnreserveImageSpace();
+
+ std::vector<std::unique_ptr<MemMap>> image_reservation_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_DEXOPT_TEST_H_
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 9669dab..a4ba6fd 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -14,23 +14,16 @@
* limitations under the License.
*/
-#include <algorithm>
-#include <fstream>
#include <string>
#include <vector>
#include <sys/param.h>
#include "android-base/strings.h"
-#include <backtrace/BacktraceMap.h>
#include <gtest/gtest.h>
#include "art_field-inl.h"
#include "class_linker-inl.h"
-#include "common_runtime_test.h"
-#include "compiler_callbacks.h"
-#include "dex2oat_environment_test.h"
-#include "gc/space/image_space.h"
-#include "mem_map.h"
+#include "dexopt_test.h"
#include "oat_file_assistant.h"
#include "oat_file_manager.h"
#include "os.h"
@@ -40,242 +33,17 @@
namespace art {
-class OatFileAssistantTest : public Dex2oatEnvironmentTest {
- public:
- virtual void SetUp() OVERRIDE {
- ReserveImageSpace();
- Dex2oatEnvironmentTest::SetUp();
- }
+class OatFileAssistantTest : public DexoptTest {};
- // Pre-Relocate the image to a known non-zero offset so we don't have to
- // deal with the runtime randomly relocating the image by 0 and messing up
- // the expected results of the tests.
- bool PreRelocateImage(const std::string& image_location, std::string* error_msg) {
- std::string image;
- if (!GetCachedImageFile(image_location, &image, error_msg)) {
- return false;
- }
-
- std::string patchoat = GetAndroidRoot();
- patchoat += kIsDebugBuild ? "/bin/patchoatd" : "/bin/patchoat";
-
- std::vector<std::string> argv;
- argv.push_back(patchoat);
- argv.push_back("--input-image-location=" + image_location);
- argv.push_back("--output-image-file=" + image);
- argv.push_back("--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)));
- argv.push_back("--base-offset-delta=0x00008000");
- return Exec(argv, error_msg);
- }
-
- virtual void PreRuntimeCreate() {
- std::string error_msg;
- ASSERT_TRUE(PreRelocateImage(GetImageLocation(), &error_msg)) << error_msg;
- ASSERT_TRUE(PreRelocateImage(GetImageLocation2(), &error_msg)) << error_msg;
- UnreserveImageSpace();
- }
-
- virtual void PostRuntimeCreate() OVERRIDE {
- ReserveImageSpace();
- }
-
- // Generate an oat file for the purposes of test.
- void GenerateOatForTest(const std::string& dex_location,
- const std::string& oat_location,
- CompilerFilter::Filter filter,
- bool relocate,
- bool pic,
- bool with_alternate_image) {
- std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA));
- std::string dalvik_cache_tmp = dalvik_cache + ".redirected";
-
- if (!relocate) {
- // Temporarily redirect the dalvik cache so dex2oat doesn't find the
- // relocated image file.
- ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
- }
-
- std::vector<std::string> args;
- args.push_back("--dex-file=" + dex_location);
- args.push_back("--oat-file=" + oat_location);
- args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
- args.push_back("--runtime-arg");
-
- // Use -Xnorelocate regardless of the relocate argument.
- // We control relocation by redirecting the dalvik cache when needed
- // rather than use this flag.
- args.push_back("-Xnorelocate");
-
- if (pic) {
- args.push_back("--compile-pic");
- }
-
- std::string image_location = GetImageLocation();
- if (with_alternate_image) {
- args.push_back("--boot-image=" + GetImageLocation2());
- }
-
- std::string error_msg;
- ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-
- if (!relocate) {
- // Restore the dalvik cache if needed.
- ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
- }
-
- // Verify the odex file was generated as expected.
- std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
- oat_location.c_str(),
- nullptr,
- nullptr,
- false,
- /*low_4gb*/false,
- dex_location.c_str(),
- &error_msg));
- ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
- EXPECT_EQ(pic, odex_file->IsPic());
- EXPECT_EQ(filter, odex_file->GetCompilerFilter());
-
- std::unique_ptr<ImageHeader> image_header(
- gc::space::ImageSpace::ReadImageHeader(image_location.c_str(),
- kRuntimeISA,
- &error_msg));
- ASSERT_TRUE(image_header != nullptr) << error_msg;
- const OatHeader& oat_header = odex_file->GetOatHeader();
- uint32_t combined_checksum = OatFileAssistant::CalculateCombinedImageChecksum();
-
- if (CompilerFilter::DependsOnImageChecksum(filter)) {
- if (with_alternate_image) {
- EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
- } else {
- EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
- }
- }
-
- if (!with_alternate_image) {
- if (CompilerFilter::IsBytecodeCompilationEnabled(filter)) {
- if (relocate) {
- EXPECT_EQ(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
- oat_header.GetImageFileLocationOatDataBegin());
- EXPECT_EQ(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
- } else {
- EXPECT_NE(reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin()),
- oat_header.GetImageFileLocationOatDataBegin());
- EXPECT_NE(image_header->GetPatchDelta(), oat_header.GetImagePatchDelta());
- }
- }
- }
- }
-
- // Generate a non-PIC odex file for the purposes of test.
- // The generated odex file will be un-relocated.
- void GenerateOdexForTest(const std::string& dex_location,
- const std::string& odex_location,
- CompilerFilter::Filter filter) {
- GenerateOatForTest(dex_location,
- odex_location,
- filter,
- /*relocate*/false,
- /*pic*/false,
- /*with_alternate_image*/false);
- }
-
- void GeneratePicOdexForTest(const std::string& dex_location,
- const std::string& odex_location,
- CompilerFilter::Filter filter) {
- GenerateOatForTest(dex_location,
- odex_location,
- filter,
- /*relocate*/false,
- /*pic*/true,
- /*with_alternate_image*/false);
- }
-
- // Generate an oat file in the oat location.
- void GenerateOatForTest(const char* dex_location,
- CompilerFilter::Filter filter,
- bool relocate,
- bool pic,
- bool with_alternate_image) {
- std::string oat_location;
- std::string error_msg;
- ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
- dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
- GenerateOatForTest(dex_location,
- oat_location,
- filter,
- relocate,
- pic,
- with_alternate_image);
- }
-
- // Generate a standard oat file in the oat location.
- void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter) {
- GenerateOatForTest(dex_location,
- filter,
- /*relocate*/true,
- /*pic*/false,
- /*with_alternate_image*/false);
- }
-
- private:
- // Reserve memory around where the image will be loaded so other memory
- // won't conflict when it comes time to load the image.
- // This can be called with an already loaded image to reserve the space
- // around it.
- void ReserveImageSpace() {
- MemMap::Init();
-
- // Ensure a chunk of memory is reserved for the image space.
- // The reservation_end includes room for the main space that has to come
- // right after the image in case of the GSS collector.
- uintptr_t reservation_start = ART_BASE_ADDRESS;
- uintptr_t reservation_end = ART_BASE_ADDRESS + 384 * MB;
-
- std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid(), true));
- ASSERT_TRUE(map.get() != nullptr) << "Failed to build process map";
- for (BacktraceMap::const_iterator it = map->begin();
- reservation_start < reservation_end && it != map->end(); ++it) {
- ReserveImageSpaceChunk(reservation_start, std::min(it->start, reservation_end));
- reservation_start = std::max(reservation_start, it->end);
- }
- ReserveImageSpaceChunk(reservation_start, reservation_end);
- }
-
- // Reserve a chunk of memory for the image space in the given range.
- // Only has effect for chunks with a positive number of bytes.
- void ReserveImageSpaceChunk(uintptr_t start, uintptr_t end) {
- if (start < end) {
- std::string error_msg;
- image_reservation_.push_back(std::unique_ptr<MemMap>(
- MemMap::MapAnonymous("image reservation",
- reinterpret_cast<uint8_t*>(start), end - start,
- PROT_NONE, false, false, &error_msg)));
- ASSERT_TRUE(image_reservation_.back().get() != nullptr) << error_msg;
- LOG(INFO) << "Reserved space for image " <<
- reinterpret_cast<void*>(image_reservation_.back()->Begin()) << "-" <<
- reinterpret_cast<void*>(image_reservation_.back()->End());
- }
- }
-
-
- // Unreserve any memory reserved by ReserveImageSpace. This should be called
- // before the image is loaded.
- void UnreserveImageSpace() {
- image_reservation_.clear();
- }
-
- std::vector<std::unique_ptr<MemMap>> image_reservation_;
-};
-
-class OatFileAssistantNoDex2OatTest : public OatFileAssistantTest {
+class OatFileAssistantNoDex2OatTest : public DexoptTest {
public:
virtual void SetUpRuntimeOptions(RuntimeOptions* options) {
- OatFileAssistantTest::SetUpRuntimeOptions(options);
+ DexoptTest::SetUpRuntimeOptions(options);
options->push_back(std::make_pair("-Xnodex2oat", nullptr));
}
};
+
// Case: We have a DEX file, but no OAT file for it.
// Expect: The status is kDex2OatNeeded.
TEST_F(OatFileAssistantTest, DexNoOat) {
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 8867743..410416e 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -788,49 +788,58 @@
*task_cpu = strtoull(fields[36].c_str(), nullptr, 10);
}
-const char* GetAndroidRoot() {
- const char* android_root = getenv("ANDROID_ROOT");
- if (android_root == nullptr) {
- if (OS::DirectoryExists("/system")) {
- android_root = "/system";
+static const char* GetAndroidDirSafe(const char* env_var,
+ const char* default_dir,
+ std::string* error_msg) {
+ const char* android_dir = getenv(env_var);
+ if (android_dir == nullptr) {
+ if (OS::DirectoryExists(default_dir)) {
+ android_dir = default_dir;
} else {
- LOG(FATAL) << "ANDROID_ROOT not set and /system does not exist";
- return "";
+ *error_msg = StringPrintf("%s not set and %s does not exist", env_var, default_dir);
+ return nullptr;
}
}
- if (!OS::DirectoryExists(android_root)) {
- LOG(FATAL) << "Failed to find ANDROID_ROOT directory " << android_root;
- return "";
+ if (!OS::DirectoryExists(android_dir)) {
+ *error_msg = StringPrintf("Failed to find %s directory %s", env_var, android_dir);
+ return nullptr;
}
- return android_root;
+ return android_dir;
}
-const char* GetAndroidData() {
+const char* GetAndroidDir(const char* env_var, const char* default_dir) {
std::string error_msg;
- const char* dir = GetAndroidDataSafe(&error_msg);
+ const char* dir = GetAndroidDirSafe(env_var, default_dir, &error_msg);
if (dir != nullptr) {
return dir;
} else {
LOG(FATAL) << error_msg;
- return "";
+ return nullptr;
}
}
+const char* GetAndroidRoot() {
+ return GetAndroidDir("ANDROID_ROOT", "/system");
+}
+
+const char* GetAndroidRootSafe(std::string* error_msg) {
+ return GetAndroidDirSafe("ANDROID_ROOT", "/system", error_msg);
+}
+
+const char* GetAndroidData() {
+ return GetAndroidDir("ANDROID_DATA", "/data");
+}
+
const char* GetAndroidDataSafe(std::string* error_msg) {
- const char* android_data = getenv("ANDROID_DATA");
- if (android_data == nullptr) {
- if (OS::DirectoryExists("/data")) {
- android_data = "/data";
- } else {
- *error_msg = "ANDROID_DATA not set and /data does not exist";
- return nullptr;
- }
+ return GetAndroidDirSafe("ANDROID_DATA", "/data", error_msg);
+}
+
+std::string GetDefaultBootImageLocation(std::string* error_msg) {
+ const char* android_root = GetAndroidRootSafe(error_msg);
+ if (android_root == nullptr) {
+ return "";
}
- if (!OS::DirectoryExists(android_data)) {
- *error_msg = StringPrintf("Failed to find ANDROID_DATA directory %s", android_data);
- return nullptr;
- }
- return android_data;
+ return StringPrintf("%s/framework/boot.art", android_root);
}
void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string* dalvik_cache,
diff --git a/runtime/utils.h b/runtime/utils.h
index 16ef706..9e663b3 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -143,12 +143,18 @@
// Find $ANDROID_ROOT, /system, or abort.
const char* GetAndroidRoot();
+// Find $ANDROID_ROOT, /system, or return null.
+const char* GetAndroidRootSafe(std::string* error_msg);
// Find $ANDROID_DATA, /data, or abort.
const char* GetAndroidData();
// Find $ANDROID_DATA, /data, or return null.
const char* GetAndroidDataSafe(std::string* error_msg);
+// Returns the default boot image location (ANDROID_ROOT/framework/boot.art).
+// Returns an empty string if ANDROID_ROOT is not set.
+std::string GetDefaultBootImageLocation(std::string* error_msg);
+
// Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache
// could not be found.
std::string GetDalvikCache(const char* subdir);
diff --git a/test/Android.bp b/test/Android.bp
index 7143b91..287df13 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -173,12 +173,13 @@
whole_static_libs: [
"libart-compiler-gtest",
"libart-runtime-gtest",
- "libgtest",
+ "libgtest"
],
shared_libs: [
"libartd",
"libartd-compiler",
"libbase",
+ "libbacktrace"
],
target: {
android: {