Merge "Enable PRODUCT_ART_USE_READ_BARRIER by default."
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 c87075f..bc08384 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -28,6 +28,7 @@
DexToDexDecompiler \
ErroneousA \
ErroneousB \
+ ErroneousInit \
ExceptionHandle \
GetMethodSignature \
ImageLayoutA \
@@ -87,7 +88,7 @@
ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces
-ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
ART_GTEST_class_table_test_DEX_DEPS := XandY
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
@@ -100,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
@@ -136,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)
@@ -219,6 +227,7 @@
art_dexdump_tests \
art_dexlayout_tests \
art_dexlist_tests \
+ art_dexoptanalyzer_tests \
art_imgdiag_tests \
art_oatdump_tests \
art_profman_tests \
@@ -614,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/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index c03ffca..b00d083 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -2199,7 +2199,7 @@
if (klass.Get() != nullptr) {
// Only do this if the class is resolved. If even resolution fails, quickening will go very,
// very wrong.
- if (klass->IsResolved()) {
+ if (klass->IsResolved() && !klass->IsErroneousResolved()) {
if (klass->GetStatus() < mirror::Class::kStatusVerified) {
ObjectLock<mirror::Class> lock(soa.Self(), klass);
// Set class status to verified.
@@ -2626,7 +2626,8 @@
void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status status) {
switch (status) {
case mirror::Class::kStatusNotReady:
- case mirror::Class::kStatusError:
+ case mirror::Class::kStatusErrorResolved:
+ case mirror::Class::kStatusErrorUnresolved:
case mirror::Class::kStatusRetryVerificationAtRuntime:
case mirror::Class::kStatusVerified:
case mirror::Class::kStatusInitialized:
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 459aca3..15e4cd8 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -756,7 +756,7 @@
bool my_early_exit = false; // Only for ourselves, ignore caller.
// Remove classes that failed to verify since we don't want to have java.lang.VerifyError in the
// app image.
- if (klass->GetStatus() == mirror::Class::kStatusError) {
+ if (klass->IsErroneous()) {
result = true;
} else {
ObjPtr<mirror::ClassExt> ext(klass->GetExtData());
@@ -777,8 +777,8 @@
visited);
}
// Check static fields and their classes.
- size_t num_static_fields = klass->NumReferenceStaticFields();
- if (num_static_fields != 0 && klass->IsResolved()) {
+ if (klass->IsResolved() && klass->NumReferenceStaticFields() != 0) {
+ size_t num_static_fields = klass->NumReferenceStaticFields();
// Presumably GC can happen when we are cross compiling, it should not cause performance
// problems to do pointer size logic.
MemberOffset field_offset = klass->GetFirstReferenceStaticFieldOffset(
@@ -1154,7 +1154,7 @@
// Visit and assign offsets for fields and field arrays.
mirror::Class* as_klass = obj->AsClass();
mirror::DexCache* dex_cache = as_klass->GetDexCache();
- DCHECK_NE(as_klass->GetStatus(), mirror::Class::kStatusError);
+ DCHECK(!as_klass->IsErroneous()) << as_klass->GetStatus();
if (compile_app_image_) {
// Extra sanity, no boot loader classes should be left!
CHECK(!IsBootClassLoaderClass(as_klass)) << as_klass->PrettyClass();
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index de5af97..bd2c5e3 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -716,7 +716,10 @@
if (compiled_class != nullptr) {
status = compiled_class->GetStatus();
} else if (writer_->compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) {
- status = mirror::Class::kStatusError;
+ // The oat class status is used only for verification of resolved classes,
+ // so use kStatusErrorResolved whether the class was resolved or unresolved
+ // during compile-time verification.
+ status = mirror::Class::kStatusErrorResolved;
} else {
status = mirror::Class::kStatusNotReady;
}
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/art_field-inl.h b/runtime/art_field-inl.h
index b9f688d..917db3e 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -52,7 +52,7 @@
}
inline MemberOffset ArtField::GetOffset() {
- DCHECK(GetDeclaringClass()->IsResolved() || GetDeclaringClass()->IsErroneous());
+ DCHECK(GetDeclaringClass()->IsResolved());
return MemberOffset(offset_);
}
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 15938c5..a35c7ab 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -109,8 +109,7 @@
}
inline uint16_t ArtMethod::GetMethodIndex() {
- DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsResolved() ||
- GetDeclaringClass()->IsErroneous());
+ DCHECK(IsRuntimeMethod() || GetDeclaringClass()->IsResolved());
return method_index_;
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b8ed530..e65672a 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1340,7 +1340,7 @@
// The image space is not yet added to the heap, avoid read barriers.
ObjPtr<mirror::Class> klass = types[j].Read();
if (space->HasAddress(klass.Ptr())) {
- DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+ DCHECK(!klass->IsErroneous()) << klass->GetStatus();
auto it = new_class_set->Find(ClassTable::TableSlot(klass));
DCHECK(it != new_class_set->end());
DCHECK_EQ(it->Read(), klass);
@@ -1703,7 +1703,7 @@
for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) {
ObjPtr<mirror::Class> klass = types[j].Read();
if (klass != nullptr) {
- DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
+ DCHECK(!klass->IsErroneous()) << klass->GetStatus();
}
}
} else {
@@ -2232,7 +2232,7 @@
// For temporary classes we must wait for them to be retired.
if (init_done_ && klass->IsTemp()) {
CHECK(!klass->IsResolved());
- if (klass->IsErroneous()) {
+ if (klass->IsErroneousUnresolved()) {
ThrowEarlierClassFailure(klass);
return nullptr;
}
@@ -2240,10 +2240,10 @@
Handle<mirror::Class> h_class(hs.NewHandle(klass));
ObjectLock<mirror::Class> lock(self, h_class);
// Loop and wait for the resolving thread to retire this class.
- while (!h_class->IsRetired() && !h_class->IsErroneous()) {
+ while (!h_class->IsRetired() && !h_class->IsErroneousUnresolved()) {
lock.WaitIgnoringInterrupts();
}
- if (h_class->IsErroneous()) {
+ if (h_class->IsErroneousUnresolved()) {
ThrowEarlierClassFailure(h_class.Get());
return nullptr;
}
@@ -2258,7 +2258,7 @@
static const size_t kNumYieldIterations = 1000;
// How long each sleep is in us.
static const size_t kSleepDurationUS = 1000; // 1 ms.
- while (!klass->IsResolved() && !klass->IsErroneous()) {
+ while (!klass->IsResolved() && !klass->IsErroneousUnresolved()) {
StackHandleScope<1> hs(self);
HandleWrapperObjPtr<mirror::Class> h_class(hs.NewHandleWrapper(&klass));
{
@@ -2269,7 +2269,7 @@
// Check for circular dependencies between classes, the lock is required for SetStatus.
if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) {
ThrowClassCircularityError(h_class.Get());
- mirror::Class::SetStatus(h_class, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(h_class, mirror::Class::kStatusErrorUnresolved, self);
return nullptr;
}
}
@@ -2286,7 +2286,7 @@
++index;
}
- if (klass->IsErroneous()) {
+ if (klass->IsErroneousUnresolved()) {
ThrowEarlierClassFailure(klass);
return nullptr;
}
@@ -2687,7 +2687,7 @@
// An exception occured during load, set status to erroneous while holding klass' lock in case
// notification is necessary.
if (!klass->IsErroneous()) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
}
return nullptr;
}
@@ -2697,7 +2697,7 @@
if (!LoadSuperAndInterfaces(klass, *new_dex_file)) {
// Loading failed.
if (!klass->IsErroneous()) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
}
return nullptr;
}
@@ -2716,13 +2716,13 @@
if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) {
// Linking failed.
if (!klass->IsErroneous()) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
}
return nullptr;
}
self->AssertNoPendingException();
CHECK(h_new_class.Get() != nullptr) << descriptor;
- CHECK(h_new_class->IsResolved()) << descriptor;
+ CHECK(h_new_class->IsResolved() && !h_new_class->IsErroneousResolved()) << descriptor;
// Instrumentation may have updated entrypoints for all methods of all
// classes. However it could not update methods of this class while we
@@ -3817,7 +3817,7 @@
}
// Need to grab the lock to change status.
ObjectLock<mirror::Class> super_lock(self, klass);
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
@@ -3939,8 +3939,8 @@
bool preverified = VerifyClassUsingOatFile(dex_file, klass.Get(), oat_file_class_status);
// If the oat file says the class had an error, re-run the verifier. That way we will get a
// precise error message. To ensure a rerun, test:
- // oat_file_class_status == mirror::Class::kStatusError => !preverified
- DCHECK(!(oat_file_class_status == mirror::Class::kStatusError) || !preverified);
+ // mirror::Class::IsErroneous(oat_file_class_status) => !preverified
+ DCHECK(!mirror::Class::IsErroneous(oat_file_class_status) || !preverified);
std::string error_msg;
verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
@@ -3998,7 +3998,7 @@
<< " because: " << error_msg;
self->AssertNoPendingException();
ThrowVerifyError(klass.Get(), "%s", error_msg.c_str());
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
}
if (preverified || verifier_failure == verifier::MethodVerifier::kNoFailure) {
// Class is verified so we don't need to do any access check on its methods.
@@ -4089,7 +4089,7 @@
// at compile time).
return false;
}
- if (oat_file_class_status == mirror::Class::kStatusError) {
+ if (mirror::Class::IsErroneous(oat_file_class_status)) {
// Compile time verification failed with a hard error. This is caused by invalid instructions
// in the class. These errors are unrecoverable.
return false;
@@ -4248,7 +4248,7 @@
Handle<mirror::ObjectArray<mirror::Class>> h_interfaces(
hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Class>>(interfaces)));
if (!LinkClass(self, descriptor.c_str(), klass, h_interfaces, &new_class)) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
return nullptr;
}
}
@@ -4463,7 +4463,8 @@
return false;
}
- CHECK(klass->IsResolved()) << klass->PrettyClass() << ": state=" << klass->GetStatus();
+ CHECK(klass->IsResolved() && !klass->IsErroneousResolved())
+ << klass->PrettyClass() << ": state=" << klass->GetStatus();
if (!klass->IsVerified()) {
VerifyClass(self, klass);
@@ -4498,7 +4499,7 @@
// A separate thread could have moved us all the way to initialized. A "simple" example
// involves a subclass of the current class being initialized at the same time (which
// will implicitly initialize the superclass, if scheduled that way). b/28254258
- DCHECK_NE(mirror::Class::kStatusError, klass->GetStatus());
+ DCHECK(!klass->IsErroneous()) << klass->GetStatus();
if (klass->IsInitialized()) {
return true;
}
@@ -4525,7 +4526,7 @@
}
if (!ValidateSuperClassDescriptors(klass)) {
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
self->AllowThreadSuspension();
@@ -4561,7 +4562,7 @@
<< (self->GetException() != nullptr ? self->GetException()->Dump() : "");
ObjectLock<mirror::Class> lock(self, klass);
// Initialization failed because the super-class is erroneous.
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
}
@@ -4592,7 +4593,7 @@
if (!iface_initialized) {
ObjectLock<mirror::Class> lock(self, klass);
// Initialization failed because one of our interfaces with default methods is erroneous.
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
}
@@ -4665,7 +4666,7 @@
if (self->IsExceptionPending()) {
WrapExceptionInInitializer(klass);
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
success = false;
} else if (Runtime::Current()->IsTransactionAborted()) {
// The exception thrown when the transaction aborted has been caught and cleared
@@ -4674,7 +4675,7 @@
<< mirror::Class::PrettyDescriptor(klass.Get())
<< " without exception while transaction was aborted: re-throw it now.";
Runtime::Current()->ThrowTransactionAbortError(self);
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
success = false;
} else {
RuntimeStats* global_stats = Runtime::Current()->GetStats();
@@ -4758,7 +4759,7 @@
// we were not using WaitIgnoringInterrupts), bail out.
if (self->IsExceptionPending()) {
WrapExceptionInInitializer(klass);
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorResolved, self);
return false;
}
// Spurious wakeup? Go back to waiting.
@@ -5169,7 +5170,7 @@
klass->SetIFieldsPtrUnchecked(nullptr);
if (UNLIKELY(h_new_class.Get() == nullptr)) {
self->AssertPendingOOMException();
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
return false;
}
@@ -7781,7 +7782,7 @@
}
}
}
- DCHECK((resolved == nullptr) || resolved->IsResolved() || resolved->IsErroneous())
+ DCHECK((resolved == nullptr) || resolved->IsResolved())
<< resolved->PrettyDescriptor() << " " << resolved->GetStatus();
return resolved.Ptr();
}
@@ -8516,13 +8517,12 @@
}
++num_resolved;
DCHECK(!klass->IsProxyClass());
- if (!klass->IsResolved()) {
- DCHECK(klass->IsErroneous());
+ DCHECK(klass->IsResolved());
+ if (klass->IsErroneousResolved()) {
continue;
}
ObjPtr<mirror::DexCache> klass_dex_cache = klass->GetDexCache();
if (klass_dex_cache == dex_cache) {
- DCHECK(klass->IsResolved());
CHECK_LT(klass->GetDexClassDefIndex(), num_class_defs);
class_set.insert(klass->GetDexTypeIndex());
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index d3bb58d..5042fb7 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -1213,7 +1213,7 @@
const DexFile& initial_dex_file ATTRIBUTE_UNUSED,
const DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED,
/*out*/DexFile const** final_dex_file ATTRIBUTE_UNUSED,
- /*out*/DexFile::ClassDef const** final_dex_cache ATTRIBUTE_UNUSED)
+ /*out*/DexFile::ClassDef const** final_class_def ATTRIBUTE_UNUSED)
REQUIRES_SHARED(Locks::mutator_lock_) {}
// A class has been loaded.
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 7b6c0dc..026afdc 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -87,6 +87,7 @@
EXPECT_FALSE(primitive->IsErroneous());
EXPECT_TRUE(primitive->IsLoaded());
EXPECT_TRUE(primitive->IsResolved());
+ EXPECT_FALSE(primitive->IsErroneousResolved());
EXPECT_TRUE(primitive->IsVerified());
EXPECT_TRUE(primitive->IsInitialized());
EXPECT_FALSE(primitive->IsArrayInstance());
@@ -125,6 +126,7 @@
EXPECT_FALSE(JavaLangObject->IsErroneous());
EXPECT_TRUE(JavaLangObject->IsLoaded());
EXPECT_TRUE(JavaLangObject->IsResolved());
+ EXPECT_FALSE(JavaLangObject->IsErroneousResolved());
EXPECT_TRUE(JavaLangObject->IsVerified());
EXPECT_TRUE(JavaLangObject->IsInitialized());
EXPECT_FALSE(JavaLangObject->IsArrayInstance());
@@ -199,6 +201,7 @@
EXPECT_FALSE(array->IsErroneous());
EXPECT_TRUE(array->IsLoaded());
EXPECT_TRUE(array->IsResolved());
+ EXPECT_FALSE(array->IsErroneousResolved());
EXPECT_TRUE(array->IsVerified());
EXPECT_TRUE(array->IsInitialized());
EXPECT_FALSE(array->IsArrayInstance());
@@ -270,6 +273,7 @@
EXPECT_TRUE(klass->GetDexCache() != nullptr);
EXPECT_TRUE(klass->IsLoaded());
EXPECT_TRUE(klass->IsResolved());
+ EXPECT_FALSE(klass->IsErroneousResolved());
EXPECT_FALSE(klass->IsErroneous());
EXPECT_FALSE(klass->IsArrayClass());
EXPECT_TRUE(klass->GetComponentType() == nullptr);
@@ -857,6 +861,7 @@
EXPECT_FALSE(MyClass->IsErroneous());
EXPECT_TRUE(MyClass->IsLoaded());
EXPECT_TRUE(MyClass->IsResolved());
+ EXPECT_FALSE(MyClass->IsErroneousResolved());
EXPECT_FALSE(MyClass->IsVerified());
EXPECT_FALSE(MyClass->IsInitialized());
EXPECT_FALSE(MyClass->IsArrayInstance());
@@ -941,6 +946,47 @@
array_klass);
}
+TEST_F(ClassLinkerTest, LookupResolvedTypeErroneousInit) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("ErroneousInit"))));
+ AssertNonExistentClass("LErroneousInit;");
+ Handle<mirror::Class> klass =
+ hs.NewHandle(class_linker_->FindClass(soa.Self(), "LErroneousInit;", class_loader));
+ ASSERT_OBJ_PTR_NE(klass.Get(), ObjPtr<mirror::Class>(nullptr));
+ dex::TypeIndex type_idx = klass->GetClassDef()->class_idx_;
+ Handle<mirror::DexCache> dex_cache = hs.NewHandle(klass->GetDexCache());
+ const DexFile& dex_file = klass->GetDexFile();
+ EXPECT_OBJ_PTR_EQ(
+ class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
+ klass.Get());
+ // Zero out the resolved type and make sure LookupResolvedType still finds it.
+ dex_cache->SetResolvedType(type_idx, nullptr);
+ EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
+ EXPECT_OBJ_PTR_EQ(
+ class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
+ klass.Get());
+ // Force initialization to turn the class erroneous.
+ bool initialized = class_linker_->EnsureInitialized(soa.Self(),
+ klass,
+ /* can_init_fields */ true,
+ /* can_init_parents */ true);
+ EXPECT_FALSE(initialized);
+ EXPECT_TRUE(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ // Check that the LookupResolvedType() can still find the resolved type.
+ EXPECT_OBJ_PTR_EQ(
+ class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
+ klass.Get());
+ // Zero out the resolved type and make sure LookupResolvedType() still finds it.
+ dex_cache->SetResolvedType(type_idx, nullptr);
+ EXPECT_TRUE(dex_cache->GetResolvedType(type_idx) == nullptr);
+ EXPECT_OBJ_PTR_EQ(
+ class_linker_->LookupResolvedType(dex_file, type_idx, dex_cache.Get(), class_loader.Get()),
+ klass.Get());
+}
+
TEST_F(ClassLinkerTest, LibCore) {
ScopedObjectAccess soa(Thread::Current());
ASSERT_TRUE(java_lang_dex_file_ != nullptr);
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/gc/heap.cc b/runtime/gc/heap.cc
index 268cca0..7044979 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3543,8 +3543,11 @@
collector::GcType gc_type = collector_ran->GetGcType();
const double multiplier = HeapGrowthMultiplier(); // Use the multiplier to grow more for
// foreground.
- const uint64_t adjusted_min_free = static_cast<uint64_t>(min_free_ * multiplier);
- const uint64_t adjusted_max_free = static_cast<uint64_t>(max_free_ * multiplier);
+ // Ensure at least 2.5 MB to temporarily fix excessive GC caused by TLAB ergonomics.
+ const uint64_t adjusted_min_free = std::max(static_cast<uint64_t>(min_free_ * multiplier),
+ static_cast<uint64_t>(5 * MB / 2));
+ const uint64_t adjusted_max_free = std::max(static_cast<uint64_t>(max_free_ * multiplier),
+ static_cast<uint64_t>(5 * MB / 2));
if (gc_type != collector::kGcTypeSticky) {
// Grow the heap for non sticky GC.
ssize_t delta = bytes_allocated / GetTargetHeapUtilization() - bytes_allocated;
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index fe6a6e9..3d3ad59 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -1169,7 +1169,7 @@
}
void Hprof::DumpHeapClass(mirror::Class* klass) {
- if (!klass->IsResolved() && !klass->IsErroneous()) {
+ if (!klass->IsResolved()) {
// Class is allocated but not yet resolved: we cannot access its fields or super class.
return;
}
diff --git a/runtime/image.cc b/runtime/image.cc
index 6d88895..54b099e 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -25,7 +25,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '5', '\0' }; // ArtMethod update
+const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '6', '\0' }; // Erroneous resolved class.
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 4ea1130..bbd6d35 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -88,11 +88,11 @@
}
void Instrumentation::InstallStubsForClass(mirror::Class* klass) {
- if (klass->IsErroneous()) {
- // We can't execute code in a erroneous class: do nothing.
- } else if (!klass->IsResolved()) {
+ if (!klass->IsResolved()) {
// We need the class to be resolved to install/uninstall stubs. Otherwise its methods
// could not be initialized or linked with regards to class inheritance.
+ } else if (klass->IsErroneousResolved()) {
+ // We can't execute code in a erroneous class: do nothing.
} else {
for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
InstallStubsForMethod(&method);
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 9964b73..f08d4da 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -115,7 +115,9 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
bool class_linker_initialized = class_linker != nullptr && class_linker->IsInitialized();
if (LIKELY(class_linker_initialized)) {
- if (UNLIKELY(new_status <= old_status && new_status != kStatusError &&
+ if (UNLIKELY(new_status <= old_status &&
+ new_status != kStatusErrorUnresolved &&
+ new_status != kStatusErrorResolved &&
new_status != kStatusRetired)) {
LOG(FATAL) << "Unexpected change back of class status for " << h_this->PrettyClass()
<< " " << old_status << " -> " << new_status;
@@ -127,10 +129,12 @@
<< h_this->PrettyClass() << " " << old_status << " -> " << new_status;
}
}
- if (UNLIKELY(new_status == kStatusError)) {
- CHECK_NE(h_this->GetStatus(), kStatusError)
+ if (UNLIKELY(IsErroneous(new_status))) {
+ CHECK(!h_this->IsErroneous())
<< "Attempt to set as erroneous an already erroneous class "
- << h_this->PrettyClass();
+ << h_this->PrettyClass()
+ << " old_status: " << old_status << " new_status: " << new_status;
+ CHECK_EQ(new_status == kStatusErrorResolved, old_status >= kStatusResolved);
if (VLOG_IS_ON(class_linker)) {
LOG(ERROR) << "Setting " << h_this->PrettyDescriptor() << " to erroneous.";
if (self->IsExceptionPending()) {
@@ -177,7 +181,7 @@
// Class is a temporary one, ensure that waiters for resolution get notified of retirement
// so that they can grab the new version of the class from the class linker's table.
CHECK_LT(new_status, kStatusResolved) << h_this->PrettyDescriptor();
- if (new_status == kStatusRetired || new_status == kStatusError) {
+ if (new_status == kStatusRetired || new_status == kStatusErrorUnresolved) {
h_this->NotifyAll(self);
}
} else {
@@ -305,7 +309,7 @@
}
if (h_this->NumStaticFields() > 0) {
os << " static fields (" << h_this->NumStaticFields() << " entries):\n";
- if (h_this->IsResolved() || h_this->IsErroneous()) {
+ if (h_this->IsResolved()) {
for (size_t i = 0; i < h_this->NumStaticFields(); ++i) {
os << StringPrintf(" %2zd: %s\n", i,
ArtField::PrettyField(h_this->GetStaticField(i)).c_str());
@@ -316,7 +320,7 @@
}
if (h_this->NumInstanceFields() > 0) {
os << " instance fields (" << h_this->NumInstanceFields() << " entries):\n";
- if (h_this->IsResolved() || h_this->IsErroneous()) {
+ if (h_this->IsResolved()) {
for (size_t i = 0; i < h_this->NumInstanceFields(); ++i) {
os << StringPrintf(" %2zd: %s\n", i,
ArtField::PrettyField(h_this->GetInstanceField(i)).c_str());
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index fb2792a..7f6aa12 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -84,6 +84,13 @@
// will be gc'ed once all refs to the class point to the newly
// cloned version.
//
+ // kStatusErrorUnresolved, kStatusErrorResolved: Class is erroneous. We need
+ // to distinguish between classes that have been resolved and classes that
+ // have not. This is important because the const-class instruction needs to
+ // return a previously resolved class even if its subsequent initialization
+ // failed. We also need this to decide whether to wrap a previous
+ // initialization failure in ClassDefNotFound error or not.
+ //
// kStatusNotReady: If a Class cannot be found in the class table by
// FindClass, it allocates an new one with AllocClass in the
// kStatusNotReady and calls LoadClass. Note if it does find a
@@ -119,8 +126,9 @@
//
// TODO: Explain the other states
enum Status {
- kStatusRetired = -2, // Retired, should not be used. Use the newly cloned one instead.
- kStatusError = -1,
+ kStatusRetired = -3, // Retired, should not be used. Use the newly cloned one instead.
+ kStatusErrorResolved = -2,
+ kStatusErrorUnresolved = -1,
kStatusNotReady = 0,
kStatusIdx = 1, // Loaded, DEX idx in super_class_type_idx_ and interfaces_type_idx_.
kStatusLoaded = 2, // DEX idx values resolved.
@@ -158,8 +166,25 @@
// Returns true if the class has failed to link.
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsErroneousUnresolved() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetStatus<kVerifyFlags>() == kStatusErrorUnresolved;
+ }
+
+ // Returns true if the class has failed to initialize.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ bool IsErroneousResolved() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetStatus<kVerifyFlags>() == kStatusErrorResolved;
+ }
+
+ // Returns true if the class status indicets that the class has failed to link or initialize.
+ static bool IsErroneous(Status status) {
+ return status == kStatusErrorUnresolved || status == kStatusErrorResolved;
+ }
+
+ // Returns true if the class has failed to link or initialize.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsErroneous() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetStatus<kVerifyFlags>() == kStatusError;
+ return IsErroneous(GetStatus<kVerifyFlags>());
}
// Returns true if the class has been loaded.
@@ -177,7 +202,8 @@
// Returns true if the class has been linked.
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsResolved() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetStatus<kVerifyFlags>() >= kStatusResolved;
+ Status status = GetStatus<kVerifyFlags>();
+ return status >= kStatusResolved || status == kStatusErrorResolved;
}
// Returns true if the class was compile-time verified.
@@ -345,7 +371,7 @@
// be replaced with a class with the right size for embedded imt/vtable.
bool IsTemp() REQUIRES_SHARED(Locks::mutator_lock_) {
Status s = GetStatus();
- return s < Status::kStatusResolving && ShouldHaveEmbeddedVTable();
+ return s < Status::kStatusResolving && s != kStatusErrorResolved && ShouldHaveEmbeddedVTable();
}
String* GetName() REQUIRES_SHARED(Locks::mutator_lock_); // Returns the cached name.
@@ -1017,7 +1043,7 @@
// Returns the number of instance fields containing reference types. Does not count fields in any
// super classes.
uint32_t NumReferenceInstanceFields() REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(IsResolved() || IsErroneous());
+ DCHECK(IsResolved());
return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_instance_fields_));
}
@@ -1045,7 +1071,7 @@
// Returns the number of static fields containing reference types.
uint32_t NumReferenceStaticFields() REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(IsResolved() || IsErroneous());
+ DCHECK(IsResolved());
return GetField32(OFFSET_OF_OBJECT_MEMBER(Class, num_reference_static_fields_));
}
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 284d2d1..a8fa7db 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -81,14 +81,12 @@
if (c != nullptr && c->IsErroneous()) {
cl->ThrowEarlierClassFailure(c.Ptr());
Thread* self = soa.Self();
- ObjPtr<mirror::Class> eiie_class =
- self->DecodeJObject(WellKnownClasses::java_lang_ExceptionInInitializerError)->AsClass();
ObjPtr<mirror::Class> iae_class =
self->DecodeJObject(WellKnownClasses::java_lang_IllegalAccessError)->AsClass();
ObjPtr<mirror::Class> ncdfe_class =
self->DecodeJObject(WellKnownClasses::java_lang_NoClassDefFoundError)->AsClass();
ObjPtr<mirror::Class> exception = self->GetException()->GetClass();
- if (exception == eiie_class || exception == iae_class || exception == ncdfe_class) {
+ if (exception == iae_class || exception == ncdfe_class) {
self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;",
c->PrettyDescriptor().c_str());
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 62d99fb..111755e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -201,8 +201,12 @@
// A representation of an invalid OatClass, used when an OatClass can't be found.
// See FindOatClass().
static OatClass Invalid() {
- return OatClass(nullptr, mirror::Class::kStatusError, kOatClassNoneCompiled, 0, nullptr,
- nullptr);
+ return OatClass(/* oat_file */ nullptr,
+ mirror::Class::kStatusErrorUnresolved,
+ kOatClassNoneCompiled,
+ /* bitmap_size */ 0,
+ /* bitmap_pointer */ nullptr,
+ /* methods_pointer */ nullptr);
}
private:
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/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index 66eb2ec..f1e78b4 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -255,7 +255,7 @@
const DexFile& initial_dex_file,
const DexFile::ClassDef& initial_class_def ATTRIBUTE_UNUSED,
/*out*/DexFile const** final_dex_file ATTRIBUTE_UNUSED,
- /*out*/DexFile::ClassDef const** final_dex_cache ATTRIBUTE_UNUSED)
+ /*out*/DexFile::ClassDef const** final_class_def ATTRIBUTE_UNUSED)
OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
std::string location(initial_dex_file.GetLocation());
std::string event =
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/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 507ea16..2610252 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -51,7 +51,6 @@
jclass WellKnownClasses::java_lang_ClassNotFoundException;
jclass WellKnownClasses::java_lang_Daemons;
jclass WellKnownClasses::java_lang_Error;
-jclass WellKnownClasses::java_lang_ExceptionInInitializerError;
jclass WellKnownClasses::java_lang_invoke_MethodHandle;
jclass WellKnownClasses::java_lang_IllegalAccessError;
jclass WellKnownClasses::java_lang_NoClassDefFoundError;
@@ -290,7 +289,6 @@
java_lang_Object = CacheClass(env, "java/lang/Object");
java_lang_OutOfMemoryError = CacheClass(env, "java/lang/OutOfMemoryError");
java_lang_Error = CacheClass(env, "java/lang/Error");
- java_lang_ExceptionInInitializerError = CacheClass(env, "java/lang/ExceptionInInitializerError");
java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError");
java_lang_invoke_MethodHandle = CacheClass(env, "java/lang/invoke/MethodHandle");
java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index b3ce3d1..db8a53c 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -61,7 +61,6 @@
static jclass java_lang_ClassNotFoundException;
static jclass java_lang_Daemons;
static jclass java_lang_Error;
- static jclass java_lang_ExceptionInInitializerError;
static jclass java_lang_IllegalAccessError;
static jclass java_lang_invoke_MethodHandle;
static jclass java_lang_NoClassDefFoundError;
diff --git a/test/008-exceptions/expected.txt b/test/008-exceptions/expected.txt
index 083ecf7..fcf2ef4 100644
--- a/test/008-exceptions/expected.txt
+++ b/test/008-exceptions/expected.txt
@@ -1,11 +1,11 @@
Got an NPE: second throw
java.lang.NullPointerException: second throw
- at Main.catchAndRethrow(Main.java:77)
- at Main.exceptions_007(Main.java:59)
- at Main.main(Main.java:67)
+ at Main.catchAndRethrow(Main.java:94)
+ at Main.exceptions_007(Main.java:74)
+ at Main.main(Main.java:82)
Caused by: java.lang.NullPointerException: first throw
- at Main.throwNullPointerException(Main.java:84)
- at Main.catchAndRethrow(Main.java:74)
+ at Main.throwNullPointerException(Main.java:101)
+ at Main.catchAndRethrow(Main.java:91)
... 2 more
Static Init
BadError: This is bad by convention: BadInit
@@ -15,3 +15,11 @@
BadErrorNoStringInit: This is bad by convention
java.lang.NoClassDefFoundError: BadInitNoStringInit
BadErrorNoStringInit: This is bad by convention
+BadSuperClass Static Init
+BadError: This is bad by convention: BadInit
+MultiDexBadInit Static Init
+java.lang.Error: MultiDexBadInit
+java.lang.NoClassDefFoundError: MultiDexBadInit
+ cause: java.lang.Error: MultiDexBadInit
+java.lang.NoClassDefFoundError: MultiDexBadInit
+ cause: java.lang.Error: MultiDexBadInit
diff --git a/test/008-exceptions/multidex.jpp b/test/008-exceptions/multidex.jpp
new file mode 100644
index 0000000..a3746f5
--- /dev/null
+++ b/test/008-exceptions/multidex.jpp
@@ -0,0 +1,27 @@
+BadError:
+ @@com.android.jack.annotations.ForceInMainDex
+ class BadError
+BadInit:
+ @@com.android.jack.annotations.ForceInMainDex
+ class BadInit
+BadErrorNoStringInit:
+ @@com.android.jack.annotations.ForceInMainDex
+ class BadErrorNoStringInit
+BadInitNoStringInit:
+ @@com.android.jack.annotations.ForceInMainDex
+ class BadInitNoStringInit
+BadSuperClass:
+ @@com.android.jack.annotations.ForceInMainDex
+ class BadSuperClass
+DerivedFromBadSuperClass:
+ @@com.android.jack.annotations.ForceInMainDex
+ class DerivedFromBadSuperClass
+Main:
+ @@com.android.jack.annotations.ForceInMainDex
+ class Main
+MultiDexBadInit:
+ @@com.android.jack.annotations.ForceInMainDex
+ class MultiDexBadInit
+MultiDexBadInitWrapper1:
+ @@com.android.jack.annotations.ForceInMainDex
+ class MultiDexBadInitWrapper1
diff --git a/test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java b/test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java
new file mode 100644
index 0000000..f3953bd
--- /dev/null
+++ b/test/008-exceptions/src-multidex/MultiDexBadInitWrapper2.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+class MultiDexBadInitWrapper2 {
+ public static void setDummy(int value) {
+ if (doThrow) { throw new Error(); }
+ MultiDexBadInit.dummy = value;
+ }
+
+ public static boolean doThrow = false;
+}
diff --git a/test/008-exceptions/src/Main.java b/test/008-exceptions/src/Main.java
index b8231f1..74af00c 100644
--- a/test/008-exceptions/src/Main.java
+++ b/test/008-exceptions/src/Main.java
@@ -50,6 +50,21 @@
}
}
+// A class that throws BadError during static initialization, serving as a super class.
+class BadSuperClass {
+ static int dummy;
+ static {
+ System.out.println("BadSuperClass Static Init");
+ if (true) {
+ throw new BadError("BadInit");
+ }
+ }
+}
+
+// A class that derives from BadSuperClass.
+class DerivedFromBadSuperClass extends BadSuperClass {
+}
+
/**
* Exceptions across method calls
*/
@@ -63,10 +78,12 @@
npe.printStackTrace(System.out);
}
}
- public static void main (String args[]) {
+ public static void main(String args[]) {
exceptions_007();
exceptionsRethrowClassInitFailure();
exceptionsRethrowClassInitFailureNoStringInit();
+ exceptionsForSuperClassInitFailure();
+ exceptionsInMultiDex();
}
private static void catchAndRethrow() {
@@ -129,4 +146,70 @@
error.printStackTrace(System.out);
}
}
+
+ private static void exceptionsForSuperClassInitFailure() {
+ try {
+ // Resolve DerivedFromBadSuperClass.
+ BadSuperClass.dummy = 1;
+ throw new IllegalStateException("Should not reach here.");
+ } catch (BadError e) {
+ System.out.println(e);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ try {
+ // Before splitting mirror::Class::kStatusError into
+ // kStatusErrorUnresolved and kStatusErrorResolved,
+ // this would trigger a
+ // CHECK(super_class->IsResolved())
+ // failure in
+ // ClassLinker::LoadSuperAndInterfaces().
+ // After the change we're getting either VerifyError
+ // (for Optimizing) or NoClassDefFoundError wrapping
+ // BadError (for interpreter or JIT).
+ new DerivedFromBadSuperClass();
+ throw new IllegalStateException("Should not reach here.");
+ } catch (NoClassDefFoundError ncdfe) {
+ if (!(ncdfe.getCause() instanceof BadError)) {
+ ncdfe.getCause().printStackTrace();
+ }
+ } catch (VerifyError e) {
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+
+ private static void exceptionsInMultiDex() {
+ try {
+ MultiDexBadInit.dummy = 1;
+ throw new IllegalStateException("Should not reach here.");
+ } catch (Error e) {
+ System.out.println(e);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ // Before splitting mirror::Class::kStatusError into
+ // kStatusErrorUnresolved and kStatusErrorResolved,
+ // the exception from wrapper 1 would have been
+ // wrapped in NoClassDefFoundError but the exception
+ // from wrapper 2 would have been unwrapped.
+ try {
+ MultiDexBadInitWrapper1.setDummy(1);
+ throw new IllegalStateException("Should not reach here.");
+ } catch (NoClassDefFoundError ncdfe) {
+ System.out.println(ncdfe);
+ System.out.println(" cause: " + ncdfe.getCause());
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ try {
+ MultiDexBadInitWrapper2.setDummy(1);
+ throw new IllegalStateException("Should not reach here.");
+ } catch (NoClassDefFoundError ncdfe) {
+ System.out.println(ncdfe);
+ System.out.println(" cause: " + ncdfe.getCause());
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
}
diff --git a/test/008-exceptions/src/MultiDexBadInit.java b/test/008-exceptions/src/MultiDexBadInit.java
new file mode 100644
index 0000000..e3ebb9c
--- /dev/null
+++ b/test/008-exceptions/src/MultiDexBadInit.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+class MultiDexBadInit {
+ static int dummy;
+ static {
+ System.out.println("MultiDexBadInit Static Init");
+ if (true) {
+ throw new Error("MultiDexBadInit");
+ }
+ }
+}
diff --git a/test/008-exceptions/src/MultiDexBadInitWrapper1.java b/test/008-exceptions/src/MultiDexBadInitWrapper1.java
new file mode 100644
index 0000000..059e6a3
--- /dev/null
+++ b/test/008-exceptions/src/MultiDexBadInitWrapper1.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+class MultiDexBadInitWrapper1 {
+ public static void setDummy(int value) {
+ if (doThrow) { throw new Error(); }
+ MultiDexBadInit.dummy = value;
+ }
+
+ public static boolean doThrow = false;
+}
diff --git a/test/142-classloader2/expected.txt b/test/142-classloader2/expected.txt
index 86f5e22..056d978 100644
--- a/test/142-classloader2/expected.txt
+++ b/test/142-classloader2/expected.txt
@@ -1 +1,5 @@
+Loaded class B.
+Caught VerifyError.
+Loaded class B.
+Caught wrapped VerifyError.
Everything OK.
diff --git a/test/142-classloader2/src/Main.java b/test/142-classloader2/src/Main.java
index 80b00e7..a0c7764 100644
--- a/test/142-classloader2/src/Main.java
+++ b/test/142-classloader2/src/Main.java
@@ -74,16 +74,25 @@
// Try to load a dex file with bad dex code. Use new instance to force verification.
try {
Class<?> badClass = Main.class.getClassLoader().loadClass("B");
+ System.out.println("Loaded class B.");
badClass.newInstance();
- System.out.println("Should not be able to load class from bad dex file.");
+ System.out.println("Should not be able to instantiate B with bad dex bytecode.");
} catch (VerifyError e) {
+ System.out.println("Caught VerifyError.");
}
// Make sure the same error is rethrown when reloading the bad class.
try {
Class<?> badClass = Main.class.getClassLoader().loadClass("B");
- System.out.println("Should not be able to load class from bad dex file.");
- } catch (VerifyError e) {
+ System.out.println("Loaded class B.");
+ badClass.newInstance();
+ System.out.println("Should not be able to instantiate B with bad dex bytecode.");
+ } catch (NoClassDefFoundError e) {
+ if (e.getCause() instanceof VerifyError) {
+ System.out.println("Caught wrapped VerifyError.");
+ } else {
+ e.printStackTrace();
+ }
}
System.out.println("Everything OK.");
diff --git a/test/154-gc-loop/expected.txt b/test/154-gc-loop/expected.txt
new file mode 100644
index 0000000..6106818
--- /dev/null
+++ b/test/154-gc-loop/expected.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+Finalize count too large: false
diff --git a/test/154-gc-loop/heap_interface.cc b/test/154-gc-loop/heap_interface.cc
new file mode 100644
index 0000000..8d610a8
--- /dev/null
+++ b/test/154-gc-loop/heap_interface.cc
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 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 "gc/heap.h"
+#include "runtime.h"
+
+namespace art {
+namespace {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_backgroundProcessState(JNIEnv*, jclass) {
+ Runtime::Current()->UpdateProcessState(kProcessStateJankImperceptible);
+}
+
+} // namespace
+} // namespace art
diff --git a/test/154-gc-loop/info.txt b/test/154-gc-loop/info.txt
new file mode 100644
index 0000000..f599db1
--- /dev/null
+++ b/test/154-gc-loop/info.txt
@@ -0,0 +1 @@
+Test that GC doesn't happen too often for a few small allocations.
diff --git a/test/154-gc-loop/src/Main.java b/test/154-gc-loop/src/Main.java
new file mode 100644
index 0000000..3a256c1
--- /dev/null
+++ b/test/154-gc-loop/src/Main.java
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+import java.lang.ref.WeakReference;
+
+public class Main {
+ static final class GcWatcher {
+ protected void finalize() throws Throwable {
+ watcher = new WeakReference<GcWatcher>(new GcWatcher());
+ ++finalizeCounter;
+ }
+ }
+ static WeakReference<GcWatcher> watcher = new WeakReference<GcWatcher>(new GcWatcher());
+ static Object o = new Object();
+ static int finalizeCounter = 0;
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ backgroundProcessState();
+ try {
+ Runtime.getRuntime().gc();
+ for (int i = 0; i < 10; ++i) {
+ o = new Object();
+ Thread.sleep(1000);
+ }
+ } catch (Exception e) {}
+ System.out.println("Finalize count too large: " +
+ ((finalizeCounter >= 10) ? Integer.toString(finalizeCounter) : "false"));
+ }
+
+ private static native void backgroundProcessState();
+}
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index d0b77a4..328216b 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -29,7 +29,7 @@
class [Ljava.lang.String; 10000
class java.lang.Object 111
class Main$TestForNonInit 11
-class Main$TestForInitFail 1001
+class Main$TestForInitFail 1011
int []
class [Ljava.lang.String; []
class java.lang.Object []
diff --git a/test/Android.bp b/test/Android.bp
index 89e4092..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: {
@@ -317,6 +318,7 @@
"141-class-unload/jni_unload.cc",
"148-multithread-gc-annotations/gc_coverage.cc",
"149-suspend-all-stress/suspend_all.cc",
+ "154-gc-loop/heap_interface.cc",
"454-get-vreg/get_vreg_jni.cc",
"457-regs/regs_jni.cc",
"461-get-reference-vreg/get_reference_vreg_jni.cc",
diff --git a/test/ErroneousInit/ErroneousInit.java b/test/ErroneousInit/ErroneousInit.java
new file mode 100644
index 0000000..67b7b20
--- /dev/null
+++ b/test/ErroneousInit/ErroneousInit.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+class ErroneousInit {
+ static {
+ if (true) {
+ throw new Error();
+ }
+ }
+}
diff --git a/test/run-test b/test/run-test
index c78fa35..a228789 100755
--- a/test/run-test
+++ b/test/run-test
@@ -408,7 +408,9 @@
mkdir -p $tmp_dir
# Add thread suspend timeout flag
-run_args="${run_args} --runtime-option -XX:ThreadSuspendTimeout=$suspend_timeout"
+if [ ! "$runtime" = "jvm" ]; then
+ run_args="${run_args} --runtime-option -XX:ThreadSuspendTimeout=$suspend_timeout"
+fi
if [ "$basic_verify" = "true" ]; then
# Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests.