1 /* 2 * Copyright (c) 2018, Google 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 <assert.h> 25 #include <pthread.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include "jvmti.h" 30 31 #ifdef __cplusplus 32 extern "C" { 33 #endif 34 35 #ifndef JNI_ENV_ARG 36 37 #ifdef __cplusplus 38 #define JNI_ENV_ARG(x, y) y 39 #define JNI_ENV_PTR(x) x 40 #else 41 #define JNI_ENV_ARG(x,y) x, y 42 #define JNI_ENV_PTR(x) (*x) 43 #endif 44 45 #endif 46 47 #define TRUE 1 48 #define FALSE 0 49 #define PRINT_OUT 1 50 #define MAX_TRACES 400 51 52 static jvmtiEnv *jvmti = NULL; 53 54 typedef struct _ObjectTrace{ 55 jweak object; 56 size_t size; 57 jvmtiFrameInfo* frames; 58 size_t frame_count; 59 jthread thread; 60 } ObjectTrace; 61 62 typedef struct _EventStorage { 63 int live_object_size; 64 int live_object_count; 65 ObjectTrace** live_objects; 66 67 int garbage_history_size; 68 int garbage_history_index; 69 ObjectTrace** garbage_collected_objects; 70 71 // Two separate mutexes to separate storage data race and the compaction field 72 // data race. 73 pthread_mutex_t storage_mutex; 74 75 int compaction_required; 76 pthread_mutex_t compaction_mutex; 77 } EventStorage; 78 79 typedef struct _ExpectedContentFrame { 80 const char *name; 81 const char *signature; 82 const char *file_name; 83 int line_number; 84 } ExpectedContentFrame; 85 86 // Given a method and a location, this method gets the line number. 87 static 88 jint get_line_number(jvmtiEnv *jvmti, jmethodID method, 89 jlocation location) { 90 // Read the line number table. 91 jvmtiLineNumberEntry *table_ptr = 0; 92 jint line_number_table_entries; 93 int jvmti_error = (*jvmti)->GetLineNumberTable(jvmti, method, 94 &line_number_table_entries, 95 &table_ptr); 96 97 if (JVMTI_ERROR_NONE != jvmti_error) { 98 return -1; 99 } 100 if (line_number_table_entries <= 0) { 101 return -1; 102 } 103 if (line_number_table_entries == 1) { 104 return table_ptr[0].line_number; 105 } 106 107 // Go through all the line numbers... 108 jint last_location = table_ptr[0].start_location; 109 int l; 110 for (l = 1; l < line_number_table_entries; l++) { 111 // ... and if you see one that is in the right place for your 112 // location, you've found the line number! 113 if ((location < table_ptr[l].start_location) && 114 (location >= last_location)) { 115 return table_ptr[l - 1].line_number; 116 } 117 last_location = table_ptr[l].start_location; 118 } 119 120 if (location >= last_location) { 121 return table_ptr[line_number_table_entries - 1].line_number; 122 } else { 123 return -1; 124 } 125 } 126 127 static jboolean check_sample_content(JNIEnv *env, 128 ObjectTrace* trace, 129 ExpectedContentFrame *expected, 130 size_t expected_count, 131 int print_out_comparisons) { 132 if (expected_count > trace->frame_count) { 133 return FALSE; 134 } 135 136 jvmtiFrameInfo* frames = trace->frames; 137 138 size_t i; 139 for (i = 0; i < expected_count; i++) { 140 // Get basic information out of the trace. 141 int bci = frames[i].location; 142 jmethodID methodid = frames[i].method; 143 char *name = NULL, *signature = NULL, *file_name = NULL; 144 145 if (bci < 0) { 146 return FALSE; 147 } 148 149 // Transform into usable information. 150 int line_number = get_line_number(jvmti, methodid, bci); 151 (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0); 152 153 jclass declaring_class; 154 if (JVMTI_ERROR_NONE != 155 (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) { 156 return FALSE; 157 } 158 159 jvmtiError err = (*jvmti)->GetSourceFileName(jvmti, declaring_class, 160 &file_name); 161 if (err != JVMTI_ERROR_NONE) { 162 return FALSE; 163 } 164 165 // Compare now, none should be NULL. 166 if (name == NULL) { 167 return FALSE; 168 } 169 170 if (file_name == NULL) { 171 return FALSE; 172 } 173 174 if (signature == NULL) { 175 return FALSE; 176 } 177 178 if (print_out_comparisons) { 179 fprintf(stderr, "\tComparing:\n"); 180 fprintf(stderr, "\t\tNames: %s and %s\n", name, expected[i].name); 181 fprintf(stderr, "\t\tSignatures: %s and %s\n", signature, expected[i].signature); 182 fprintf(stderr, "\t\tFile name: %s and %s\n", file_name, expected[i].file_name); 183 fprintf(stderr, "\t\tLines: %d and %d\n", line_number, expected[i].line_number); 184 fprintf(stderr, "\t\tResult is %d\n", 185 (strcmp(name, expected[i].name) || 186 strcmp(signature, expected[i].signature) || 187 strcmp(file_name, expected[i].file_name) || 188 line_number != expected[i].line_number)); 189 } 190 191 if (strcmp(name, expected[i].name) || 192 strcmp(signature, expected[i].signature) || 193 strcmp(file_name, expected[i].file_name) || 194 line_number != expected[i].line_number) { 195 return FALSE; 196 } 197 } 198 199 return TRUE; 200 } 201 202 // Static native API for various tests. 203 static void fill_native_frames(JNIEnv* env, jobjectArray frames, 204 ExpectedContentFrame* native_frames, size_t size) { 205 size_t i; 206 for (i = 0; i < size; i++) { 207 jobject obj = (*env)->GetObjectArrayElement(env, frames, i); 208 jclass frame_class = (*env)->GetObjectClass(env, obj); 209 jfieldID line_number_field_id = (*env)->GetFieldID(env, frame_class, 210 "lineNumber", "I"); 211 int line_number = (*env)->GetIntField(env, obj, line_number_field_id); 212 213 jfieldID string_id = (*env)->GetFieldID(env, frame_class, "method", 214 "Ljava/lang/String;"); 215 jstring string_object = (jstring) (*env)->GetObjectField(env, obj, 216 string_id); 217 const char* method = (*env)->GetStringUTFChars(env, string_object, 0); 218 219 string_id = (*env)->GetFieldID(env, frame_class, "fileName", 220 "Ljava/lang/String;"); 221 string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); 222 const char* file_name = (*env)->GetStringUTFChars(env, string_object, 0); 223 224 string_id = (*env)->GetFieldID(env, frame_class, "signature", 225 "Ljava/lang/String;"); 226 string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); 227 const char* signature= (*env)->GetStringUTFChars(env, string_object, 0); 228 229 native_frames[i].name = method; 230 native_frames[i].file_name = file_name; 231 native_frames[i].signature = signature; 232 native_frames[i].line_number = line_number; 233 } 234 } 235 236 // Internal storage system implementation. 237 238 static EventStorage global_event_storage; 239 240 static void event_storage_set_compaction_required(EventStorage* storage) { 241 pthread_mutex_lock(&storage->compaction_mutex); 242 storage->compaction_required = 1; 243 pthread_mutex_unlock(&storage->compaction_mutex); 244 } 245 246 static int event_storage_get_compaction_required(EventStorage* storage) { 247 pthread_mutex_lock(&storage->compaction_mutex); 248 int result = storage->compaction_required; 249 pthread_mutex_unlock(&storage->compaction_mutex); 250 return result; 251 } 252 253 static void event_storage_set_garbage_history(EventStorage* storage, int value) { 254 pthread_mutex_lock(&storage->storage_mutex); 255 global_event_storage.garbage_history_size = value; 256 free(global_event_storage.garbage_collected_objects); 257 size_t size = 258 sizeof(*global_event_storage.garbage_collected_objects) * value; 259 global_event_storage.garbage_collected_objects = malloc(size); 260 memset(global_event_storage.garbage_collected_objects, 0, size); 261 pthread_mutex_unlock(&storage->storage_mutex); 262 } 263 264 // No mutex here, it is handled by the caller. 265 static void event_storage_add_garbage_collected_object(EventStorage* storage, 266 ObjectTrace* object) { 267 int idx = storage->garbage_history_index; 268 free(storage->garbage_collected_objects[idx]); 269 storage->garbage_collected_objects[idx] = object; 270 storage->garbage_history_index = (idx + 1) % storage->garbage_history_size; 271 } 272 273 static int event_storage_get_count(EventStorage* storage) { 274 pthread_mutex_lock(&storage->storage_mutex); 275 int result = storage->live_object_count; 276 pthread_mutex_unlock(&storage->storage_mutex); 277 return result; 278 } 279 280 static double event_storage_get_average_rate(EventStorage* storage) { 281 double accumulation = 0; 282 283 pthread_mutex_lock(&storage->storage_mutex); 284 int max_size = storage->live_object_count; 285 286 int i; 287 for (i = 0; i < max_size; i++) { 288 accumulation += storage->live_objects[i]->size; 289 } 290 pthread_mutex_unlock(&storage->storage_mutex); 291 return accumulation / max_size; 292 } 293 294 static jboolean event_storage_contains(JNIEnv* env, 295 EventStorage* storage, 296 ExpectedContentFrame* frames, 297 size_t size) { 298 pthread_mutex_lock(&storage->storage_mutex); 299 fprintf(stderr, "Checking storage count %d\n", storage->live_object_count); 300 int i; 301 for (i = 0; i < storage->live_object_count; i++) { 302 ObjectTrace* trace = storage->live_objects[i]; 303 304 if (check_sample_content(env, trace, frames, size, PRINT_OUT)) { 305 pthread_mutex_unlock(&storage->storage_mutex); 306 return TRUE; 307 } 308 } 309 pthread_mutex_unlock(&storage->storage_mutex); 310 return FALSE; 311 } 312 313 static jboolean event_storage_garbage_contains(JNIEnv* env, 314 EventStorage* storage, 315 ExpectedContentFrame* frames, 316 size_t size) { 317 pthread_mutex_lock(&storage->storage_mutex); 318 fprintf(stderr, "Checking garbage storage count %d\n", 319 storage->garbage_history_size); 320 int i; 321 for (i = 0; i < storage->garbage_history_size; i++) { 322 ObjectTrace* trace = storage->garbage_collected_objects[i]; 323 324 if (trace == NULL) { 325 continue; 326 } 327 328 if (check_sample_content(env, trace, frames, size, PRINT_OUT)) { 329 pthread_mutex_unlock(&storage->storage_mutex); 330 return TRUE; 331 } 332 } 333 pthread_mutex_unlock(&storage->storage_mutex); 334 return FALSE; 335 } 336 337 // No mutex here, handled by the caller. 338 static void event_storage_augment_storage(EventStorage* storage) { 339 int new_max = (storage->live_object_size * 2) + 1; 340 ObjectTrace** new_objects = malloc(new_max * sizeof(*new_objects)); 341 342 int current_count = storage->live_object_count; 343 memcpy(new_objects, storage->live_objects, current_count * sizeof(*new_objects)); 344 free(storage->live_objects); 345 storage->live_objects = new_objects; 346 347 storage->live_object_size = new_max; 348 } 349 350 static void event_storage_add(EventStorage* storage, 351 JNIEnv* jni, 352 jthread thread, 353 jobject object, 354 jclass klass, 355 jlong size) { 356 jvmtiFrameInfo frames[64]; 357 jint count; 358 jvmtiError err; 359 360 err = (*jvmti)->GetStackTrace(jvmti, thread, 0, 64, frames, &count); 361 if (err == JVMTI_ERROR_NONE && count >= 1) { 362 jvmtiFrameInfo* allocated_frames = (jvmtiFrameInfo*) malloc(count * sizeof(*allocated_frames)); 363 memcpy(allocated_frames, frames, count * sizeof(*allocated_frames)); 364 365 ObjectTrace* live_object = (ObjectTrace*) malloc(sizeof(*live_object)); 366 live_object->frames = allocated_frames; 367 live_object->frame_count = count; 368 live_object->size = size; 369 live_object->thread = thread; 370 live_object->object = (*jni)->NewWeakGlobalRef(jni, object); 371 372 // Only now lock and get things done quickly. 373 pthread_mutex_lock(&storage->storage_mutex); 374 375 if (storage->live_object_count >= storage->live_object_size) { 376 event_storage_augment_storage(storage); 377 } 378 assert(storage->live_object_count < storage->live_object_size); 379 380 storage->live_objects[storage->live_object_count] = live_object; 381 storage->live_object_count++; 382 383 pthread_mutex_unlock(&storage->storage_mutex); 384 } 385 } 386 387 static void event_storage_compact(EventStorage* storage, JNIEnv* jni) { 388 pthread_mutex_lock(&storage->compaction_mutex); 389 storage->compaction_required = 0; 390 pthread_mutex_unlock(&storage->compaction_mutex); 391 392 pthread_mutex_lock(&storage->storage_mutex); 393 394 int max = storage->live_object_count; 395 int i, dest; 396 ObjectTrace** live_objects = storage->live_objects; 397 398 for (i = 0, dest = 0; i < max; i++) { 399 ObjectTrace* live_object = live_objects[i]; 400 jweak object = live_object->object; 401 402 if (!(*jni)->IsSameObject(jni, object, NULL)) { 403 if (dest != i) { 404 live_objects[dest] = live_object; 405 dest++; 406 } 407 } else { 408 event_storage_add_garbage_collected_object(storage, live_object); 409 } 410 } 411 412 storage->live_object_count = dest; 413 pthread_mutex_unlock(&storage->storage_mutex); 414 } 415 416 static void event_storage_free_objects(ObjectTrace** array, int max) { 417 int i; 418 for (i = 0; i < max; i++) { 419 free(array[i]), array[i] = NULL; 420 } 421 } 422 423 static void event_storage_reset(EventStorage* storage) { 424 pthread_mutex_lock(&storage->storage_mutex); 425 426 // Reset everything except the mutex and the garbage collection. 427 event_storage_free_objects(storage->live_objects, 428 storage->live_object_count); 429 storage->live_object_size = 0; 430 storage->live_object_count = 0; 431 free(storage->live_objects), storage->live_objects = NULL; 432 433 event_storage_free_objects(storage->garbage_collected_objects, 434 storage->garbage_history_size); 435 436 storage->compaction_required = 0; 437 storage->garbage_history_index = 0; 438 439 pthread_mutex_unlock(&storage->storage_mutex); 440 } 441 442 // Start of the JVMTI agent code. 443 444 static const char *EXC_CNAME = "java/lang/Exception"; 445 446 static int check_error(jvmtiError err, const char *s) { 447 if (err != JVMTI_ERROR_NONE) { 448 printf(" ## %s error: %d\n", s, err); 449 return 1; 450 } 451 return 0; 452 } 453 454 static int check_capability_error(jvmtiError err, const char *s) { 455 if (err != JVMTI_ERROR_NONE) { 456 if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY) { 457 return 0; 458 } 459 printf(" ## %s error: %d\n", s, err); 460 return 1; 461 } 462 return 1; 463 } 464 465 static 466 jint throw_exc(JNIEnv *env, char *msg) { 467 jclass exc_class = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, EXC_CNAME)); 468 469 if (exc_class == NULL) { 470 printf("throw_exc: Error in FindClass(env, %s)\n", EXC_CNAME); 471 return -1; 472 } 473 return JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg); 474 } 475 476 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); 477 478 JNIEXPORT 479 jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { 480 return Agent_Initialize(jvm, options, reserved); 481 } 482 483 JNIEXPORT 484 jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { 485 return Agent_Initialize(jvm, options, reserved); 486 } 487 488 JNIEXPORT 489 jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { 490 return JNI_VERSION_1_8; 491 } 492 493 JNIEXPORT 494 void JNICALL SampledObjectAlloc(jvmtiEnv *jvmti_env, 495 JNIEnv* jni_env, 496 jthread thread, 497 jobject object, 498 jclass object_klass, 499 jlong size) { 500 if (event_storage_get_compaction_required(&global_event_storage)) { 501 event_storage_compact(&global_event_storage, jni_env); 502 } 503 504 event_storage_add(&global_event_storage, jni_env, thread, object, 505 object_klass, size); 506 } 507 508 JNIEXPORT 509 void JNICALL GarbageCollectionFinish(jvmtiEnv *jvmti_env) { 510 event_storage_set_compaction_required(&global_event_storage); 511 } 512 513 static int enable_notifications() { 514 if (check_error((*jvmti)->SetEventNotificationMode( 515 jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL), 516 "Set event notifications")) { 517 return 1; 518 } 519 520 return check_error((*jvmti)->SetEventNotificationMode( 521 jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL), 522 "Set event notifications"); 523 } 524 525 static 526 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { 527 jint res; 528 529 res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), 530 JVMTI_VERSION_9); 531 if (res != JNI_OK || jvmti == NULL) { 532 printf(" Error: wrong result of a valid call to GetEnv!\n"); 533 return JNI_ERR; 534 } 535 536 pthread_mutex_init(&global_event_storage.storage_mutex, 0); 537 pthread_mutex_init(&global_event_storage.compaction_mutex, 0); 538 event_storage_set_garbage_history(&global_event_storage, 200); 539 540 jvmtiEventCallbacks callbacks; 541 memset(&callbacks, 0, sizeof(callbacks)); 542 callbacks.SampledObjectAlloc = &SampledObjectAlloc; 543 callbacks.GarbageCollectionFinish = &GarbageCollectionFinish; 544 545 jvmtiCapabilities caps; 546 memset(&caps, 0, sizeof(caps)); 547 // Get line numbers, sample heap, sample events, and filename for the test. 548 caps.can_get_line_numbers = 1; 549 caps.can_generate_sampled_alloc_events = 1; 550 caps.can_get_source_file_name = 1; 551 caps.can_generate_garbage_collection_events = 1; 552 if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), "Add capabilities")){ 553 return JNI_ERR; 554 } 555 556 if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks, 557 sizeof(jvmtiEventCallbacks)), 558 " Set Event Callbacks")) { 559 return JNI_ERR; 560 } 561 return JNI_OK; 562 } 563 564 JNIEXPORT void JNICALL 565 Java_MyPackage_HeapMonitor_setSamplingRate(JNIEnv* env, jclass cls, jint value) { 566 (*jvmti)->SetHeapSamplingRate(jvmti, value); 567 } 568 569 JNIEXPORT void JNICALL 570 Java_MyPackage_HeapMonitor_setGarbageHistory(JNIEnv* env, jclass cls, jint value) { 571 event_storage_set_garbage_history(&global_event_storage, value); 572 } 573 574 JNIEXPORT jboolean JNICALL 575 Java_MyPackage_HeapMonitor_eventStorageIsEmpty(JNIEnv* env, jclass cls) { 576 return event_storage_get_count(&global_event_storage) == 0; 577 } 578 579 JNIEXPORT jint JNICALL 580 Java_MyPackage_HeapMonitor_getEventStorageElementCount(JNIEnv* env, jclass cls) { 581 return event_storage_get_count(&global_event_storage); 582 } 583 584 JNIEXPORT void JNICALL 585 Java_MyPackage_HeapMonitor_enableSamplingEvents(JNIEnv* env, jclass cls) { 586 enable_notifications(); 587 } 588 589 JNIEXPORT void JNICALL 590 Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) { 591 check_error((*jvmti)->SetEventNotificationMode( 592 jvmti, JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL), 593 "Set event notifications"); 594 595 check_error((*jvmti)->SetEventNotificationMode( 596 jvmti, JVMTI_DISABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL), 597 "Garbage Collection Finish"); 598 } 599 600 JNIEXPORT jboolean JNICALL 601 Java_MyPackage_HeapMonitor_obtainedEvents(JNIEnv* env, jclass cls, jobjectArray frames) { 602 jsize size = (*env)->GetArrayLength(env, frames); 603 ExpectedContentFrame native_frames[size]; 604 fill_native_frames(env, frames, native_frames, size); 605 return event_storage_contains(env, &global_event_storage, native_frames, size); 606 } 607 608 JNIEXPORT jboolean JNICALL 609 Java_MyPackage_HeapMonitor_garbageContains(JNIEnv* env, jclass cls, jobjectArray frames) { 610 jsize size = (*env)->GetArrayLength(env, frames); 611 ExpectedContentFrame native_frames[size]; 612 fill_native_frames(env, frames, native_frames, size); 613 return event_storage_garbage_contains(env, &global_event_storage, native_frames, size); 614 } 615 616 JNIEXPORT void JNICALL 617 Java_MyPackage_HeapMonitor_forceGarbageCollection(JNIEnv* env, jclass cls) { 618 check_error((*jvmti)->ForceGarbageCollection(jvmti), 619 "Forced Garbage Collection"); 620 } 621 622 JNIEXPORT void JNICALL 623 Java_MyPackage_HeapMonitor_resetEventStorage(JNIEnv* env, jclass cls) { 624 return event_storage_reset(&global_event_storage); 625 } 626 627 JNIEXPORT jboolean JNICALL 628 Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env, 629 jclass cls) { 630 jvmtiCapabilities caps; 631 memset(&caps, 0, sizeof(caps)); 632 caps.can_generate_sampled_alloc_events = 1; 633 if (check_error((*jvmti)->RelinquishCapabilities(jvmti, &caps), 634 "Add capabilities\n")){ 635 return FALSE; 636 } 637 638 if (check_capability_error((*jvmti)->SetHeapSamplingRate(jvmti, 1<<19), 639 "Set Heap Sampling Rate")) { 640 return FALSE; 641 } 642 return TRUE; 643 } 644 645 JNIEXPORT jdouble JNICALL 646 Java_MyPackage_HeapMonitorStatRateTest_getAverageRate(JNIEnv *env, jclass cls) { 647 return event_storage_get_average_rate(&global_event_storage); 648 } 649 650 typedef struct sThreadsFound { 651 jthread* threads; 652 int num_threads; 653 } ThreadsFound; 654 655 static void find_threads_in_array(ThreadsFound* thread_data, 656 ObjectTrace** array, 657 int array_size) { 658 int i; 659 jthread* threads = thread_data->threads; 660 int num_threads = thread_data->num_threads; 661 662 for (i = 0; i < array_size; i++) { 663 ObjectTrace* object = array[i]; 664 665 if (object == NULL) { 666 continue; 667 } 668 669 // Check it is the right frame: only accept helper top framed traces. 670 if (object->frame_count == 0) { 671 continue; 672 } 673 674 jvmtiFrameInfo* frames = object->frames; 675 jthread thread = object->thread; 676 677 jmethodID methodid = frames[0].method; 678 char *name = NULL, *signature = NULL, *file_name = NULL; 679 (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0); 680 681 if (strcmp(name, "helper")) { 682 continue; 683 } 684 685 // Really not efficient look-up but it's for a test... 686 int found = 0; 687 int j; 688 for (j = 0; j < num_threads; j++) { 689 if (thread == threads[j]) { 690 found = 1; 691 break; 692 } 693 } 694 695 if (!found) { 696 threads[num_threads] = thread; 697 num_threads++; 698 } 699 } 700 thread_data->num_threads = num_threads; 701 } 702 703 JNIEXPORT jboolean JNICALL 704 Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls, 705 jint num_threads) { 706 pthread_mutex_lock(&global_event_storage.storage_mutex); 707 jint trace_counter; 708 709 ThreadsFound thread_data; 710 thread_data.num_threads = 0; 711 thread_data.threads = malloc(sizeof(jthread) * num_threads); 712 memset(thread_data.threads, 0, sizeof(jthread) * num_threads); 713 714 find_threads_in_array(&thread_data, global_event_storage.live_objects, 715 global_event_storage.live_object_count); 716 find_threads_in_array(&thread_data, 717 global_event_storage.garbage_collected_objects, 718 global_event_storage.garbage_history_size); 719 720 free(thread_data.threads); 721 pthread_mutex_unlock(&global_event_storage.storage_mutex); 722 return thread_data.num_threads == num_threads; 723 } 724 725 #ifdef __cplusplus 726 } 727 #endif