1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2018, Google and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 #include <assert.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) 39 #define JNI_ENV_ARG2(x, y) y 40 #define JNI_ENV_ARG3(x, y, z) y, z 41 #define JNI_ENV_ARG4(x, y, z, w) y, z, w 42 #define JNI_ENV_PTR(x) x 43 #else 44 #define JNI_ENV_ARG(x) x 45 #define JNI_ENV_ARG2(x, y) x, y 46 #define JNI_ENV_ARG3(x, y, z) x, y, z 47 #define JNI_ENV_ARG4(x, y, z, w) x, y, z, w 48 #define JNI_ENV_PTR(x) (*x) 49 #endif 50 51 #endif 52 53 #define TRUE 1 54 #define FALSE 0 55 #define PRINT_OUT 0 56 57 static jvmtiEnv *jvmti = NULL; 58 static jvmtiEnv *second_jvmti = NULL; 59 60 typedef struct _ObjectTrace{ 61 jweak object; 62 jlong size; 63 jvmtiFrameInfo* frames; 64 size_t frame_count; 65 jthread thread; 66 } ObjectTrace; 67 68 typedef struct _EventStorage { 69 int live_object_additions; 70 int live_object_size; 71 int live_object_count; 72 ObjectTrace** live_objects; 73 74 int garbage_history_size; 75 int garbage_history_index; 76 ObjectTrace** garbage_collected_objects; 77 78 // Two separate monitors to separate storage data race and the compaction field 79 // data race. 80 jrawMonitorID storage_monitor; 81 82 int compaction_required; 83 jrawMonitorID compaction_monitor; 84 } EventStorage; 85 86 typedef struct _ExpectedContentFrame { 87 const char *name; 88 const char *signature; 89 const char *file_name; 90 int line_number; 91 } ExpectedContentFrame; 92 93 static 94 void event_storage_lock(EventStorage* storage) { 95 (*jvmti)->RawMonitorEnter(jvmti, storage->storage_monitor); 96 } 97 98 static 99 void event_storage_unlock(EventStorage* storage) { 100 (*jvmti)->RawMonitorExit(jvmti, storage->storage_monitor); 101 } 102 103 static 104 void event_storage_lock_compaction(EventStorage* storage) { 105 (*jvmti)->RawMonitorEnter(jvmti, storage->compaction_monitor); 106 } 107 108 static 109 void event_storage_unlock_compaction(EventStorage* storage) { 110 (*jvmti)->RawMonitorExit(jvmti, storage->compaction_monitor); 111 } 112 113 // Given a method and a location, this method gets the line number. 114 static 115 jint get_line_number(jvmtiEnv* jvmti, jmethodID method, 116 jlocation location) { 117 // Read the line number table. 118 jvmtiLineNumberEntry *table_ptr = 0; 119 jint line_number_table_entries; 120 int l; 121 jlocation last_location; 122 int jvmti_error = (*jvmti)->GetLineNumberTable(jvmti, method, 123 &line_number_table_entries, 124 &table_ptr); 125 126 if (JVMTI_ERROR_NONE != jvmti_error) { 127 return -1; 128 } 129 if (line_number_table_entries <= 0) { 130 return -1; 131 } 132 if (line_number_table_entries == 1) { 133 return table_ptr[0].line_number; 134 } 135 136 // Go through all the line numbers... 137 last_location = table_ptr[0].start_location; 138 for (l = 1; l < line_number_table_entries; l++) { 139 // ... and if you see one that is in the right place for your 140 // location, you've found the line number! 141 if ((location < table_ptr[l].start_location) && 142 (location >= last_location)) { 143 return table_ptr[l - 1].line_number; 144 } 145 last_location = table_ptr[l].start_location; 146 } 147 148 if (location >= last_location) { 149 return table_ptr[line_number_table_entries - 1].line_number; 150 } else { 151 return -1; 152 } 153 } 154 155 static void print_out_frames(JNIEnv* env, ObjectTrace* trace) { 156 jvmtiFrameInfo* frames = trace->frames; 157 size_t i; 158 for (i = 0; i < trace->frame_count; i++) { 159 // Get basic information out of the trace. 160 jlocation bci = frames[i].location; 161 jmethodID methodid = frames[i].method; 162 char *name = NULL, *signature = NULL, *file_name = NULL; 163 jclass declaring_class; 164 int line_number; 165 jvmtiError err; 166 167 if (bci < 0) { 168 fprintf(stderr, "\tNative frame\n"); 169 continue; 170 } 171 172 // Transform into usable information. 173 line_number = get_line_number(jvmti, methodid, bci); 174 if (JVMTI_ERROR_NONE != 175 (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0)) { 176 fprintf(stderr, "\tUnknown method name\n"); 177 continue; 178 } 179 180 if (JVMTI_ERROR_NONE != 181 (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) { 182 fprintf(stderr, "\tUnknown class\n"); 183 continue; 184 } 185 186 err = (*jvmti)->GetSourceFileName(jvmti, declaring_class, 187 &file_name); 188 if (err != JVMTI_ERROR_NONE) { 189 fprintf(stderr, "\tUnknown file\n"); 190 continue; 191 } 192 193 // Compare now, none should be NULL. 194 if (name == NULL) { 195 fprintf(stderr, "\tUnknown name\n"); 196 continue; 197 } 198 199 if (file_name == NULL) { 200 fprintf(stderr, "\tUnknown file\n"); 201 continue; 202 } 203 204 if (signature == NULL) { 205 fprintf(stderr, "\tUnknown signature\n"); 206 continue; 207 } 208 209 fprintf(stderr, "\t%s%s (%s: %d)\n", 210 name, signature, file_name, line_number); 211 } 212 } 213 214 static jboolean check_sample_content(JNIEnv* env, 215 ObjectTrace* trace, 216 ExpectedContentFrame *expected, 217 size_t expected_count, 218 jboolean check_lines, 219 int print_out_comparisons) { 220 jvmtiFrameInfo* frames; 221 size_t i; 222 223 if (expected_count > trace->frame_count) { 224 return FALSE; 225 } 226 227 frames = trace->frames; 228 for (i = 0; i < expected_count; i++) { 229 // Get basic information out of the trace. 230 jlocation bci = frames[i].location; 231 jmethodID methodid = frames[i].method; 232 char *name = NULL, *signature = NULL, *file_name = NULL; 233 jclass declaring_class; 234 int line_number; 235 jboolean differ; 236 jvmtiError err; 237 238 if (bci < 0 && expected[i].line_number != -1) { 239 return FALSE; 240 } 241 242 // Transform into usable information. 243 line_number = get_line_number(jvmti, methodid, bci); 244 (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0); 245 246 if (JVMTI_ERROR_NONE != 247 (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) { 248 return FALSE; 249 } 250 251 err = (*jvmti)->GetSourceFileName(jvmti, declaring_class, 252 &file_name); 253 if (err != JVMTI_ERROR_NONE) { 254 return FALSE; 255 } 256 257 // Compare now, none should be NULL. 258 if (name == NULL) { 259 return FALSE; 260 } 261 262 if (file_name == NULL) { 263 return FALSE; 264 } 265 266 if (signature == NULL) { 267 return FALSE; 268 } 269 270 differ = (strcmp(name, expected[i].name) || 271 strcmp(signature, expected[i].signature) || 272 strcmp(file_name, expected[i].file_name) || 273 (check_lines && line_number != expected[i].line_number)); 274 275 if (print_out_comparisons) { 276 fprintf(stderr, "\tComparing: (check_lines: %d)\n", check_lines); 277 fprintf(stderr, "\t\tNames: %s and %s\n", name, expected[i].name); 278 fprintf(stderr, "\t\tSignatures: %s and %s\n", signature, expected[i].signature); 279 fprintf(stderr, "\t\tFile name: %s and %s\n", file_name, expected[i].file_name); 280 fprintf(stderr, "\t\tLines: %d and %d\n", line_number, expected[i].line_number); 281 fprintf(stderr, "\t\tResult is %d\n", differ); 282 } 283 284 if (differ) { 285 return FALSE; 286 } 287 } 288 289 return TRUE; 290 } 291 292 // Static native API for various tests. 293 static int fill_native_frames(JNIEnv* env, jobjectArray frames, 294 ExpectedContentFrame* native_frames, size_t size) { 295 size_t i; 296 for (i = 0; i < size; i++) { 297 jclass frame_class = NULL; 298 jfieldID line_number_field_id = 0; 299 int line_number = 0; 300 jfieldID string_id = 0; 301 jstring string_object = NULL; 302 const char* method = NULL; 303 const char* file_name = NULL; 304 const char* signature = NULL; 305 306 jobject obj = JNI_ENV_PTR(env)->GetObjectArrayElement( 307 JNI_ENV_ARG3(env, frames, (jsize) i)); 308 309 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 310 fprintf(stderr, "fill_native_frames: Exception in jni GetObjectArrayElement\n"); 311 return -1; 312 } 313 314 frame_class = JNI_ENV_PTR(env)->GetObjectClass(JNI_ENV_ARG2(env, obj)); 315 316 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 317 fprintf(stderr, "fill_native_frames: Exception in jni GetObjectClass\n"); 318 return -1; 319 } 320 321 line_number_field_id = 322 JNI_ENV_PTR(env)->GetFieldID(JNI_ENV_ARG4(env, frame_class, "lineNumber", "I")); 323 324 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 325 fprintf(stderr, "fill_native_frames: Exception in jni GetFieldID\n"); 326 return -1; 327 } 328 329 line_number = JNI_ENV_PTR(env)->GetIntField(JNI_ENV_ARG3(env, obj, line_number_field_id)); 330 331 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 332 fprintf(stderr, "fill_native_frames: Exception in jni GetIntField\n"); 333 return -1; 334 } 335 336 string_id = JNI_ENV_PTR(env)->GetFieldID( 337 JNI_ENV_ARG4(env, frame_class, "method", "Ljava/lang/String;")); 338 339 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 340 fprintf(stderr, "fill_native_frames: Exception in jni GetFieldID\n"); 341 return -1; 342 } 343 344 string_object = (jstring) JNI_ENV_PTR(env)->GetObjectField( 345 JNI_ENV_ARG3(env, obj, string_id)); 346 347 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 348 fprintf(stderr, "fill_native_frames: Exception in jni GetObjectField\n"); 349 return -1; 350 } 351 352 method = JNI_ENV_PTR(env)->GetStringUTFChars(JNI_ENV_ARG3(env, string_object, 0)); 353 354 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 355 fprintf(stderr, "Exception in jni GetStringUTFChars\n"); 356 return -1; 357 } 358 359 string_id = JNI_ENV_PTR(env)->GetFieldID( 360 JNI_ENV_ARG4(env, frame_class, "fileName", "Ljava/lang/String;")); 361 362 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 363 fprintf(stderr, "Exception in jni GetFieldID\n"); 364 return -1; 365 } 366 367 string_object = 368 (jstring) (JNI_ENV_PTR(env)->GetObjectField( 369 JNI_ENV_ARG3(env, obj, string_id))); 370 371 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 372 fprintf(stderr, "fill_native_frames: Exception in second jni GetObjectField\n"); 373 return -1; 374 } 375 376 file_name = JNI_ENV_PTR(env)->GetStringUTFChars( 377 JNI_ENV_ARG3(env, string_object, 0)); 378 379 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 380 fprintf(stderr, "fill_native_frames: Exception in jni GetStringUTFChars\n"); 381 return -1; 382 } 383 384 string_id = JNI_ENV_PTR(env)->GetFieldID( 385 JNI_ENV_ARG4(env, frame_class, "signature", "Ljava/lang/String;")); 386 387 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 388 fprintf(stderr, "fill_native_frames: Exception in second jni GetFieldID\n"); 389 return -1; 390 } 391 392 string_object = 393 (jstring) (JNI_ENV_PTR(env)->GetObjectField( 394 JNI_ENV_ARG3(env, obj, string_id))); 395 396 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 397 fprintf(stderr, "fill_native_frames: Exception in third jni GetObjectField\n"); 398 return -1; 399 } 400 401 signature = JNI_ENV_PTR(env)->GetStringUTFChars( 402 JNI_ENV_ARG3(env, string_object, 0)); 403 404 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 405 fprintf(stderr, "fill_native_frames: Exception in jni GetStringUTFChars\n"); 406 return -1; 407 } 408 409 native_frames[i].name = method; 410 native_frames[i].file_name = file_name; 411 native_frames[i].signature = signature; 412 native_frames[i].line_number = line_number; 413 } 414 415 return 0; 416 } 417 418 // Internal storage system implementation. 419 static EventStorage global_event_storage; 420 static EventStorage second_global_event_storage; 421 422 static void event_storage_set_compaction_required(EventStorage* storage) { 423 event_storage_lock_compaction(storage); 424 storage->compaction_required = 1; 425 event_storage_unlock_compaction(storage); 426 } 427 428 static int event_storage_get_compaction_required(EventStorage* storage) { 429 int result; 430 event_storage_lock_compaction(storage); 431 result = storage->compaction_required; 432 event_storage_unlock_compaction(storage); 433 return result; 434 } 435 436 static void event_storage_set_garbage_history(EventStorage* storage, int value) { 437 size_t size; 438 event_storage_lock(storage); 439 global_event_storage.garbage_history_size = value; 440 free(global_event_storage.garbage_collected_objects); 441 size = sizeof(*global_event_storage.garbage_collected_objects) * value; 442 global_event_storage.garbage_collected_objects = malloc(size); 443 memset(global_event_storage.garbage_collected_objects, 0, size); 444 event_storage_unlock(storage); 445 } 446 447 // No mutex here, it is handled by the caller. 448 static void event_storage_add_garbage_collected_object(EventStorage* storage, 449 ObjectTrace* object) { 450 int idx = storage->garbage_history_index; 451 ObjectTrace* old_object = storage->garbage_collected_objects[idx]; 452 if (old_object != NULL) { 453 free(old_object->frames); 454 free(storage->garbage_collected_objects[idx]); 455 } 456 457 storage->garbage_collected_objects[idx] = object; 458 storage->garbage_history_index = (idx + 1) % storage->garbage_history_size; 459 } 460 461 static int event_storage_get_count(EventStorage* storage) { 462 int result; 463 event_storage_lock(storage); 464 result = storage->live_object_count; 465 event_storage_unlock(storage); 466 return result; 467 } 468 469 static double event_storage_get_average_size(EventStorage* storage) { 470 double accumulation = 0; 471 int max_size; 472 int i; 473 474 event_storage_lock(storage); 475 max_size = storage->live_object_count; 476 477 for (i = 0; i < max_size; i++) { 478 accumulation += storage->live_objects[i]->size; 479 } 480 481 event_storage_unlock(storage); 482 return accumulation / max_size; 483 } 484 485 static jboolean event_storage_contains(JNIEnv* env, 486 EventStorage* storage, 487 ExpectedContentFrame* frames, 488 size_t size, 489 jboolean check_lines) { 490 int i; 491 event_storage_lock(storage); 492 fprintf(stderr, "Checking storage count %d\n", storage->live_object_count); 493 for (i = 0; i < storage->live_object_count; i++) { 494 ObjectTrace* trace = storage->live_objects[i]; 495 496 if (check_sample_content(env, trace, frames, size, check_lines, PRINT_OUT)) { 497 event_storage_unlock(storage); 498 return TRUE; 499 } 500 } 501 event_storage_unlock(storage); 502 return FALSE; 503 } 504 505 static jboolean event_storage_garbage_contains(JNIEnv* env, 506 EventStorage* storage, 507 ExpectedContentFrame* frames, 508 size_t size, 509 jboolean check_lines) { 510 int i; 511 event_storage_lock(storage); 512 fprintf(stderr, "Checking garbage storage count %d\n", 513 storage->garbage_history_size); 514 for (i = 0; i < storage->garbage_history_size; i++) { 515 ObjectTrace* trace = storage->garbage_collected_objects[i]; 516 517 if (trace == NULL) { 518 continue; 519 } 520 521 if (check_sample_content(env, trace, frames, size, check_lines, PRINT_OUT)) { 522 event_storage_unlock(storage); 523 return TRUE; 524 } 525 } 526 event_storage_unlock(storage); 527 return FALSE; 528 } 529 530 // No mutex here, handled by the caller. 531 static void event_storage_augment_storage(EventStorage* storage) { 532 int new_max = (storage->live_object_size * 2) + 1; 533 ObjectTrace** new_objects = malloc(new_max * sizeof(*new_objects)); 534 535 int current_count = storage->live_object_count; 536 memcpy(new_objects, storage->live_objects, current_count * sizeof(*new_objects)); 537 free(storage->live_objects); 538 storage->live_objects = new_objects; 539 storage->live_object_size = new_max; 540 } 541 542 static void event_storage_add(EventStorage* storage, 543 JNIEnv* jni, 544 jthread thread, 545 jobject object, 546 jclass klass, 547 jlong size) { 548 jvmtiFrameInfo frames[64]; 549 jint count; 550 jvmtiError err; 551 552 err = (*jvmti)->GetStackTrace(jvmti, thread, 0, 64, frames, &count); 553 if (err == JVMTI_ERROR_NONE && count >= 1) { 554 ObjectTrace* live_object; 555 jvmtiFrameInfo* allocated_frames = (jvmtiFrameInfo*) malloc(count * sizeof(*allocated_frames)); 556 memcpy(allocated_frames, frames, count * sizeof(*allocated_frames)); 557 558 live_object = (ObjectTrace*) malloc(sizeof(*live_object)); 559 live_object->frames = allocated_frames; 560 live_object->frame_count = count; 561 live_object->size = size; 562 live_object->thread = thread; 563 live_object->object = (*jni)->NewWeakGlobalRef(jni, object); 564 565 if (JNI_ENV_PTR(jni)->ExceptionOccurred(JNI_ENV_ARG(jni))) { 566 JNI_ENV_PTR(jni)->FatalError( 567 JNI_ENV_ARG2(jni, "Error in event_storage_add: Exception in jni NewWeakGlobalRef")); 568 } 569 570 // Only now lock and get things done quickly. 571 event_storage_lock(storage); 572 573 storage->live_object_additions++; 574 575 if (storage->live_object_count >= storage->live_object_size) { 576 event_storage_augment_storage(storage); 577 } 578 assert(storage->live_object_count < storage->live_object_size); 579 580 if (PRINT_OUT) { 581 fprintf(stderr, "Adding trace for thread %p, frame_count %d, storage %p\n", 582 thread, count, storage); 583 print_out_frames(jni, live_object); 584 } 585 storage->live_objects[storage->live_object_count] = live_object; 586 storage->live_object_count++; 587 588 event_storage_unlock(storage); 589 } 590 } 591 592 static void event_storage_compact(EventStorage* storage, JNIEnv* jni) { 593 int max, i, dest; 594 ObjectTrace** live_objects; 595 596 event_storage_lock_compaction(storage); 597 storage->compaction_required = 0; 598 event_storage_unlock_compaction(storage); 599 600 event_storage_lock(storage); 601 602 max = storage->live_object_count; 603 live_objects = storage->live_objects; 604 605 for (i = 0, dest = 0; i < max; i++) { 606 ObjectTrace* live_object = live_objects[i]; 607 jweak object = live_object->object; 608 609 if (!(*jni)->IsSameObject(jni, object, NULL)) { 610 if (dest != i) { 611 live_objects[dest] = live_object; 612 dest++; 613 } 614 } else { 615 (*jni)->DeleteWeakGlobalRef(jni, object); 616 live_object->object = NULL; 617 618 event_storage_add_garbage_collected_object(storage, live_object); 619 } 620 } 621 622 storage->live_object_count = dest; 623 event_storage_unlock(storage); 624 } 625 626 static void event_storage_free_objects(ObjectTrace** array, int max) { 627 int i; 628 for (i = 0; i < max; i++) { 629 free(array[i]), array[i] = NULL; 630 } 631 } 632 633 static void event_storage_reset(EventStorage* storage) { 634 event_storage_lock(storage); 635 636 // Reset everything except the mutex and the garbage collection. 637 event_storage_free_objects(storage->live_objects, 638 storage->live_object_count); 639 storage->live_object_additions = 0; 640 storage->live_object_size = 0; 641 storage->live_object_count = 0; 642 free(storage->live_objects), storage->live_objects = NULL; 643 644 event_storage_free_objects(storage->garbage_collected_objects, 645 storage->garbage_history_size); 646 647 storage->compaction_required = 0; 648 storage->garbage_history_index = 0; 649 650 event_storage_unlock(storage); 651 } 652 653 static int event_storage_number_additions(EventStorage* storage) { 654 int result; 655 event_storage_lock(storage); 656 result = storage->live_object_additions; 657 event_storage_unlock(storage); 658 return result; 659 } 660 661 // Start of the JVMTI agent code. 662 static const char *EXC_CNAME = "java/lang/Exception"; 663 664 static int check_error(jvmtiError err, const char *s) { 665 if (err != JVMTI_ERROR_NONE) { 666 printf(" ## %s error: %d\n", s, err); 667 return 1; 668 } 669 return 0; 670 } 671 672 static int check_capability_error(jvmtiError err, const char *s) { 673 if (err != JVMTI_ERROR_NONE) { 674 if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY) { 675 return 0; 676 } 677 fprintf(stderr, " ## %s error: %d\n", s, err); 678 } 679 return 1; 680 } 681 682 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); 683 684 JNIEXPORT 685 jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { 686 return Agent_Initialize(jvm, options, reserved); 687 } 688 689 JNIEXPORT 690 jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { 691 return Agent_Initialize(jvm, options, reserved); 692 } 693 694 JNIEXPORT 695 jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { 696 return JNI_VERSION_1_8; 697 } 698 699 #define MAX_THREADS 500 700 701 typedef struct ThreadStats { 702 int number_threads; 703 int counts[MAX_THREADS]; 704 int not_helper_counts[MAX_THREADS]; 705 int index[MAX_THREADS]; 706 jthread threads[MAX_THREADS]; 707 708 int method_resolution_problem; 709 } ThreadStats; 710 711 static ThreadStats thread_stats; 712 713 static void add_thread_count(jthread thread, int lock, int helper) { 714 int i; 715 jvmtiThreadInfo info; 716 const char* name; 717 char* end; 718 int idx; 719 int err; 720 721 if (lock) { 722 event_storage_lock(&global_event_storage); 723 } 724 725 for (i = 0; i < thread_stats.number_threads; i++) { 726 if (thread_stats.threads[i] == thread) { 727 if (helper) { 728 thread_stats.counts[i]++; 729 } else { 730 thread_stats.not_helper_counts[i]++; 731 } 732 733 if (lock) { 734 event_storage_unlock(&global_event_storage); 735 } 736 return; 737 } 738 } 739 740 thread_stats.threads[thread_stats.number_threads] = thread; 741 742 err = (*jvmti)->GetThreadInfo(jvmti, thread, &info); 743 if (err != JVMTI_ERROR_NONE) { 744 if (lock) { 745 event_storage_unlock(&global_event_storage); 746 } 747 748 // Just to have it accounted as an error... 749 info.name = "Allocator99"; 750 } 751 752 if (!strstr(info.name, "Allocator")) { 753 if (lock) { 754 event_storage_unlock(&global_event_storage); 755 } 756 757 // Just to have it accounted as an error... 758 info.name = "Allocator98"; 759 } 760 761 name = info.name + 9; 762 end = NULL; 763 idx = strtol(name, &end, 0); 764 765 if (*end == '\0') { 766 if (helper) { 767 thread_stats.counts[thread_stats.number_threads]++; 768 } else { 769 thread_stats.not_helper_counts[thread_stats.number_threads]++; 770 } 771 772 thread_stats.index[thread_stats.number_threads] = idx; 773 thread_stats.number_threads++; 774 } else { 775 fprintf(stderr, "Problem with thread name...: %p %s\n", thread, name); 776 } 777 778 if (PRINT_OUT) { 779 fprintf(stderr, "Added %s - %p - %d - lock: %d\n", info.name, thread, idx, lock); 780 } 781 782 if (lock) { 783 event_storage_unlock(&global_event_storage); 784 } 785 } 786 787 static void print_thread_stats() { 788 int i; 789 event_storage_lock(&global_event_storage); 790 fprintf(stderr, "Method resolution problem: %d\n", thread_stats.method_resolution_problem); 791 fprintf(stderr, "Thread count:\n"); 792 for (i = 0; i < thread_stats.number_threads; i++) { 793 fprintf(stderr, "\t%p: %d: %d - %d\n", thread_stats.threads[i], 794 thread_stats.index[i], 795 thread_stats.counts[i], 796 thread_stats.not_helper_counts[i]); 797 } 798 event_storage_unlock(&global_event_storage); 799 } 800 801 JNIEXPORT 802 void JNICALL SampledObjectAlloc(jvmtiEnv *jvmti_env, 803 JNIEnv* jni_env, 804 jthread thread, 805 jobject object, 806 jclass object_klass, 807 jlong size) { 808 add_thread_count(thread, 1, 1); 809 810 if (event_storage_get_compaction_required(&global_event_storage)) { 811 event_storage_compact(&global_event_storage, jni_env); 812 } 813 814 event_storage_add(&global_event_storage, jni_env, thread, object, 815 object_klass, size); 816 } 817 818 JNIEXPORT 819 void JNICALL VMObjectAlloc(jvmtiEnv *jvmti_env, 820 JNIEnv* jni_env, 821 jthread thread, 822 jobject object, 823 jclass object_klass, 824 jlong size) { 825 event_storage_add(&second_global_event_storage, jni_env, thread, object, 826 object_klass, size); 827 } 828 829 JNIEXPORT 830 void JNICALL GarbageCollectionFinish(jvmtiEnv *jvmti_env) { 831 event_storage_set_compaction_required(&global_event_storage); 832 } 833 834 static int enable_notifications() { 835 if (check_error((*jvmti)->SetEventNotificationMode( 836 jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL), 837 "Set event notifications")) { 838 return 1; 839 } 840 841 return check_error((*jvmti)->SetEventNotificationMode( 842 jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL), 843 "Set event notifications"); 844 } 845 846 static int enable_notifications_for_two_threads(jthread first, jthread second) { 847 if (check_error((*jvmti)->SetEventNotificationMode( 848 jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL), 849 "Set event notifications")) { 850 return 0; 851 } 852 853 if (check_error((*jvmti)->SetEventNotificationMode( 854 jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, first), 855 "Set event notifications")) { 856 return 0; 857 } 858 859 // Second thread should fail. 860 if (check_error((*jvmti)->SetEventNotificationMode( 861 jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, second), 862 "Set event notifications")) { 863 return 0; 864 } 865 866 return 1; 867 } 868 869 static 870 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { 871 jint res; 872 jvmtiEventCallbacks callbacks; 873 jvmtiCapabilities caps; 874 875 res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG3(jvm, (void **) &jvmti, 876 JVMTI_VERSION_9)); 877 if (res != JNI_OK || jvmti == NULL) { 878 fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n"); 879 return JNI_ERR; 880 } 881 882 // Get second jvmti environment. 883 res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG3(jvm, (void **) &second_jvmti, 884 JVMTI_VERSION_9)); 885 if (res != JNI_OK || second_jvmti == NULL) { 886 fprintf(stderr, "Error: wrong result of a valid second call to GetEnv!\n"); 887 return JNI_ERR; 888 } 889 890 if (PRINT_OUT) { 891 fprintf(stderr, "Storage is at %p, secondary is at %p\n", 892 &global_event_storage, &second_global_event_storage); 893 } 894 895 (*jvmti)->CreateRawMonitor(jvmti, "storage_monitor", 896 &global_event_storage.storage_monitor); 897 (*jvmti)->CreateRawMonitor(jvmti, "second_storage_monitor", 898 &second_global_event_storage.storage_monitor); 899 900 (*jvmti)->CreateRawMonitor(jvmti, "compaction_monitor", 901 &global_event_storage.compaction_monitor); 902 (*jvmti)->CreateRawMonitor(jvmti, "second_compaction_monitor", 903 &second_global_event_storage.compaction_monitor); 904 905 event_storage_set_garbage_history(&global_event_storage, 200); 906 event_storage_set_garbage_history(&second_global_event_storage, 200); 907 908 memset(&callbacks, 0, sizeof(callbacks)); 909 callbacks.SampledObjectAlloc = &SampledObjectAlloc; 910 callbacks.VMObjectAlloc = &VMObjectAlloc; 911 callbacks.GarbageCollectionFinish = &GarbageCollectionFinish; 912 913 memset(&caps, 0, sizeof(caps)); 914 // Get line numbers, sample events, filename, and gc events for the tests. 915 caps.can_get_line_numbers = 1; 916 caps.can_get_source_file_name = 1; 917 caps.can_generate_garbage_collection_events = 1; 918 caps.can_generate_sampled_object_alloc_events = 1; 919 caps.can_generate_vm_object_alloc_events = 1; 920 if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), "Add capabilities")) { 921 return JNI_ERR; 922 } 923 924 if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks, 925 sizeof(jvmtiEventCallbacks)), 926 " Set Event Callbacks")) { 927 return JNI_ERR; 928 } 929 return JNI_OK; 930 } 931 932 JNIEXPORT void JNICALL 933 Java_MyPackage_HeapMonitor_setSamplingInterval(JNIEnv* env, jclass cls, jint value) { 934 (*jvmti)->SetHeapSamplingInterval(jvmti, value); 935 } 936 937 JNIEXPORT jboolean JNICALL 938 Java_MyPackage_HeapMonitor_eventStorageIsEmpty(JNIEnv* env, jclass cls) { 939 return event_storage_get_count(&global_event_storage) == 0; 940 } 941 942 JNIEXPORT jint JNICALL 943 Java_MyPackage_HeapMonitor_getEventStorageElementCount(JNIEnv* env, jclass cls) { 944 return event_storage_get_count(&global_event_storage); 945 } 946 947 JNIEXPORT void JNICALL 948 Java_MyPackage_HeapMonitor_enableSamplingEvents(JNIEnv* env, jclass cls) { 949 enable_notifications(); 950 } 951 952 JNIEXPORT jboolean JNICALL 953 Java_MyPackage_HeapMonitor_enableSamplingEventsForTwoThreads(JNIEnv* env, 954 jclass cls, 955 jthread first, 956 jthread second) { 957 return enable_notifications_for_two_threads(first, second); 958 } 959 960 JNIEXPORT void JNICALL 961 Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) { 962 check_error((*jvmti)->SetEventNotificationMode( 963 jvmti, JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL), 964 "Set event notifications"); 965 966 check_error((*jvmti)->SetEventNotificationMode( 967 jvmti, JVMTI_DISABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL), 968 "Garbage Collection Finish"); 969 } 970 971 JNIEXPORT jboolean JNICALL 972 Java_MyPackage_HeapMonitor_obtainedEvents(JNIEnv* env, jclass cls, 973 jobjectArray frames, 974 jboolean check_lines) { 975 jboolean result; 976 jsize size = JNI_ENV_PTR(env)->GetArrayLength(JNI_ENV_ARG2(env, frames)); 977 ExpectedContentFrame *native_frames; 978 979 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 980 JNI_ENV_PTR(env)->FatalError( 981 JNI_ENV_ARG2(env, "obtainedEvents failed with the GetArrayLength call")); 982 } 983 984 native_frames = malloc(size * sizeof(*native_frames)); 985 986 if (native_frames == NULL) { 987 JNI_ENV_PTR(env)->FatalError( 988 JNI_ENV_ARG2(env, "Error in obtainedEvents: malloc returned NULL for native_frames allocation\n")); 989 } 990 991 if (fill_native_frames(env, frames, native_frames, size) != 0) { 992 JNI_ENV_PTR(env)->FatalError( 993 JNI_ENV_ARG2(env, "Error in obtainedEvents: fill_native_frames returned failed status\n")); 994 } 995 result = event_storage_contains(env, &global_event_storage, native_frames, 996 size, check_lines); 997 998 free(native_frames), native_frames = NULL; 999 return result; 1000 } 1001 1002 JNIEXPORT jboolean JNICALL 1003 Java_MyPackage_HeapMonitor_garbageContains(JNIEnv* env, jclass cls, 1004 jobjectArray frames, 1005 jboolean check_lines) { 1006 jboolean result; 1007 jsize size = JNI_ENV_PTR(env)->GetArrayLength(JNI_ENV_ARG2(env, frames)); 1008 ExpectedContentFrame *native_frames; 1009 1010 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) { 1011 JNI_ENV_PTR(env)->FatalError( 1012 JNI_ENV_ARG2(env, "garbageContains failed with the GetArrayLength call")); 1013 } 1014 1015 native_frames = malloc(size * sizeof(*native_frames)); 1016 1017 if (native_frames == NULL) { 1018 JNI_ENV_PTR(env)->FatalError( 1019 JNI_ENV_ARG2(env, "Error in garbageContains: malloc returned NULL for native_frames allocation\n")); 1020 } 1021 1022 if (fill_native_frames(env, frames, native_frames, size) != 0) { 1023 JNI_ENV_PTR(env)->FatalError( 1024 JNI_ENV_ARG2(env, "Error in garbageContains: fill_native_frames returned failed status\n")); 1025 } 1026 result = event_storage_garbage_contains(env, &global_event_storage, 1027 native_frames, size, check_lines); 1028 1029 free(native_frames), native_frames = NULL; 1030 return result; 1031 } 1032 1033 JNIEXPORT void JNICALL 1034 Java_MyPackage_HeapMonitor_forceGarbageCollection(JNIEnv* env, jclass cls) { 1035 check_error((*jvmti)->ForceGarbageCollection(jvmti), 1036 "Forced Garbage Collection"); 1037 } 1038 1039 JNIEXPORT void JNICALL 1040 Java_MyPackage_HeapMonitor_resetEventStorage(JNIEnv* env, jclass cls) { 1041 event_storage_reset(&global_event_storage); 1042 event_storage_reset(&second_global_event_storage); 1043 } 1044 1045 JNIEXPORT jboolean JNICALL 1046 Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env, 1047 jclass cls) { 1048 jvmtiCapabilities caps; 1049 memset(&caps, 0, sizeof(caps)); 1050 caps.can_generate_sampled_object_alloc_events = 1; 1051 if (check_error((*jvmti)->RelinquishCapabilities(jvmti, &caps), 1052 "Add capabilities\n")){ 1053 return FALSE; 1054 } 1055 1056 if (check_capability_error((*jvmti)->SetHeapSamplingInterval(jvmti, 1<<19), 1057 "Set Heap Sampling Interval")) { 1058 return FALSE; 1059 } 1060 return TRUE; 1061 } 1062 1063 JNIEXPORT jboolean JNICALL 1064 Java_MyPackage_HeapMonitorIllegalArgumentTest_testIllegalArgument(JNIEnv *env, 1065 jclass cls) { 1066 if (check_error((*jvmti)->SetHeapSamplingInterval(jvmti, 0), 1067 "Sampling interval 0 failed\n")){ 1068 return FALSE; 1069 } 1070 1071 if (check_error((*jvmti)->SetHeapSamplingInterval(jvmti, 1024), 1072 "Sampling interval 1024 failed\n")){ 1073 return FALSE; 1074 } 1075 1076 if (!check_error((*jvmti)->SetHeapSamplingInterval(jvmti, -1), 1077 "Sampling interval -1 passed\n")){ 1078 return FALSE; 1079 } 1080 1081 if (!check_error((*jvmti)->SetHeapSamplingInterval(jvmti, -1024), 1082 "Sampling interval -1024 passed\n")){ 1083 return FALSE; 1084 } 1085 1086 return TRUE; 1087 } 1088 1089 JNIEXPORT jdouble JNICALL 1090 Java_MyPackage_HeapMonitor_getAverageSize(JNIEnv *env, jclass cls) { 1091 return event_storage_get_average_size(&global_event_storage); 1092 } 1093 1094 typedef struct sThreadsFound { 1095 jthread* threads; 1096 int num_threads; 1097 } ThreadsFound; 1098 1099 JNIEXPORT jboolean JNICALL 1100 Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls, 1101 jint num_threads) { 1102 1103 print_thread_stats(); 1104 // Ensure we got stacks from at least num_threads. 1105 return thread_stats.number_threads >= num_threads; 1106 } 1107 1108 JNIEXPORT 1109 void JNICALL SampledObjectAlloc2(jvmtiEnv *jvmti_env, 1110 JNIEnv* jni_env, 1111 jthread thread, 1112 jobject object, 1113 jclass object_klass, 1114 jlong size) { 1115 // Nop for now, two agents are not yet implemented. 1116 assert(0); 1117 } 1118 1119 JNIEXPORT jboolean JNICALL 1120 Java_MyPackage_HeapMonitorTwoAgentsTest_enablingSamplingInSecondaryAgent( 1121 JNIEnv* env, jclass cls) { 1122 // Currently this method should be failing directly at the AddCapability step 1123 // but the implementation is correct for when multi-agent support is enabled. 1124 jvmtiCapabilities caps; 1125 jvmtiEventCallbacks callbacks; 1126 1127 memset(&caps, 0, sizeof(caps)); 1128 caps.can_generate_sampled_object_alloc_events = 1; 1129 if (check_error((*second_jvmti)->AddCapabilities(second_jvmti, &caps), 1130 "Set the capability for second agent")) { 1131 return FALSE; 1132 } 1133 1134 memset(&callbacks, 0, sizeof(callbacks)); 1135 callbacks.SampledObjectAlloc = &SampledObjectAlloc2; 1136 1137 if (check_error((*second_jvmti)->SetEventCallbacks(second_jvmti, &callbacks, 1138 sizeof(jvmtiEventCallbacks)), 1139 " Set Event Callbacks for second agent")) { 1140 return FALSE; 1141 } 1142 1143 return TRUE; 1144 } 1145 1146 JNIEXPORT void JNICALL 1147 Java_MyPackage_HeapMonitor_enableVMEvents(JNIEnv* env, jclass cls) { 1148 check_error((*jvmti)->SetEventNotificationMode( 1149 jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, NULL), 1150 "Set vm event notifications"); 1151 } 1152 1153 JNIEXPORT jint JNICALL 1154 Java_MyPackage_HeapMonitorVMEventsTest_vmEvents(JNIEnv* env, jclass cls) { 1155 return event_storage_number_additions(&second_global_event_storage); 1156 } 1157 1158 JNIEXPORT jint JNICALL 1159 Java_MyPackage_HeapMonitor_sampledEvents(JNIEnv* env, jclass cls) { 1160 return event_storage_number_additions(&global_event_storage); 1161 } 1162 1163 static jobject allocate_object(JNIEnv* env) { 1164 // Construct an Object. 1165 jclass cls = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG2(env, "java/lang/Object")); 1166 jmethodID constructor; 1167 jobject result; 1168 1169 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env)) || cls == NULL) { 1170 JNI_ENV_PTR(env)->FatalError( 1171 JNI_ENV_ARG2(env, "Error in jni FindClass: Cannot find Object class\n")); 1172 } 1173 1174 constructor = JNI_ENV_PTR(env)->GetMethodID(JNI_ENV_ARG4(env, cls, "<init>", "()V")); 1175 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env)) || constructor == NULL) { 1176 JNI_ENV_PTR(env)->FatalError( 1177 JNI_ENV_ARG2(env, "Error in jni GetMethodID: Cannot find Object class constructor\n")); 1178 } 1179 1180 // Call back constructor to allocate a new instance, with an int argument 1181 result = JNI_ENV_PTR(env)->NewObject(JNI_ENV_ARG3(env, cls, constructor)); 1182 1183 if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env)) || result == NULL) { 1184 JNI_ENV_PTR(env)->FatalError( 1185 JNI_ENV_ARG2(env, "Error in jni NewObject: Cannot allocate an object\n")); 1186 } 1187 return result; 1188 } 1189 1190 // Ensure we got a callback for the test. 1191 static int did_recursive_callback_test; 1192 1193 JNIEXPORT 1194 void JNICALL RecursiveSampledObjectAlloc(jvmtiEnv *jvmti_env, 1195 JNIEnv* jni_env, 1196 jthread thread, 1197 jobject object, 1198 jclass object_klass, 1199 jlong size) { 1200 // Basically ensure that if we were to allocate objects, we would not have an 1201 // infinite recursion here. 1202 int i; 1203 for (i = 0; i < 1000; i++) { 1204 if (allocate_object(jni_env) == NULL) { 1205 JNI_ENV_PTR(jni_env)->FatalError(JNI_ENV_ARG2(jni_env, "allocate_object returned NULL\n")); 1206 } 1207 } 1208 1209 did_recursive_callback_test = 1; 1210 } 1211 1212 JNIEXPORT jboolean JNICALL 1213 Java_MyPackage_HeapMonitorRecursiveTest_didCallback(JNIEnv* env, jclass cls) { 1214 return did_recursive_callback_test != 0; 1215 } 1216 1217 JNIEXPORT void JNICALL 1218 Java_MyPackage_HeapMonitorRecursiveTest_setCallbackToCallAllocateSomeMore(JNIEnv* env, jclass cls) { 1219 jvmtiEventCallbacks callbacks; 1220 1221 memset(&callbacks, 0, sizeof(callbacks)); 1222 callbacks.SampledObjectAlloc = &RecursiveSampledObjectAlloc; 1223 1224 if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks, 1225 sizeof(jvmtiEventCallbacks)), 1226 " Set Event Callbacks")) { 1227 JNI_ENV_PTR(env)->FatalError(JNI_ENV_ARG2(env, "Cannot reset the callback.")); 1228 } 1229 } 1230 1231 #ifdef __cplusplus 1232 } 1233 #endif