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