Integrate VerifierDeps in compiler driver.
If a VerifierDeps is given, try fast verify. Otherwise create
a new VerifierDeps and do the full verification.
test: test-art-host, verifier_deps_test
bug: 30937355
Change-Id: Ifb030d7b5232c95872164f601057a56ab96038e1
diff --git a/compiler/dex/quick_compiler_callbacks.h b/compiler/dex/quick_compiler_callbacks.h
index 34fd88b..db0fdaa 100644
--- a/compiler/dex/quick_compiler_callbacks.h
+++ b/compiler/dex/quick_compiler_callbacks.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_DEX_QUICK_COMPILER_CALLBACKS_H_
#include "compiler_callbacks.h"
+#include "verifier/verifier_deps.h"
namespace art {
@@ -46,16 +47,16 @@
}
verifier::VerifierDeps* GetVerifierDeps() const OVERRIDE {
- return verifier_deps_;
+ return verifier_deps_.get();
}
- void SetVerifierDeps(verifier::VerifierDeps* deps) {
- verifier_deps_ = deps;
+ void SetVerifierDeps(verifier::VerifierDeps* deps) OVERRIDE {
+ verifier_deps_.reset(deps);
}
private:
VerificationResults* const verification_results_;
- verifier::VerifierDeps* verifier_deps_;
+ std::unique_ptr<verifier::VerifierDeps> verifier_deps_;
};
} // namespace art
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 1b87725..7d045b1 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -39,6 +39,7 @@
#include "compiled_class.h"
#include "compiled_method.h"
#include "compiler.h"
+#include "compiler_callbacks.h"
#include "compiler_driver-inl.h"
#include "dex_compilation_unit.h"
#include "dex_file-inl.h"
@@ -393,6 +394,7 @@
void CompilerDriver::CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
+ verifier::VerifierDeps* verifier_deps,
TimingLogger* timings) {
DCHECK(!Runtime::Current()->IsStarted());
@@ -404,7 +406,7 @@
// 2) Resolve all classes
// 3) Attempt to verify all classes
// 4) Attempt to initialize image classes, and trivially initialized classes
- PreCompile(class_loader, dex_files, timings);
+ PreCompile(class_loader, dex_files, verifier_deps, timings);
if (GetCompilerOptions().IsBootImage()) {
// We don't need to setup the intrinsics for non boot image compilation, as
// those compilations will pick up a boot image that have the ArtMethod already
@@ -676,7 +678,7 @@
InitializeThreadPools();
- PreCompile(jclass_loader, dex_files, timings);
+ PreCompile(jclass_loader, dex_files, /* verifier_deps */ nullptr, timings);
// Can we run DEX-to-DEX compiler on this class ?
optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level =
@@ -873,6 +875,7 @@
void CompilerDriver::PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
+ verifier::VerifierDeps* verifier_deps,
TimingLogger* timings) {
CheckThreadPools();
@@ -906,7 +909,7 @@
VLOG(compiler) << "Resolve const-strings: " << GetMemoryUsageString(false);
}
- Verify(class_loader, dex_files, timings);
+ Verify(class_loader, dex_files, verifier_deps, timings);
VLOG(compiler) << "Verify: " << GetMemoryUsageString(false);
if (had_hard_verifier_failure_ && GetCompilerOptions().AbortOnHardVerifierFailure()) {
@@ -1932,15 +1935,61 @@
}
}
-void CompilerDriver::Verify(jobject class_loader,
+void CompilerDriver::Verify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
+ verifier::VerifierDeps* verifier_deps,
TimingLogger* timings) {
+ if (verifier_deps != nullptr) {
+ TimingLogger::ScopedTiming t("Fast Verify", timings);
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
+ MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (verifier_deps->ValidateDependencies(class_loader, soa.Self())) {
+ // We successfully validated the dependencies, now update class status
+ // of verified classes. Note that the dependencies also record which classes
+ // could not be fully verified; we could try again, but that would hurt verification
+ // time. So instead we assume these classes still need to be verified at
+ // runtime.
+ for (const DexFile* dex_file : dex_files) {
+ // Fetch the list of unverified classes and turn it into a set for faster
+ // lookups.
+ const std::vector<uint16_t>& unverified_classes =
+ verifier_deps->GetUnverifiedClasses(*dex_file);
+ std::set<uint16_t> set(unverified_classes.begin(), unverified_classes.end());
+ for (uint32_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);
+ cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
+ if (cls.Get() == nullptr) {
+ CHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ } else if (set.find(class_def.class_idx_) == set.end()) {
+ ObjectLock<mirror::Class> lock(soa.Self(), cls);
+ mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self());
+ }
+ }
+ }
+ return;
+ }
+ }
+
+ // If there is no passed `verifier_deps` (because of non-existing vdex), or
+ // the passed `verifier_deps` is not valid anymore, create a new one for
+ // non boot image compilation. The verifier will need it to record the new dependencies.
+ // Then dex2oat can update the vdex file with these new dependencies.
+ if (!GetCompilerOptions().IsBootImage()) {
+ Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
+ new verifier::VerifierDeps(dex_files));
+ }
// Note: verification should not be pulling in classes anymore when compiling the boot image,
// as all should have been resolved before. As such, doing this in parallel should still
// be deterministic.
for (const DexFile* dex_file : dex_files) {
CHECK(dex_file != nullptr);
- VerifyDexFile(class_loader,
+ VerifyDexFile(jclass_loader,
*dex_file,
dex_files,
parallel_thread_pool_.get(),
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 4a48f9c..c8d6cb0 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -50,6 +50,7 @@
namespace verifier {
class MethodVerifier;
+class VerifierDeps;
class VerifierDepsTest;
} // namespace verifier
@@ -117,6 +118,7 @@
void CompileAll(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
+ verifier::VerifierDeps* verifier_deps,
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_);
@@ -415,6 +417,7 @@
private:
void PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
+ verifier::VerifierDeps* verifier_deps,
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_);
@@ -437,7 +440,9 @@
void Verify(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
+ verifier::VerifierDeps* verifier_deps,
TimingLogger* timings);
+
void VerifyDexFile(jobject class_loader,
const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
diff --git a/compiler/driver/compiler_driver_test.cc b/compiler/driver/compiler_driver_test.cc
index 845028d..9679a79 100644
--- a/compiler/driver/compiler_driver_test.cc
+++ b/compiler/driver/compiler_driver_test.cc
@@ -43,6 +43,7 @@
TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
compiler_driver_->CompileAll(class_loader,
GetDexFiles(class_loader),
+ /* verifier_deps */ nullptr,
&timings);
t.NewTiming("MakeAllExecutable");
MakeAllExecutable(class_loader);
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 8fdf6fc..fcb8979 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -189,7 +189,7 @@
TimingLogger timings("ImageTest::WriteRead", false, false);
TimingLogger::ScopedTiming t("CompileAll", &timings);
driver->SetDexFilesForOatFile(class_path);
- driver->CompileAll(class_loader, class_path, &timings);
+ driver->CompileAll(class_loader, class_path, /* verifier_deps */ nullptr, &timings);
t.NewTiming("WriteElf");
SafeMap<std::string, std::string> key_value_store;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 64ee574..102637f 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -377,7 +377,8 @@
if (kCompile) {
TimingLogger timings2("OatTest::WriteRead", false, false);
compiler_driver_->SetDexFilesForOatFile(class_linker->GetBootClassPath());
- compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings2);
+ compiler_driver_->CompileAll(
+ class_loader, class_linker->GetBootClassPath(), /* verifier_deps */ nullptr, &timings2);
}
ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex");
@@ -391,7 +392,8 @@
ASSERT_TRUE(success);
if (kCompile) { // OatWriter strips the code, regenerate to compare
- compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
+ compiler_driver_->CompileAll(
+ class_loader, class_linker->GetBootClassPath(), /* verifier_deps */ nullptr, &timings);
}
std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp_oat.GetFilename(),
tmp_oat.GetFilename(),
@@ -515,7 +517,7 @@
soa.Decode<mirror::ClassLoader>(class_loader).Ptr());
}
compiler_driver_->SetDexFilesForOatFile(dex_files);
- compiler_driver_->CompileAll(class_loader, dex_files, &timings);
+ compiler_driver_->CompileAll(class_loader, dex_files, /* verifier_deps */ nullptr, &timings);
ScratchFile tmp_oat, tmp_vdex(tmp_oat, ".vdex");
SafeMap<std::string, std::string> key_value_store;
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index 8d2a0e7..6b690aa 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -72,6 +72,22 @@
return klass;
}
+ void SetupCompilerDriver() {
+ compiler_options_->boot_image_ = false;
+ compiler_driver_->InitializeThreadPools();
+ }
+
+ void VerifyWithCompilerDriver(verifier::VerifierDeps* deps) {
+ TimingLogger timings("Verify", false, false);
+ // The compiler driver handles the verifier deps in the callbacks, so
+ // remove what this class did for unit testing.
+ verifier_deps_.reset(nullptr);
+ callbacks_->SetVerifierDeps(nullptr);
+ compiler_driver_->Verify(class_loader_, dex_files_, deps, &timings);
+ // The compiler driver may have updated the VerifierDeps in the callback object.
+ verifier_deps_.reset(callbacks_->GetVerifierDeps());
+ }
+
void SetVerifierDeps(const std::vector<const DexFile*>& dex_files) {
verifier_deps_.reset(new verifier::VerifierDeps(dex_files));
VerifierDepsCompilerCallbacks* callbacks =
@@ -156,15 +172,12 @@
}
void VerifyDexFile(const char* multidex = nullptr) {
- std::string error_msg;
{
ScopedObjectAccess soa(Thread::Current());
LoadDexFile(&soa, "VerifierDeps", multidex);
}
- TimingLogger timings("Verify", false, false);
- compiler_options_->boot_image_ = false;
- compiler_driver_->InitializeThreadPools();
- compiler_driver_->Verify(class_loader_, dex_files_, &timings);
+ SetupCompilerDriver();
+ VerifyWithCompilerDriver(/* verifier_deps */ nullptr);
}
bool TestAssignabilityRecording(const std::string& dst,
@@ -185,6 +198,33 @@
return true;
}
+ // Check that the status of classes in `class_loader_` match the
+ // expected status in `deps`.
+ void VerifyClassStatus(const verifier::VerifierDeps& deps) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader_handle(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_)));
+ MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
+ for (const DexFile* dex_file : dex_files_) {
+ const std::vector<uint16_t>& unverified_classes = deps.GetUnverifiedClasses(*dex_file);
+ std::set<uint16_t> set(unverified_classes.begin(), unverified_classes.end());
+ for (uint32_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);
+ cls.Assign(class_linker_->FindClass(soa.Self(), descriptor, class_loader_handle));
+ if (cls.Get() == nullptr) {
+ CHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ } else if (set.find(class_def.class_idx_) == set.end()) {
+ ASSERT_EQ(cls->GetStatus(), mirror::Class::kStatusVerified);
+ } else {
+ ASSERT_LT(cls->GetStatus(), mirror::Class::kStatusVerified);
+ }
+ }
+ }
+ }
+
bool HasUnverifiedClass(const std::string& cls) {
const DexFile::TypeId* type_id = primary_dex_file_->FindTypeId(cls.c_str());
DCHECK(type_id != nullptr);
@@ -1160,7 +1200,7 @@
MutableHandle<mirror::ClassLoader> new_class_loader(hs.NewHandle<mirror::ClassLoader>(nullptr));
{
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_TRUE(verifier_deps_->Verify(new_class_loader, soa.Self()));
+ ASSERT_TRUE(verifier_deps_->ValidateDependencies(new_class_loader, soa.Self()));
}
std::vector<uint8_t> buffer;
@@ -1170,7 +1210,7 @@
{
VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_TRUE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_TRUE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
// Fiddle with the dependencies to make sure we catch any change and fail to verify.
@@ -1181,7 +1221,7 @@
VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
deps->assignable_types_.insert(*deps->unassignable_types_.begin());
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
{
@@ -1190,7 +1230,7 @@
VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
deps->unassignable_types_.insert(*deps->assignable_types_.begin());
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
// Mess up with classes.
@@ -1208,7 +1248,7 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
{
@@ -1225,7 +1265,7 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
{
@@ -1242,7 +1282,7 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
// Mess up with fields.
@@ -1261,7 +1301,7 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
{
@@ -1279,7 +1319,7 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
{
@@ -1297,7 +1337,7 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
{
@@ -1316,7 +1356,7 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
// Mess up with methods.
@@ -1338,7 +1378,7 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
{
@@ -1357,7 +1397,7 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
{
@@ -1376,7 +1416,7 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
{
@@ -1396,7 +1436,7 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
{
@@ -1415,7 +1455,7 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
}
{
@@ -1434,7 +1474,56 @@
}
ASSERT_TRUE(found);
new_class_loader.Assign(soa.Decode<mirror::ClassLoader>(LoadDex("VerifierDeps")));
- ASSERT_FALSE(decoded_deps.Verify(new_class_loader, soa.Self()));
+ ASSERT_FALSE(decoded_deps.ValidateDependencies(new_class_loader, soa.Self()));
+ }
+ }
+}
+
+TEST_F(VerifierDepsTest, CompilerDriver) {
+ SetupCompilerDriver();
+
+ // Test both multi-dex and single-dex configuration.
+ for (const char* multi : { "MultiDex", static_cast<const char*>(nullptr) }) {
+ // Test that the compiler driver behaves as expected when the dependencies
+ // verify and when they don't verify.
+ for (bool verify_failure : { false, true }) {
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ LoadDexFile(&soa, "VerifierDeps", multi);
+ }
+ VerifyWithCompilerDriver(/* verifier_deps */ nullptr);
+
+ std::vector<uint8_t> buffer;
+ verifier_deps_->Encode(dex_files_, &buffer);
+
+ {
+ ScopedObjectAccess soa(Thread::Current());
+ LoadDexFile(&soa, "VerifierDeps", multi);
+ }
+ verifier::VerifierDeps decoded_deps(dex_files_, ArrayRef<const uint8_t>(buffer));
+ if (verify_failure) {
+ // Just taint the decoded VerifierDeps with one invalid entry.
+ VerifierDeps::DexFileDeps* deps = decoded_deps.GetDexFileDeps(*primary_dex_file_);
+ bool found = false;
+ for (const auto& entry : deps->classes_) {
+ if (entry.IsResolved()) {
+ deps->classes_.insert(VerifierDeps::ClassResolution(
+ entry.GetDexTypeIndex(), VerifierDeps::kUnresolvedMarker));
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ }
+ VerifyWithCompilerDriver(&decoded_deps);
+
+ if (verify_failure) {
+ ASSERT_FALSE(verifier_deps_ == nullptr);
+ ASSERT_FALSE(verifier_deps_->Equals(decoded_deps));
+ } else {
+ ASSERT_TRUE(verifier_deps_ == nullptr);
+ VerifyClassStatus(decoded_deps);
+ }
}
}
}