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