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 29 extern "C" JNIEXPORT void JNICALL 30 Java_ValueHeapwalkingTest_setTag(JNIEnv* jni_env, jclass clazz, jobject object, jlong tag) { 31 jvmtiError err = jvmti->SetTag(object, tag); 32 check_jvmti_error(err, "could not set tag"); 33 } 34 35 extern "C" JNIEXPORT jlong JNICALL 36 Java_ValueHeapwalkingTest_getTag(JNIEnv* jni_env, jclass clazz, jobject object) { 37 jlong tag; 38 check_jvmti_error(jvmti->GetTag(object, &tag), "could not get tag"); 39 return tag; 40 } 41 42 const int TAG_VALUE_CLASS = 1; 43 const int TAG_VALUE2_CLASS = 2; 44 const int TAG_HOLDER_CLASS = 3; 45 const int TAG_VALUE_ARRAY = 4; 46 const int TAG_VALUE3_ARRAY = 5; 47 const int MAX_TAG = 5; 48 const int START_TAG = 10; // start value for tagging objects 49 50 static const char* tag_str(jlong tag) { 51 switch (tag) { 52 case 0: return "None"; 53 case TAG_VALUE_CLASS: return "Value class"; 54 case TAG_VALUE2_CLASS: return "Value2 class"; 55 case TAG_HOLDER_CLASS: return "ValueHolder class"; 56 case TAG_VALUE_ARRAY: return "Value[] object"; 57 case TAG_VALUE3_ARRAY: return "Value2[] object"; 58 } 59 return "Unknown"; 60 } 61 62 struct Callback_Data { 63 // Updated by heap_iteration_callback. 64 jint counters[MAX_TAG + 1]; 65 // Updated by heap_reference_callback. 66 jint ref_counters[MAX_TAG + 1][MAX_TAG + 1]; 67 // Updated by primitive_field_callback. 68 jint primitive_counters[MAX_TAG + 1]; 69 jlong tag_counter; 70 }; 71 72 static Callback_Data callbackData; 73 74 extern "C" JNIEXPORT void JNICALL 75 Java_ValueHeapwalkingTest_reset(JNIEnv* jni_env, jclass clazz) { 76 memset(&callbackData, 0, sizeof(callbackData)); 77 callbackData.tag_counter = START_TAG; 78 } 79 80 extern "C" JNIEXPORT jint JNICALL 81 Java_ValueHeapwalkingTest_count(JNIEnv* jni_env, jclass clazz, jint tag) { 82 return callbackData.counters[tag]; 83 } 84 85 extern "C" JNIEXPORT jint JNICALL 86 Java_ValueHeapwalkingTest_refCount(JNIEnv* jni_env, jclass clazz, jint fromTag, jint toTag) { 87 return callbackData.ref_counters[fromTag][toTag]; 88 } 89 90 extern "C" JNIEXPORT jint JNICALL 91 Java_ValueHeapwalkingTest_primitiveFieldCount(JNIEnv* jni_env, jclass clazz, jint tag) { 92 return callbackData.primitive_counters[tag]; 93 } 94 95 extern "C" JNIEXPORT jlong JNICALL 96 Java_ValueHeapwalkingTest_getMaxTag(JNIEnv* jni_env, jclass clazz) { 97 return callbackData.tag_counter; 98 } 99 100 static jlong safe_deref(jlong* ref) { 101 return ref == nullptr ? 0 : *ref; 102 } 103 104 static jint JNICALL 105 heap_iteration_callback(jlong class_tag, 106 jlong size, 107 jlong* tag_ptr, 108 jint length, 109 void* user_data) { 110 Callback_Data* data = (Callback_Data*)user_data; 111 112 if (class_tag != 0 && class_tag <= MAX_TAG) { 113 data->counters[class_tag]++; 114 printf("heap_iteration_callback: class_tag = %d (%s), tag = %d (%s), length = %d\n", 115 (int)class_tag, tag_str(class_tag), (int)*tag_ptr, tag_str(*tag_ptr), length); 116 fflush(nullptr); 117 } 118 return 0; 119 } 120 121 static jint JNICALL 122 heap_reference_callback(jvmtiHeapReferenceKind reference_kind, 123 const jvmtiHeapReferenceInfo* reference_info, 124 jlong class_tag, 125 jlong referrer_class_tag, 126 jlong size, 127 jlong* tag_ptr, 128 jlong* referrer_tag_ptr, 129 jint length, 130 void* user_data) { 131 Callback_Data* data = (Callback_Data*)user_data; 132 133 jlong tag = class_tag; 134 if (tag == 0 && *tag_ptr != 0 && *tag_ptr <= MAX_TAG) { 135 tag = *tag_ptr; 136 } 137 jlong referrer_tag = referrer_class_tag; 138 if (referrer_tag == 0 && safe_deref(referrer_tag_ptr) != 0 && safe_deref(referrer_tag_ptr) <= MAX_TAG) { 139 referrer_tag = *referrer_tag_ptr; 140 } 141 142 if (tag != 0 && referrer_tag != 0) { 143 // For testing we count only JVMTI_HEAP_REFERENCE_FIELD and JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT references. 144 if (reference_kind == JVMTI_HEAP_REFERENCE_FIELD || reference_kind == JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT) { 145 data->ref_counters[referrer_tag][tag]++; 146 } 147 148 jlong cur_tag = *tag_ptr; 149 char new_tag_str[64] = {}; 150 if (*tag_ptr == 0) { // i.e. class_tag != 0, but the object is untagged 151 *tag_ptr = ++data->tag_counter; 152 snprintf(new_tag_str, sizeof(new_tag_str), ", set tag to %d", (int)*tag_ptr); 153 } 154 printf("heap_reference_callback: kind = %d, class_tag = %d (%s), tag = %d (%s), referrer_tag = %d (%s) %s\n", 155 (int)reference_kind, (int)class_tag, tag_str(class_tag), (int)cur_tag, tag_str(*tag_ptr), 156 (int)referrer_tag, tag_str(referrer_tag), new_tag_str); 157 fflush(nullptr); 158 } 159 160 return JVMTI_VISIT_OBJECTS; 161 } 162 163 static jint JNICALL 164 primitive_field_callback(jvmtiHeapReferenceKind kind, 165 const jvmtiHeapReferenceInfo* info, 166 jlong object_class_tag, 167 jlong* object_tag_ptr, 168 jvalue value, 169 jvmtiPrimitiveType value_type, 170 void* user_data) { 171 Callback_Data* data = (Callback_Data*)user_data; 172 if (object_class_tag != 0) { 173 char value_str[64] = {}; 174 switch (value_type) { 175 case JVMTI_PRIMITIVE_TYPE_BOOLEAN: snprintf(value_str, sizeof(value_str), "(boolean) %s", value.z ? "true" : "false"); break; 176 case JVMTI_PRIMITIVE_TYPE_BYTE: snprintf(value_str, sizeof(value_str), "(byte) %d", value.b); break; 177 case JVMTI_PRIMITIVE_TYPE_CHAR: snprintf(value_str, sizeof(value_str), "(char) %c", value.c); break; 178 case JVMTI_PRIMITIVE_TYPE_SHORT: snprintf(value_str, sizeof(value_str), "(short): %d", value.s); break; 179 case JVMTI_PRIMITIVE_TYPE_INT: snprintf(value_str, sizeof(value_str), "(int): %d", value.i); break; 180 case JVMTI_PRIMITIVE_TYPE_LONG: snprintf(value_str, sizeof(value_str), "(long): %lld", (long long)value.j); break; 181 case JVMTI_PRIMITIVE_TYPE_FLOAT: snprintf(value_str, sizeof(value_str), "(float): %f", value.f); break; 182 case JVMTI_PRIMITIVE_TYPE_DOUBLE: snprintf(value_str, sizeof(value_str), "(double): %f", value.d); break; 183 default: snprintf(value_str, sizeof(value_str), "invalid_type %d (%c)", (int)value_type, (char)value_type); 184 } 185 186 if (object_class_tag != 0 && object_class_tag <= MAX_TAG) { 187 data->primitive_counters[object_class_tag]++; 188 if (*object_tag_ptr != 0) { 189 *object_tag_ptr = *object_tag_ptr; 190 } 191 } 192 193 printf("primitive_field_callback: kind = %d, class_tag = %d (%s), tag = %d (%s), value = %s\n", 194 (int)kind, (int)object_class_tag, tag_str(object_class_tag), 195 (int)*object_tag_ptr, tag_str(*object_tag_ptr), value_str); 196 fflush(nullptr); 197 } 198 return 0; 199 } 200 201 static jint JNICALL 202 array_primitive_value_callback(jlong class_tag, 203 jlong size, 204 jlong* tag_ptr, 205 jint element_count, 206 jvmtiPrimitiveType element_type, 207 const void* elements, 208 void* user_data) { 209 Callback_Data* data = (Callback_Data*)user_data; 210 if (class_tag != 0 || *tag_ptr != 0) { 211 printf("array_primitive_value_callback: class_tag = %d (%s), tag = %d (%s), element_count = %d, element_type = %c\n", 212 (int)class_tag, tag_str(class_tag), (int)*tag_ptr, tag_str(*tag_ptr), element_count, (char)element_type); 213 fflush(nullptr); 214 } 215 return 0; 216 } 217 218 static jint JNICALL 219 string_primitive_value_callback(jlong class_tag, 220 jlong size, 221 jlong* tag_ptr, 222 const jchar* value, 223 jint value_length, 224 void* user_data) { 225 Callback_Data* data = (Callback_Data*)user_data; 226 if (class_tag != 0 || *tag_ptr != 0) { 227 jchar value_copy[1024] = {}; // fills with 0 228 if (value_length > 1023) { 229 value_length = 1023; 230 } 231 memcpy(value_copy, value, value_length * sizeof(jchar)); 232 printf("string_primitive_value_callback: class_tag = %d (%s), tag = %d (%s), value=\"%ls\"\n", 233 (int)class_tag, tag_str(class_tag), (int)*tag_ptr, tag_str(*tag_ptr), (wchar_t*)value_copy); 234 fflush(nullptr); 235 } 236 return 0; 237 } 238 239 extern "C" JNIEXPORT void JNICALL 240 Java_ValueHeapwalkingTest_followReferences(JNIEnv* jni_env, jclass clazz) { 241 jvmtiHeapCallbacks callbacks = {}; 242 callbacks.heap_iteration_callback = heap_iteration_callback; 243 callbacks.heap_reference_callback = heap_reference_callback; 244 callbacks.primitive_field_callback = primitive_field_callback; 245 callbacks.array_primitive_value_callback = array_primitive_value_callback; 246 callbacks.string_primitive_value_callback = string_primitive_value_callback; 247 248 jvmtiError err = jvmti->FollowReferences(0 /* filter nothing */, 249 nullptr /* no class filter */, 250 nullptr /* no initial object, follow roots */, 251 &callbacks, 252 &callbackData); 253 check_jvmti_error(err, "FollowReferences failed"); 254 } 255 256 extern "C" JNIEXPORT void JNICALL 257 Java_ValueHeapwalkingTest_iterateThroughHeap(JNIEnv* jni_env, jclass clazz) { 258 jvmtiHeapCallbacks callbacks = {}; 259 callbacks.heap_iteration_callback = heap_iteration_callback; 260 callbacks.heap_reference_callback = heap_reference_callback; 261 callbacks.primitive_field_callback = primitive_field_callback; 262 callbacks.array_primitive_value_callback = array_primitive_value_callback; 263 callbacks.string_primitive_value_callback = string_primitive_value_callback; 264 265 jvmtiError err = jvmti->IterateThroughHeap(0 /* filter nothing */, 266 nullptr /* no class filter */, 267 &callbacks, 268 &callbackData); 269 check_jvmti_error(err, "IterateThroughHeap failed"); 270 } 271 272 extern "C" JNIEXPORT jint JNICALL 273 Java_ValueHeapwalkingTest_getObjectWithTags(JNIEnv* jni_env, jclass clazz, jlong minTag, jlong maxTag, jobjectArray objects, jlongArray tags) { 274 jsize len = jni_env->GetArrayLength(objects); 275 276 jint tag_count = (jint)(maxTag - minTag + 1); 277 jlong* scan_tags = nullptr; 278 check_jvmti_error(jvmti->Allocate(tag_count * sizeof(jlong), (unsigned char**)&scan_tags), 279 "Allocate failed"); 280 281 for (jlong i = 0; i < tag_count; i++) { 282 scan_tags[i] = i + minTag; 283 } 284 285 jint count = 0; 286 jobject* object_result = nullptr; 287 jlong* tag_result = nullptr; 288 289 check_jvmti_error(jvmti->GetObjectsWithTags(tag_count, scan_tags, &count, &object_result, &tag_result), 290 "GetObjectsWithTags failed"); 291 292 if (count > len) { 293 printf("GetObjectsWithTags returned too many entries: %d (object length is %d)\n", count, (int)len); 294 fflush(nullptr); 295 abort(); 296 } 297 298 for (jint i = 0; i < count; i++) { 299 jni_env->SetObjectArrayElement(objects, i, object_result[i]); 300 } 301 jni_env->SetLongArrayRegion(tags, 0, count, tag_result); 302 303 jvmti->Deallocate((unsigned char*)scan_tags); 304 jvmti->Deallocate((unsigned char*)object_result); 305 jvmti->Deallocate((unsigned char*)tag_result); 306 307 return count; 308 } 309 310 extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { 311 if (vm->GetEnv(reinterpret_cast<void **>(&jvmti), JVMTI_VERSION) != JNI_OK || !jvmti) { 312 LOG("Could not initialize JVMTI\n"); 313 abort(); 314 } 315 jvmtiCapabilities capabilities; 316 memset(&capabilities, 0, sizeof(capabilities)); 317 capabilities.can_tag_objects = 1; 318 check_jvmti_error(jvmti->AddCapabilities(&capabilities), "adding capabilities"); 319 return JVMTI_ERROR_NONE; 320 } 321