Fix incorrect DCHECK
We were incorrectly asserting that the disable_pop_frame_depth field
always increased instead of that it never decreased. This meant that
we would get DCHECK failures if one caused a recursive ClassPrepare or
ClassLoad event to trigger.
Test: ./test.py --host
Bug: 146170757
Change-Id: I980a8511033fa88f3fccebb7e654336133cf33bd
diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h
index 23f7151..883a4cc 100644
--- a/openjdkjvmti/events-inl.h
+++ b/openjdkjvmti/events-inl.h
@@ -453,8 +453,10 @@
thread_, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames);
old_disable_frame_pop_depth_ = data->disable_pop_frame_depth;
data->disable_pop_frame_depth = current_top_frame_;
+ // Check that we cleaned up any old disables. This should only increase (or be equals if we do
+ // another ClassLoad/Prepare recursively).
DCHECK(old_disable_frame_pop_depth_ == JvmtiGlobalTLSData::kNoDisallowedPopFrame ||
- current_top_frame_ > old_disable_frame_pop_depth_)
+ current_top_frame_ >= old_disable_frame_pop_depth_)
<< "old: " << old_disable_frame_pop_depth_ << " current: " << current_top_frame_;
}
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index 1f6954e..5032762 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -596,5 +596,30 @@
}
}
+static jobject gRunnableGlobal = nullptr;
+extern "C" JNIEXPORT void JNICALL Java_art_Test912_runRecursiveClassPrepareEvents(
+ JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jobject runnable) {
+ CHECK(gRunnableGlobal == nullptr);
+ gRunnableGlobal = env->NewGlobalRef(runnable);
+ EnableEvents(
+ env,
+ true,
+ nullptr,
+ [](jvmtiEnv* jenv ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env,
+ jthread thread ATTRIBUTE_UNUSED,
+ jclass klass ATTRIBUTE_UNUSED) -> void {
+ jclass runnable_class = jni_env->FindClass("java/lang/Runnable");
+ jni_env->CallVoidMethod(
+ gRunnableGlobal, jni_env->GetMethodID(runnable_class, "run", "()V"));
+ });
+ jclass runnable_class = env->FindClass("java/lang/Runnable");
+ env->CallVoidMethod(
+ runnable, env->GetMethodID(runnable_class, "run", "()V"));
+ EnableEvents(env, false, nullptr, nullptr);
+ env->DeleteGlobalRef(gRunnableGlobal);
+ gRunnableGlobal = nullptr;
+}
+
} // namespace Test912Classes
} // namespace art
diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt
index 7ad5d60..d7699b6 100644
--- a/test/912-classes/expected.txt
+++ b/test/912-classes/expected.txt
@@ -91,3 +91,7 @@
Prepare: L$Proxy21; on ClassEvents (cur=ClassEvents)
Load: [Lart/Test912; on ClassEvents
Prepare: [Lart/Test912; on ClassEvents (cur=ClassEvents)
+class-prepare event START!
+class-prepare event START!
+class-prepare event END!
+class-prepare event END!
diff --git a/test/912-classes/src-art/art/Test912.java b/test/912-classes/src-art/art/Test912.java
index 1a60185..a2e8934 100644
--- a/test/912-classes/src-art/art/Test912.java
+++ b/test/912-classes/src-art/art/Test912.java
@@ -105,6 +105,9 @@
};
classEventsThread.start();
classEventsThread.join();
+
+ // b/146170757
+ TestRecursiveClassPrepareEvents();
}
private static void testClass(String className) throws Exception {
@@ -394,6 +397,32 @@
private static native void setEqualityEventStorageClass(Class<?> c);
private static native void enableClassLoadPrepareEqualityEvents(boolean b);
+ private static native void runRecursiveClassPrepareEvents(Runnable forceLoad);
+
+ private static void TestRecursiveClassPrepareEvents() {
+ final int[] called = new int[] { 0 };
+ runRecursiveClassPrepareEvents(() -> {
+ if (called[0] == 2) {
+ return;
+ } else {
+ called[0]++;
+ }
+ try {
+ System.out.println("class-prepare event START!");
+ // Load a new class in a new class-loader.
+ Class<?> class_loader_class = Class.forName("dalvik.system.InMemoryDexClassLoader");
+ Constructor<?> ctor = class_loader_class.getConstructor(ByteBuffer.class, ClassLoader.class);
+ Class<?> target = ((ClassLoader)ctor.newInstance(
+ ByteBuffer.wrap(DEX_BYTES), Test912.class.getClassLoader())).loadClass("Transform");
+ target.newInstance();
+ } catch (Exception e) { }
+ System.out.println("class-prepare event END!");
+ });
+ if (called[0] != 2) {
+ System.out.println("Failed to cause recursive Class prepare.");
+ }
+ }
+
private static class TestForNonInit {
public static double dummy = Math.random(); // So it can't be compile-time initialized.
}