8371083: FollowReferences reports non-class objects as JVMTI_HEAP_REFERENCE_SYSTEM_CLASS

Reviewed-by: lmesnik, sspitsyn
This commit is contained in:
Alex Menkov 2025-11-14 19:39:26 +00:00
parent 58b601ac42
commit 3924a28a22
3 changed files with 209 additions and 3 deletions

View File

@ -2190,6 +2190,39 @@ class SimpleRootsClosure : public OopClosure {
virtual void do_oop(narrowOop* obj_p) { ShouldNotReachHere(); }
};
// A supporting closure used to process ClassLoaderData roots.
class CLDRootsClosure: public OopClosure {
private:
bool _continue;
public:
CLDRootsClosure(): _continue(true) {}
inline bool stopped() {
return !_continue;
}
void do_oop(oop* obj_p) {
if (stopped()) {
return;
}
oop o = NativeAccess<AS_NO_KEEPALIVE>::oop_load(obj_p);
// ignore null
if (o == nullptr) {
return;
}
jvmtiHeapReferenceKind kind = JVMTI_HEAP_REFERENCE_OTHER;
if (o->klass() == vmClasses::Class_klass()) {
kind = JVMTI_HEAP_REFERENCE_SYSTEM_CLASS;
}
// invoke the callback
_continue = CallbackInvoker::report_simple_root(kind, o);
}
virtual void do_oop(narrowOop* obj_p) { ShouldNotReachHere(); }
};
// A supporting closure used to process JNI locals
class JNILocalRootsClosure : public OopClosure {
private:
@ -2776,10 +2809,10 @@ inline bool VM_HeapWalkOperation::collect_simple_roots() {
}
// Preloaded classes and loader from the system dictionary
blk.set_kind(JVMTI_HEAP_REFERENCE_SYSTEM_CLASS);
CLDToOopClosure cld_closure(&blk, ClassLoaderData::_claim_none);
CLDRootsClosure cld_roots_closure;
CLDToOopClosure cld_closure(&cld_roots_closure, ClassLoaderData::_claim_none);
ClassLoaderDataGraph::always_strong_cld_do(&cld_closure);
if (blk.stopped()) {
if (cld_roots_closure.stopped()) {
return false;
}

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8371083
* @summary Verify FollowReferences does not report non-classes roots as JVMTI_HEAP_REFERENCE_SYSTEM_CLASS
* @requires vm.jvmti
* @run main/othervm/native -agentlib:KindSystemClass
* KindSystemClass
*/
public class KindSystemClass {
static native int tagSysClasses();
static native Object[] getObjectsWithTags();
public static void main(String[] args) throws Exception {
System.loadLibrary("KindSystemClass");
int tagged = tagSysClasses();
System.out.println("Tagged " + tagged + " classes");
Object[] objs = getObjectsWithTags();
System.out.println("Tagged objects (total " + objs.length + "):");
int nonClassesCnt = 0;
for (int i = 0; i < objs.length; i++) {
Object obj = objs[i];
String s;
if (obj instanceof Class cls) {
s = "OK: " + cls;
} else {
nonClassesCnt++;
s = "ERROR, not a class: " + obj;
}
System.out.println("[" + i + "] " + s);
}
if (nonClassesCnt != 0) {
throw new RuntimeException("Found " + nonClassesCnt + " non-classes");
}
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <jvmti.h>
#include "jvmti_common.hpp"
static jvmtiEnv *jvmti = nullptr;
static int class_counter = 0;
static int other_counter = 0;
static jint JNICALL
heap_reference_callback(jvmtiHeapReferenceKind reference_kind,
const jvmtiHeapReferenceInfo* reference_info,
jlong class_tag,
jlong referrer_class_tag,
jlong size,
jlong* tag_ptr,
jlong* referrer_tag_ptr,
jint length,
void* user_data) {
switch (reference_kind) {
case JVMTI_HEAP_REFERENCE_SYSTEM_CLASS:
*tag_ptr = ++class_counter;
break;
case JVMTI_HEAP_REFERENCE_OTHER:
++other_counter;
break;
default:
break;
}
return JVMTI_VISIT_OBJECTS;
}
extern "C" JNIEXPORT jint JNICALL
Java_KindSystemClass_tagSysClasses(JNIEnv* jni, jclass clazz) {
jvmtiHeapCallbacks callbacks = {};
callbacks.heap_reference_callback = heap_reference_callback;
jvmtiError err = jvmti->FollowReferences(0 /* filter nothing */,
nullptr /* no class filter */,
nullptr /* no initial object, follow roots */,
&callbacks,
nullptr);
check_jvmti_error(err, "FollowReferences failed");
LOG("JVMTI_HEAP_REFERENCE_SYSTEM_CLASS: %d, JVMTI_HEAP_REFERENCE_OTHER: %d\n", class_counter, other_counter);
return class_counter;
}
extern "C" JNIEXPORT jobjectArray JNICALL
Java_KindSystemClass_getObjectsWithTags(JNIEnv* jni, jclass clazz) {
// request tagged objects with tags 1..class_counter
jlong* tags = nullptr;
jvmtiError err = jvmti->Allocate(class_counter * sizeof(jlong), (unsigned char**)&tags);
check_jvmti_error(err, "Allocate failed");
for (int i = 0; i < class_counter; i++) {
tags[i] = i + 1;
}
jint count = 0;
jobject* objects = nullptr;
err = jvmti->GetObjectsWithTags(class_counter, tags,
&count, &objects, nullptr);
check_jvmti_error(err, "GetObjectsWithTags failed");
jclass object_klass = jni->FindClass("java/lang/Object");
jobjectArray array = jni->NewObjectArray(count, object_klass, nullptr);
for (jint i = 0; i < count; i++) {
jni->SetObjectArrayElement(array, i, objects[i]);
}
deallocate(jvmti, jni, objects);
return array;
}
extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION) != JNI_OK || !jvmti) {
LOG("Could not initialize JVMTI\n");
abort();
}
jvmtiCapabilities capabilities;
memset(&capabilities, 0, sizeof(capabilities));
capabilities.can_tag_objects = 1;
check_jvmti_error(jvmti->AddCapabilities(&capabilities), "adding capabilities");
return JVMTI_ERROR_NONE;
}