diff options
| -rw-r--r-- | runtime/Android.bp | 1 | ||||
| -rw-r--r-- | runtime/native/dalvik_system_ZygoteHooks.cc | 44 | ||||
| -rw-r--r-- | runtime/non_debuggable_classes.cc | 42 | ||||
| -rw-r--r-- | runtime/non_debuggable_classes.h | 46 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_redefine.cc | 10 |
5 files changed, 141 insertions, 2 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index d136aa15b3..b4c7b9cc6a 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -171,6 +171,7 @@ cc_defaults { "native/org_apache_harmony_dalvik_ddmc_DdmServer.cc", "native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc", "native/sun_misc_Unsafe.cc", + "non_debuggable_classes.cc", "oat.cc", "oat_file.cc", "oat_file_assistant.cc", diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index fd22d9e646..100f476b43 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -26,9 +26,11 @@ #include "jit/jit.h" #include "jni_internal.h" #include "JNIHelp.h" +#include "non_debuggable_classes.h" #include "scoped_thread_state_change-inl.h" #include "ScopedUtfChars.h" #include "thread-inl.h" +#include "thread_list.h" #include "trace.h" #if defined(__linux__) @@ -39,6 +41,10 @@ namespace art { +// Set to true to always determine the non-debuggable classes even if we would not allow a debugger +// to actually attach. +static constexpr bool kAlwaysCollectNonDebuggableClasses = kIsDebugBuild; + using android::base::StringPrintf; static void EnableDebugger() { @@ -68,6 +74,39 @@ static void EnableDebugger() { } } +static void DoCollectNonDebuggableCallback(Thread* thread, void* data ATTRIBUTE_UNUSED) + REQUIRES(Locks::mutator_lock_) { + class NonDebuggableStacksVisitor : public StackVisitor { + public: + explicit NonDebuggableStacksVisitor(Thread* t) + : StackVisitor(t, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {} + + ~NonDebuggableStacksVisitor() OVERRIDE {} + + bool VisitFrame() OVERRIDE REQUIRES(Locks::mutator_lock_) { + if (GetMethod()->IsRuntimeMethod()) { + return true; + } + NonDebuggableClasses::AddNonDebuggableClass(GetMethod()->GetDeclaringClass()); + if (kIsDebugBuild) { + LOG(INFO) << GetMethod()->GetDeclaringClass()->PrettyClass() + << " might not be fully debuggable/deoptimizable due to " + << GetMethod()->PrettyMethod() << " appearing on the stack during zygote fork."; + } + return true; + } + }; + NonDebuggableStacksVisitor visitor(thread); + visitor.WalkStack(); +} + +static void CollectNonDebuggableClasses() { + Runtime* const runtime = Runtime::Current(); + ScopedSuspendAll suspend("Checking stacks for non-obsoletable methods!", /*long_suspend*/false); + MutexLock mu(Thread::Current(), *Locks::thread_list_lock_); + runtime->GetThreadList()->ForEach(DoCollectNonDebuggableCallback, nullptr); +} + static void EnableDebugFeatures(uint32_t debug_flags) { // Must match values in com.android.internal.os.Zygote. enum { @@ -131,12 +170,17 @@ static void EnableDebugFeatures(uint32_t debug_flags) { debug_flags &= ~DEBUG_ALWAYS_JIT; } + bool needs_non_debuggable_classes = false; if ((debug_flags & DEBUG_JAVA_DEBUGGABLE) != 0) { runtime->AddCompilerOption("--debuggable"); runtime->SetJavaDebuggable(true); // Deoptimize the boot image as it may be non-debuggable. runtime->DeoptimizeBootImage(); debug_flags &= ~DEBUG_JAVA_DEBUGGABLE; + needs_non_debuggable_classes = true; + } + if (needs_non_debuggable_classes || kAlwaysCollectNonDebuggableClasses) { + CollectNonDebuggableClasses(); } if ((debug_flags & DEBUG_NATIVE_DEBUGGABLE) != 0) { diff --git a/runtime/non_debuggable_classes.cc b/runtime/non_debuggable_classes.cc new file mode 100644 index 0000000000..db121a90e2 --- /dev/null +++ b/runtime/non_debuggable_classes.cc @@ -0,0 +1,42 @@ +/* + * 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 "non_debuggable_classes.h" + +#include "base/logging.h" +#include "jni_internal.h" +#include "mirror/class-inl.h" +#include "obj_ptr-inl.h" +#include "ScopedLocalRef.h" +#include "thread-inl.h" + +namespace art { + +std::vector<jclass> NonDebuggableClasses::non_debuggable_classes; + +void NonDebuggableClasses::AddNonDebuggableClass(ObjPtr<mirror::Class> klass) { + Thread* self = Thread::Current(); + JNIEnvExt* env = self->GetJniEnv(); + for (jclass c : non_debuggable_classes) { + if (self->DecodeJObject(c)->AsClass() == klass.Ptr()) { + return; + } + } + ScopedLocalRef<jclass> lr(env, env->AddLocalReference<jclass>(klass)); + non_debuggable_classes.push_back(reinterpret_cast<jclass>(env->NewGlobalRef(lr.get()))); +} + +} // namespace art diff --git a/runtime/non_debuggable_classes.h b/runtime/non_debuggable_classes.h new file mode 100644 index 0000000000..b72afd8299 --- /dev/null +++ b/runtime/non_debuggable_classes.h @@ -0,0 +1,46 @@ +/* + * 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_NON_DEBUGGABLE_CLASSES_H_ +#define ART_RUNTIME_NON_DEBUGGABLE_CLASSES_H_ + +#include <vector> + +#include "base/mutex.h" +#include "jni.h" +#include "obj_ptr.h" + +namespace art { + +namespace mirror { +class Class; +} // namespace mirror + +struct NonDebuggableClasses { + public: + static const std::vector<jclass>& GetNonDebuggableClasses() { + return non_debuggable_classes; + } + + static void AddNonDebuggableClass(ObjPtr<mirror::Class> klass) REQUIRES(Locks::mutator_lock_); + + private: + static std::vector<jclass> non_debuggable_classes; +}; + +} // namespace art + +#endif // ART_RUNTIME_NON_DEBUGGABLE_CLASSES_H_ diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index eefd012d9a..7cc7a631a4 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -56,6 +56,7 @@ #include "mirror/class-inl.h" #include "mirror/class_ext.h" #include "mirror/object.h" +#include "non_debuggable_classes.h" #include "object_lock.h" #include "runtime.h" #include "ScopedLocalRef.h" @@ -248,8 +249,13 @@ jvmtiError Redefiner::GetClassRedefinitionError(art::Handle<art::mirror::Class> return ERR(UNMODIFIABLE_CLASS); } - // TODO We should check if the class has non-obsoletable methods on the stack - LOG(WARNING) << "presence of non-obsoletable methods on stacks is not currently checked"; + for (jclass c : art::NonDebuggableClasses::GetNonDebuggableClasses()) { + if (klass.Get() == art::Thread::Current()->DecodeJObject(c)->AsClass()) { + *error_msg = "Class might have stack frames that cannot be made obsolete"; + return ERR(UNMODIFIABLE_CLASS); + } + } + return OK; } |