You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
415 lines
16 KiB
415 lines
16 KiB
/* |
|
* 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 <stdint.h> |
|
#include <stdio.h> |
|
#include <memory> |
|
|
|
#include "art_method-inl.h" |
|
#include "class_linker-inl.h" |
|
#include "common_compiler_test.h" |
|
#include "compiler_callbacks.h" |
|
#include "dex_file.h" |
|
#include "dex_file_types.h" |
|
#include "gc/heap.h" |
|
#include "mirror/class-inl.h" |
|
#include "mirror/class_loader.h" |
|
#include "mirror/dex_cache-inl.h" |
|
#include "mirror/object_array-inl.h" |
|
#include "mirror/object-inl.h" |
|
#include "handle_scope-inl.h" |
|
#include "jit/profile_compilation_info.h" |
|
#include "scoped_thread_state_change-inl.h" |
|
|
|
namespace art { |
|
|
|
class CompilerDriverTest : public CommonCompilerTest { |
|
protected: |
|
void CompileAll(jobject class_loader) REQUIRES(!Locks::mutator_lock_) { |
|
TimingLogger timings("CompilerDriverTest::CompileAll", false, false); |
|
TimingLogger::ScopedTiming t(__FUNCTION__, &timings); |
|
dex_files_ = GetDexFiles(class_loader); |
|
compiler_driver_->SetDexFilesForOatFile(dex_files_);; |
|
compiler_driver_->CompileAll(class_loader, dex_files_, &timings); |
|
t.NewTiming("MakeAllExecutable"); |
|
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_) { |
|
CompileAll(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 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 DexFile::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))); |
|
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) { |
|
CompileAll(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 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++) { |
|
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 CompilerDriverMethodsTest : public CompilerDriverTest { |
|
protected: |
|
std::unordered_set<std::string>* GetCompiledMethods() OVERRIDE { |
|
return new std::unordered_set<std::string>({ |
|
"byte StaticLeafMethods.identity(byte)", |
|
"int StaticLeafMethods.sum(int, int, int)", |
|
"double StaticLeafMethods.sum(double, double, double, double)" |
|
}); |
|
} |
|
}; |
|
|
|
TEST_F(CompilerDriverMethodsTest, Selection) { |
|
Thread* self = Thread::Current(); |
|
jobject class_loader; |
|
{ |
|
ScopedObjectAccess soa(self); |
|
class_loader = LoadDex("StaticLeafMethods"); |
|
} |
|
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()); |
|
} |
|
|
|
CompileAll(class_loader); |
|
|
|
ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); |
|
ScopedObjectAccess soa(self); |
|
StackHandleScope<1> hs(self); |
|
Handle<mirror::ClassLoader> h_loader( |
|
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader))); |
|
mirror::Class* klass = class_linker->FindClass(self, "LStaticLeafMethods;", h_loader); |
|
ASSERT_NE(klass, nullptr); |
|
|
|
std::unique_ptr<std::unordered_set<std::string>> expected(GetCompiledMethods()); |
|
|
|
const auto pointer_size = class_linker->GetImagePointerSize(); |
|
for (auto& m : klass->GetDirectMethods(pointer_size)) { |
|
std::string name = m.PrettyMethod(true); |
|
const void* code = m.GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); |
|
ASSERT_NE(code, nullptr); |
|
if (expected->find(name) != expected->end()) { |
|
expected->erase(name); |
|
EXPECT_FALSE(class_linker->IsQuickToInterpreterBridge(code)); |
|
} else { |
|
EXPECT_TRUE(class_linker->IsQuickToInterpreterBridge(code)); |
|
} |
|
} |
|
EXPECT_TRUE(expected->empty()); |
|
} |
|
|
|
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))); |
|
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()); |
|
} |
|
|
|
CompileAll(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))); |
|
mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader); |
|
ASSERT_NE(klass, nullptr); |
|
EXPECT_TRUE(klass->IsVerified()); |
|
|
|
mirror::Class::Status status; |
|
bool found = compiler_driver_->GetCompiledClass( |
|
ClassReference(&klass->GetDexFile(), klass->GetDexTypeIndex().index_), &status); |
|
ASSERT_TRUE(found); |
|
EXPECT_EQ(status, mirror::Class::kStatusVerified); |
|
} |
|
}; |
|
|
|
TEST_F(CompilerDriverVerifyTest, VerifyCompilation) { |
|
Thread* self = Thread::Current(); |
|
jobject class_loader; |
|
{ |
|
ScopedObjectAccess soa(self); |
|
class_loader = LoadDex("ProfileTestMultiDex"); |
|
} |
|
ASSERT_NE(class_loader, nullptr); |
|
|
|
CompileAll(class_loader); |
|
|
|
CheckVerifiedClass(class_loader, "LMain;"); |
|
CheckVerifiedClass(class_loader, "LSecond;"); |
|
} |
|
|
|
// Test that a class of status kStatusRetryVerificationAtRuntime is indeed recorded that way in the |
|
// driver. |
|
// Test that checks that classes can be assumed as verified if unloading mode is enabled and |
|
// the class status is at least verified. |
|
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(); |
|
} |
|
compiler_driver_->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. |
|
for (size_t i = mirror::Class::kStatusRetryVerificationAtRuntime; |
|
i < mirror::Class::kStatusMax; |
|
++i) { |
|
const mirror::Class::Status expected_status = static_cast<mirror::Class::Status>(i); |
|
// Skip unsupported status that are not supposed to be ever recorded. |
|
if (expected_status == mirror::Class::kStatusVerifyingAtRuntime || |
|
expected_status == mirror::Class::kStatusInitializing) { |
|
continue; |
|
} |
|
compiler_driver_->RecordClassStatus(ref, expected_status); |
|
mirror::Class::Status status = {}; |
|
ASSERT_TRUE(compiler_driver_->GetCompiledClass(ref, &status)); |
|
EXPECT_EQ(status, expected_status); |
|
|
|
// Check that we can assume verified if we are a status that is at least verified. |
|
if (status >= mirror::Class::kStatusVerified) { |
|
// Check that the class can be assumed as verified in the compiler driver. |
|
EXPECT_TRUE(callbacks_->CanAssumeVerified(ref)) << status; |
|
} |
|
} |
|
} |
|
|
|
// TODO: need check-cast test (when stub complete & we can throw/catch |
|
|
|
} // namespace art
|
|
|