summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Sebastien Hertz <shertz@google.com> 2014-11-26 22:11:27 +0100
committer Sebastien Hertz <shertz@google.com> 2014-12-04 10:20:24 +0100
commit6963e44331258b131bcc0599b868ba15902d6d22 (patch)
tree24ef16e739e99d3e9d980f2acde8dd301c236c37
parent220526b05d4365a1820a694c98527eda2d3dc980 (diff)
JDWP: fix breakpoint for method in the image
When we set a breakpoint in a compiled method, we deoptimize it by changing its entrypoint so it is executed with the interpreter. However, methods in the image can be called with their direct code pointer, ignoring the updated entrypoint. In that case, the method is not executed with the interpreter and we miss the breakpoint. This CL avoids that situation by forcing a full deoptimization so everything runs with the interpreter. However, if the image has been compiled in PIC mode, we keep using selective deoptimization because direct code pointer is not used in this mode. Bug: 17965285 Change-Id: Icaf8cbb7fe9ad01d36f7378c59d50d9ce42ae57f
-rw-r--r--runtime/class_linker.cc30
-rw-r--r--runtime/class_linker.h4
-rw-r--r--runtime/class_linker_test.cc20
-rw-r--r--runtime/debugger.cc12
-rw-r--r--runtime/mirror/class.h5
5 files changed, 69 insertions, 2 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f092772239..7a8e4a3d92 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -5926,4 +5926,34 @@ std::size_t ClassLinker::ClassDescriptorHashEquals::operator()(const char* descr
return ComputeModifiedUtf8Hash(descriptor);
}
+bool ClassLinker::MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m) {
+ // Non-image methods don't use direct code pointer.
+ if (!m->GetDeclaringClass()->IsBootStrapClassLoaded()) {
+ return false;
+ }
+ if (m->IsPrivate()) {
+ // The method can only be called inside its own oat file. Therefore it won't be called using
+ // its direct code if the oat file has been compiled in PIC mode.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const DexFile& dex_file = m->GetDeclaringClass()->GetDexFile();
+ const OatFile::OatDexFile* oat_dex_file = class_linker->FindOpenedOatDexFileForDexFile(dex_file);
+ if (oat_dex_file == nullptr) {
+ // No oat file: the method has not been compiled.
+ return false;
+ }
+ const OatFile* oat_file = oat_dex_file->GetOatFile();
+ return oat_file != nullptr && !oat_file->IsPic();
+ } else {
+ // The method can be called outside its own oat file. Therefore it won't be called using its
+ // direct code pointer only if all loaded oat files have been compiled in PIC mode.
+ ReaderMutexLock mu(Thread::Current(), dex_lock_);
+ for (const OatFile* oat_file : oat_files_) {
+ if (!oat_file->IsPic()) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
} // namespace art
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index b78d0b5bc3..55332f808f 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -471,6 +471,10 @@ class ClassLinker {
LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Returns true if the method can be called with its direct code pointer, false otherwise.
+ bool MayBeCalledWithDirectCodePointer(mirror::ArtMethod* m)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
private:
const OatFile::OatMethod FindOatMethodFor(mirror::ArtMethod* method, bool* found)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 99d0746962..c22c51e525 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -1141,4 +1141,24 @@ TEST_F(ClassLinkerTest, Preverified_App) {
CheckPreverified(statics.Get(), true);
}
+TEST_F(ClassLinkerTest, IsBootStrapClassLoaded) {
+ ScopedObjectAccess soa(Thread::Current());
+
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(LoadDex("Statics"))));
+
+ // java.lang.Object is a bootstrap class.
+ Handle<mirror::Class> jlo_class(
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
+ ASSERT_TRUE(jlo_class.Get() != nullptr);
+ EXPECT_TRUE(jlo_class.Get()->IsBootStrapClassLoaded());
+
+ // Statics is not a bootstrap class.
+ Handle<mirror::Class> statics(
+ hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader)));
+ ASSERT_TRUE(statics.Get() != nullptr);
+ EXPECT_FALSE(statics.Get()->IsBootStrapClassLoaded());
+}
+
} // namespace art
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 49b132dd0f..86d027b8ab 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -3266,8 +3266,16 @@ static DeoptimizationRequest::Kind GetRequiredDeoptimizationKind(Thread* self,
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
const bool is_compiled = class_linker->GetOatMethodQuickCodeFor(m) != nullptr;
if (is_compiled) {
- VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m);
- return DeoptimizationRequest::kSelectiveDeoptimization;
+ // If the method may be called through its direct code pointer (without loading
+ // its updated entrypoint), we need full deoptimization to not miss the breakpoint.
+ if (class_linker->MayBeCalledWithDirectCodePointer(m)) {
+ VLOG(jdwp) << "Need full deoptimization because of possible direct code call "
+ << "into image for compiled method " << PrettyMethod(m);
+ return DeoptimizationRequest::kFullDeoptimization;
+ } else {
+ VLOG(jdwp) << "Need selective deoptimization for compiled method " << PrettyMethod(m);
+ return DeoptimizationRequest::kSelectiveDeoptimization;
+ }
} else {
// Method is not compiled: we don't need to deoptimize.
VLOG(jdwp) << "No need for deoptimization for non-compiled method " << PrettyMethod(m);
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index a77972efc5..2fc5ffb517 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1043,6 +1043,11 @@ class MANAGED Class FINAL : public Object {
DISALLOW_COPY_AND_ASSIGN(InitializeClassVisitor);
};
+ // Returns true if the class loader is null, ie the class loader is the boot strap class loader.
+ bool IsBootStrapClassLoaded() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return GetClassLoader() == nullptr;
+ }
+
private:
void SetVerifyErrorClass(Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);