summaryrefslogtreecommitdiff
path: root/dex2oat/driver/compiler_driver_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'dex2oat/driver/compiler_driver_test.cc')
-rw-r--r--dex2oat/driver/compiler_driver_test.cc370
1 files changed, 370 insertions, 0 deletions
diff --git a/dex2oat/driver/compiler_driver_test.cc b/dex2oat/driver/compiler_driver_test.cc
new file mode 100644
index 0000000000..dd2b3abe14
--- /dev/null
+++ b/dex2oat/driver/compiler_driver_test.cc
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2011 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 "driver/compiler_driver.h"
+
+#include <limits>
+#include <stdint.h>
+#include <stdio.h>
+#include <memory>
+
+#include "art_method-inl.h"
+#include "base/casts.h"
+#include "class_linker-inl.h"
+#include "common_compiler_driver_test.h"
+#include "compiler_callbacks.h"
+#include "dex/dex_file.h"
+#include "dex/dex_file_types.h"
+#include "gc/heap.h"
+#include "handle_scope-inl.h"
+#include "mirror/class-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/dex_cache-inl.h"
+#include "mirror/object-inl.h"
+#include "mirror/object_array-inl.h"
+#include "profile/profile_compilation_info.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace art {
+
+class CompilerDriverTest : public CommonCompilerDriverTest {
+ protected:
+ void CompileAllAndMakeExecutable(jobject class_loader) REQUIRES(!Locks::mutator_lock_) {
+ TimingLogger timings("CompilerDriverTest::CompileAllAndMakeExecutable", false, false);
+ dex_files_ = GetDexFiles(class_loader);
+ CompileAll(class_loader, dex_files_, &timings);
+ TimingLogger::ScopedTiming t("MakeAllExecutable", &timings);
+ MakeAllExecutable(class_loader);
+ }
+
+ void EnsureCompiled(jobject class_loader, const char* class_name, const char* method,
+ const char* signature, bool is_virtual)
+ REQUIRES(!Locks::mutator_lock_) {
+ CompileAllAndMakeExecutable(class_loader);
+ Thread::Current()->TransitionFromSuspendedToRunnable();
+ bool started = runtime_->Start();
+ CHECK(started);
+ env_ = Thread::Current()->GetJniEnv();
+ class_ = env_->FindClass(class_name);
+ CHECK(class_ != nullptr) << "Class not found: " << class_name;
+ if (is_virtual) {
+ mid_ = env_->GetMethodID(class_, method, signature);
+ } else {
+ mid_ = env_->GetStaticMethodID(class_, method, signature);
+ }
+ CHECK(mid_ != nullptr) << "Method not found: " << class_name << "." << method << signature;
+ }
+
+ void MakeAllExecutable(jobject class_loader) {
+ const std::vector<const DexFile*> class_path = GetDexFiles(class_loader);
+ for (size_t i = 0; i != class_path.size(); ++i) {
+ const DexFile* dex_file = class_path[i];
+ CHECK(dex_file != nullptr);
+ MakeDexFileExecutable(class_loader, *dex_file);
+ }
+ }
+
+ void MakeExecutable(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
+ CHECK(method != nullptr);
+
+ const CompiledMethod* compiled_method = nullptr;
+ if (!method->IsAbstract()) {
+ mirror::DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ compiled_method =
+ compiler_driver_->GetCompiledMethod(MethodReference(&dex_file,
+ method->GetDexMethodIndex()));
+ }
+ CommonCompilerTest::MakeExecutable(method, compiled_method);
+ }
+
+ void MakeDexFileExecutable(jobject class_loader, const DexFile& dex_file) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ for (size_t i = 0; i < dex_file.NumClassDefs(); i++) {
+ const dex::ClassDef& class_def = dex_file.GetClassDef(i);
+ const char* descriptor = dex_file.GetClassDescriptor(class_def);
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+ ObjPtr<mirror::Class> c = class_linker->FindClass(soa.Self(), descriptor, loader);
+ CHECK(c != nullptr);
+ const auto pointer_size = class_linker->GetImagePointerSize();
+ for (auto& m : c->GetMethods(pointer_size)) {
+ MakeExecutable(&m);
+ }
+ }
+ }
+
+ JNIEnv* env_;
+ jclass class_;
+ jmethodID mid_;
+ std::vector<const DexFile*> dex_files_;
+};
+
+// Disabled due to 10 second runtime on host
+// TODO: Update the test for hash-based dex cache arrays. Bug: 30627598
+TEST_F(CompilerDriverTest, DISABLED_LARGE_CompileDexLibCore) {
+ CompileAllAndMakeExecutable(nullptr);
+
+ // All libcore references should resolve
+ ScopedObjectAccess soa(Thread::Current());
+ ASSERT_TRUE(java_lang_dex_file_ != nullptr);
+ const DexFile& dex = *java_lang_dex_file_;
+ ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(soa.Self(), dex);
+ EXPECT_EQ(dex.NumStringIds(), dex_cache->NumStrings());
+ for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
+ const ObjPtr<mirror::String> string = dex_cache->GetResolvedString(dex::StringIndex(i));
+ EXPECT_TRUE(string != nullptr) << "string_idx=" << i;
+ }
+ EXPECT_EQ(dex.NumTypeIds(), dex_cache->NumResolvedTypes());
+ for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
+ const ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(dex::TypeIndex(i));
+ EXPECT_TRUE(type != nullptr)
+ << "type_idx=" << i << " " << dex.GetTypeDescriptor(dex.GetTypeId(dex::TypeIndex(i)));
+ }
+ EXPECT_TRUE(dex_cache->StaticMethodSize() == dex_cache->NumResolvedMethods()
+ || dex.NumMethodIds() == dex_cache->NumResolvedMethods());
+ auto* cl = Runtime::Current()->GetClassLinker();
+ auto pointer_size = cl->GetImagePointerSize();
+ for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
+ // FIXME: This is outdated for hash-based method array.
+ ArtMethod* method = dex_cache->GetResolvedMethod(i, pointer_size);
+ EXPECT_TRUE(method != nullptr) << "method_idx=" << i
+ << " " << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i))
+ << " " << dex.GetMethodName(dex.GetMethodId(i));
+ EXPECT_TRUE(method->GetEntryPointFromQuickCompiledCode() != nullptr) << "method_idx=" << i
+ << " " << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i)) << " "
+ << dex.GetMethodName(dex.GetMethodId(i));
+ }
+ EXPECT_TRUE(dex_cache->StaticArtFieldSize() == dex_cache->NumResolvedFields()
+ || dex.NumFieldIds() == dex_cache->NumResolvedFields());
+ for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
+ // FIXME: This is outdated for hash-based field array.
+ ArtField* field = dex_cache->GetResolvedField(i, cl->GetImagePointerSize());
+ EXPECT_TRUE(field != nullptr) << "field_idx=" << i
+ << " " << dex.GetFieldDeclaringClassDescriptor(dex.GetFieldId(i))
+ << " " << dex.GetFieldName(dex.GetFieldId(i));
+ }
+
+ // TODO check Class::IsVerified for all classes
+
+ // TODO: check that all Method::GetCode() values are non-null
+}
+
+TEST_F(CompilerDriverTest, AbstractMethodErrorStub) {
+ jobject class_loader;
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ class_loader = LoadDex("AbstractMethod");
+ }
+ ASSERT_TRUE(class_loader != nullptr);
+ EnsureCompiled(class_loader, "AbstractClass", "foo", "()V", true);
+
+ // Create a jobj_ of ConcreteClass, NOT AbstractClass.
+ jclass c_class = env_->FindClass("ConcreteClass");
+
+ jmethodID constructor = env_->GetMethodID(c_class, "<init>", "()V");
+
+ jobject jobj_ = env_->NewObject(c_class, constructor);
+ ASSERT_TRUE(jobj_ != nullptr);
+
+ // Force non-virtual call to AbstractClass foo, will throw AbstractMethodError exception.
+ env_->CallNonvirtualVoidMethod(jobj_, class_, mid_);
+
+ EXPECT_EQ(env_->ExceptionCheck(), JNI_TRUE);
+ jthrowable exception = env_->ExceptionOccurred();
+ env_->ExceptionClear();
+ jclass jlame = env_->FindClass("java/lang/AbstractMethodError");
+ EXPECT_TRUE(env_->IsInstanceOf(exception, jlame));
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ Thread::Current()->ClearException();
+ }
+}
+
+class CompilerDriverProfileTest : public CompilerDriverTest {
+ protected:
+ ProfileCompilationInfo* GetProfileCompilationInfo() override {
+ ScopedObjectAccess soa(Thread::Current());
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
+
+ ProfileCompilationInfo info;
+ for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
+ profile_info_.AddMethodIndex(ProfileCompilationInfo::MethodHotness::kFlagHot,
+ MethodReference(dex_file.get(), 1));
+ profile_info_.AddMethodIndex(ProfileCompilationInfo::MethodHotness::kFlagHot,
+ MethodReference(dex_file.get(), 2));
+ }
+ return &profile_info_;
+ }
+
+ CompilerFilter::Filter GetCompilerFilter() const override {
+ // Use a profile based filter.
+ return CompilerFilter::kSpeedProfile;
+ }
+
+ std::unordered_set<std::string> GetExpectedMethodsForClass(const std::string& clazz) {
+ if (clazz == "Main") {
+ return std::unordered_set<std::string>({
+ "java.lang.String Main.getA()",
+ "java.lang.String Main.getB()"});
+ } else if (clazz == "Second") {
+ return std::unordered_set<std::string>({
+ "java.lang.String Second.getX()",
+ "java.lang.String Second.getY()"});
+ } else {
+ return std::unordered_set<std::string>();
+ }
+ }
+
+ void CheckCompiledMethods(jobject class_loader,
+ const std::string& clazz,
+ const std::unordered_set<std::string>& expected_methods) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> h_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+ ObjPtr<mirror::Class> klass = class_linker->FindClass(self, clazz.c_str(), h_loader);
+ ASSERT_NE(klass, nullptr);
+
+ const auto pointer_size = class_linker->GetImagePointerSize();
+ size_t number_of_compiled_methods = 0;
+ for (auto& m : klass->GetVirtualMethods(pointer_size)) {
+ std::string name = m.PrettyMethod(true);
+ const void* code = m.GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
+ ASSERT_NE(code, nullptr);
+ if (expected_methods.find(name) != expected_methods.end()) {
+ number_of_compiled_methods++;
+ EXPECT_FALSE(class_linker->IsQuickToInterpreterBridge(code));
+ } else {
+ EXPECT_TRUE(class_linker->IsQuickToInterpreterBridge(code));
+ }
+ }
+ EXPECT_EQ(expected_methods.size(), number_of_compiled_methods);
+ }
+
+ private:
+ ProfileCompilationInfo profile_info_;
+};
+
+TEST_F(CompilerDriverProfileTest, ProfileGuidedCompilation) {
+ Thread* self = Thread::Current();
+ jobject class_loader;
+ {
+ ScopedObjectAccess soa(self);
+ class_loader = LoadDex("ProfileTestMultiDex");
+ }
+ ASSERT_NE(class_loader, nullptr);
+
+ // Need to enable dex-file writability. Methods rejected to be compiled will run through the
+ // dex-to-dex compiler.
+ for (const DexFile* dex_file : GetDexFiles(class_loader)) {
+ ASSERT_TRUE(dex_file->EnableWrite());
+ }
+
+ CompileAllAndMakeExecutable(class_loader);
+
+ std::unordered_set<std::string> m = GetExpectedMethodsForClass("Main");
+ std::unordered_set<std::string> s = GetExpectedMethodsForClass("Second");
+ CheckCompiledMethods(class_loader, "LMain;", m);
+ CheckCompiledMethods(class_loader, "LSecond;", s);
+}
+
+// Test that a verify only compiler filter updates the CompiledClass map,
+// which will be used for OatClass.
+class CompilerDriverVerifyTest : public CompilerDriverTest {
+ protected:
+ CompilerFilter::Filter GetCompilerFilter() const override {
+ return CompilerFilter::kVerify;
+ }
+
+ void CheckVerifiedClass(jobject class_loader, const std::string& clazz) const {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> h_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+ ObjPtr<mirror::Class> klass = class_linker->FindClass(self, clazz.c_str(), h_loader);
+ ASSERT_NE(klass, nullptr);
+ EXPECT_TRUE(klass->IsVerified());
+
+ ClassStatus status;
+ bool found = compiler_driver_->GetCompiledClass(
+ ClassReference(&klass->GetDexFile(), klass->GetDexTypeIndex().index_), &status);
+ ASSERT_TRUE(found);
+ EXPECT_EQ(status, ClassStatus::kVerified);
+ }
+};
+
+TEST_F(CompilerDriverVerifyTest, VerifyCompilation) {
+ Thread* self = Thread::Current();
+ jobject class_loader;
+ {
+ ScopedObjectAccess soa(self);
+ class_loader = LoadDex("ProfileTestMultiDex");
+ }
+ ASSERT_NE(class_loader, nullptr);
+
+ CompileAllAndMakeExecutable(class_loader);
+
+ CheckVerifiedClass(class_loader, "LMain;");
+ CheckVerifiedClass(class_loader, "LSecond;");
+}
+
+// Test that a class of status ClassStatus::kRetryVerificationAtRuntime is indeed
+// recorded that way in the driver.
+TEST_F(CompilerDriverVerifyTest, RetryVerifcationStatusCheckVerified) {
+ Thread* const self = Thread::Current();
+ jobject class_loader;
+ std::vector<const DexFile*> dex_files;
+ const DexFile* dex_file = nullptr;
+ {
+ ScopedObjectAccess soa(self);
+ class_loader = LoadDex("ProfileTestMultiDex");
+ ASSERT_NE(class_loader, nullptr);
+ dex_files = GetDexFiles(class_loader);
+ ASSERT_GT(dex_files.size(), 0u);
+ dex_file = dex_files.front();
+ }
+ SetDexFilesForOatFile(dex_files);
+ callbacks_->SetDoesClassUnloading(true, compiler_driver_.get());
+ ClassReference ref(dex_file, 0u);
+ // Test that the status is read from the compiler driver as expected.
+ static_assert(enum_cast<size_t>(ClassStatus::kLast) < std::numeric_limits<size_t>::max(),
+ "Make sure incrementing the class status does not overflow.");
+ for (size_t i = enum_cast<size_t>(ClassStatus::kRetryVerificationAtRuntime);
+ i <= enum_cast<size_t>(ClassStatus::kLast);
+ ++i) {
+ const ClassStatus expected_status = enum_cast<ClassStatus>(i);
+ // Skip unsupported status that are not supposed to be ever recorded.
+ if (expected_status == ClassStatus::kVerifyingAtRuntime ||
+ expected_status == ClassStatus::kInitializing) {
+ continue;
+ }
+ compiler_driver_->RecordClassStatus(ref, expected_status);
+ ClassStatus status = {};
+ ASSERT_TRUE(compiler_driver_->GetCompiledClass(ref, &status));
+ EXPECT_EQ(status, expected_status);
+ }
+}
+
+// TODO: need check-cast test (when stub complete & we can throw/catch
+
+} // namespace art