1 /*
2 * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 #include <jvmti.h>
25 #include "jvmti_common.hpp"
26
27 static jvmtiEnv *jvmti = nullptr;
28 struct CallbackData {
29 jlong tag;
30 jint counter;
31
32 CallbackData(jlong tag): tag(tag), counter(0) {}
33 };
34
35 static jint JNICALL
36 heap_reference_callback(jvmtiHeapReferenceKind reference_kind,
37 const jvmtiHeapReferenceInfo* reference_info,
38 jlong class_tag,
39 jlong referrer_class_tag,
40 jlong size,
41 jlong* tag_ptr,
42 jlong* referrer_tag_ptr,
43 jint length,
44 void* user_data) {
45 if (reference_kind == JVMTI_HEAP_REFERENCE_SYSTEM_CLASS) {
46 CallbackData* data = (CallbackData*)user_data;
47 data->counter++;
48 *tag_ptr = data->tag;
49 }
50 return JVMTI_VISIT_OBJECTS;
51 }
52
53 extern "C" JNIEXPORT jint JNICALL
54 Java_HeapwalkDupClasses_tagWithFollowReferences(JNIEnv* jni, jclass clazz, jlong tag) {
55 jvmtiHeapCallbacks callbacks = {};
56 callbacks.heap_reference_callback = heap_reference_callback;
57
58 CallbackData data(tag);
59
60 jvmtiError err = jvmti->FollowReferences(0 /* filter nothing */,
61 nullptr /* no class filter */,
62 nullptr /* no initial object, follow roots */,
63 &callbacks,
64 &data);
65 check_jvmti_error(err, "FollowReferences failed");
66
67 return data.counter;
68 }
69
70 static jvmtiIterationControl JNICALL
71 heap_root_callback(jvmtiHeapRootKind root_kind,
72 jlong class_tag,
73 jlong size,
74 jlong* tag_ptr,
75 void* user_data) {
76 if (root_kind == JVMTI_HEAP_ROOT_SYSTEM_CLASS) {
77 CallbackData* data = (CallbackData*)user_data;
78 data->counter++;
79 *tag_ptr = data->tag;
80 }
81 return JVMTI_ITERATION_CONTINUE;
82 }
83
84 extern "C" JNIEXPORT jint JNICALL
85 Java_HeapwalkDupClasses_tagWithIterateOverReachableObjects(JNIEnv* jni, jclass clazz, jlong tag) {
86 CallbackData data(tag);
87 jvmtiError err = jvmti->IterateOverReachableObjects(heap_root_callback,
88 nullptr,
89 nullptr,
90 &data);
91 check_jvmti_error(err, "IterateOverReachableObjects failed");
92
93 return data.counter;
94 }
95
96 extern "C" JNIEXPORT jobjectArray JNICALL
97 Java_HeapwalkDupClasses_getObjectsWithTags(JNIEnv* jni, jclass clazz, jlong tag) {
98 jlong tags[1] = {tag};
99
100 jint count = 0;
101 jobject* objects = nullptr;
102
103 jvmtiError err = jvmti->GetObjectsWithTags(1, tags,
104 &count, &objects, nullptr);
105 check_jvmti_error(err, "GetObjectsWithTags failed");
106
107 jclass object_klass = jni->FindClass("java/lang/Object");
108 jobjectArray array = jni->NewObjectArray(count, object_klass, nullptr);
109
110 for (jint i = 0; i < count; i++) {
111 jni->SetObjectArrayElement(array, i, objects[i]);
112 }
113
114 deallocate(jvmti, jni, objects);
115
116 return array;
117 }
118
119 extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
120 if (vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION) != JNI_OK || !jvmti) {
121 LOG("Could not initialize JVMTI\n");
122 abort();
123 }
124 jvmtiCapabilities capabilities;
125 memset(&capabilities, 0, sizeof(capabilities));
126 capabilities.can_tag_objects = 1;
127 check_jvmti_error(jvmti->AddCapabilities(&capabilities), "adding capabilities");
128 return JVMTI_ERROR_NONE;
129 }