1 /* 2 * Copyright (c) 2017, 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 <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include "jvmti.h" 28 29 #ifdef __cplusplus 30 extern "C" { 31 #endif 32 33 #ifndef JNI_ENV_ARG 34 35 #ifdef __cplusplus 36 #define JNI_ENV_ARG(x, y) y 37 #define JNI_ENV_PTR(x) x 38 #else 39 #define JNI_ENV_ARG(x,y) x, y 40 #define JNI_ENV_PTR(x) (*x) 41 #endif 42 43 #endif 44 45 #define TRUE 1 46 #define FALSE 0 47 #define PRINT_OUT 1 48 #define MAX_TRACES 400 49 50 static const char *EXC_CNAME = "java/lang/Exception"; 51 static jvmtiEnv *jvmti = NULL; 52 53 static int check_error(jvmtiError err, const char *s) { 54 if (err != JVMTI_ERROR_NONE) { 55 printf(" ## %s error: %d\n", s, err); 56 return 1; 57 } 58 return 0; 59 } 60 61 static int check_capability_error(jvmtiError err, const char *s) { 62 if (err != JVMTI_ERROR_NONE) { 63 if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY) { 64 return 0; 65 } 66 printf(" ## %s error: %d\n", s, err); 67 return 1; 68 } 69 return 1; 70 } 71 72 static 73 jint throw_exc(JNIEnv *env, char *msg) { 74 jclass exc_class = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, EXC_CNAME)); 75 76 if (exc_class == NULL) { 77 printf("throw_exc: Error in FindClass(env, %s)\n", EXC_CNAME); 78 return -1; 79 } 80 return JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg); 81 } 82 83 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); 84 85 JNIEXPORT 86 jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { 87 return Agent_Initialize(jvm, options, reserved); 88 } 89 90 JNIEXPORT 91 jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { 92 return Agent_Initialize(jvm, options, reserved); 93 } 94 95 JNIEXPORT 96 jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { 97 return JNI_VERSION_1_8; 98 } 99 100 static 101 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { 102 jint res; 103 104 res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), 105 JVMTI_VERSION_9); 106 if (res != JNI_OK || jvmti == NULL) { 107 printf(" Error: wrong result of a valid call to GetEnv!\n"); 108 return JNI_ERR; 109 } 110 111 jvmtiEventCallbacks callbacks; 112 memset(&callbacks, 0, sizeof(callbacks)); 113 114 jvmtiCapabilities caps; 115 memset(&caps, 0, sizeof(caps)); 116 // Get line numbers, sample heap, and filename for the test. 117 caps.can_get_line_numbers = 1; 118 caps.can_sample_heap = 1; 119 caps.can_get_source_file_name = 1; 120 if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), 121 "Add capabilities\n")){ 122 return JNI_ERR; 123 } 124 if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks, 125 sizeof(jvmtiEventCallbacks)), 126 " Set Event Callbacks")) { 127 return JNI_ERR; 128 } 129 return JNI_OK; 130 } 131 132 // Given a method and a location, this method gets the line number. 133 static 134 jint get_line_number(jvmtiEnv *jvmti, jmethodID method, 135 jlocation location) { 136 // Read the line number table. 137 jvmtiLineNumberEntry *table_ptr = 0; 138 jint line_number_table_entries; 139 int jvmti_error = (*jvmti)->GetLineNumberTable(jvmti, method, 140 &line_number_table_entries, 141 &table_ptr); 142 143 if (JVMTI_ERROR_NONE != jvmti_error) { 144 return -1; 145 } 146 if (line_number_table_entries <= 0) { 147 return -1; 148 } 149 if (line_number_table_entries == 1) { 150 return table_ptr[0].line_number; 151 } 152 153 // Go through all the line numbers... 154 jint last_location = table_ptr[0].start_location; 155 int l; 156 for (l = 1; l < line_number_table_entries; l++) { 157 // ... and if you see one that is in the right place for your 158 // location, you've found the line number! 159 if ((location < table_ptr[l].start_location) && 160 (location >= last_location)) { 161 return table_ptr[l - 1].line_number; 162 } 163 last_location = table_ptr[l].start_location; 164 } 165 166 if (location >= last_location) { 167 return table_ptr[line_number_table_entries - 1].line_number; 168 } else { 169 return -1; 170 } 171 } 172 173 typedef struct _ExpectedContentFrame { 174 const char *name; 175 const char *signature; 176 const char *file_name; 177 int line_number; 178 } ExpectedContentFrame; 179 180 static jboolean check_sample_content(JNIEnv *env, 181 jvmtiAllocTraceInfo* trace, 182 ExpectedContentFrame *expected, 183 int expected_count, 184 int print_out_comparisons) { 185 int i; 186 187 jvmtiStackInfo* stack_info = trace->stack_info; 188 189 if (expected_count > stack_info->frame_count) { 190 return FALSE; 191 } 192 193 jvmtiFrameInfo* frames = stack_info->frame_buffer; 194 195 for (i = 0; i < expected_count; i++) { 196 // Get basic information out of the trace. 197 int bci = frames[i].location; 198 jmethodID methodid = frames[i].method; 199 char *name = NULL, *signature = NULL, *file_name = NULL; 200 201 if (bci < 0) { 202 return FALSE; 203 } 204 205 // Transform into usable information. 206 int line_number = get_line_number(jvmti, methodid, bci); 207 (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0); 208 209 jclass declaring_class; 210 if (JVMTI_ERROR_NONE != 211 (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) { 212 return FALSE; 213 } 214 215 jvmtiError err = (*jvmti)->GetSourceFileName(jvmti, declaring_class, 216 &file_name); 217 if (err != JVMTI_ERROR_NONE) { 218 return FALSE; 219 } 220 221 // Compare now, none should be NULL. 222 if (name == NULL) { 223 return FALSE; 224 } 225 226 if (file_name == NULL) { 227 return FALSE; 228 } 229 230 if (signature == NULL) { 231 return FALSE; 232 } 233 234 if (print_out_comparisons) { 235 fprintf(stderr, "\tComparing:\n"); 236 fprintf(stderr, "\t\tNames: %s and %s\n", name, expected[i].name); 237 fprintf(stderr, "\t\tSignatures: %s and %s\n", signature, expected[i].signature); 238 fprintf(stderr, "\t\tFile name: %s and %s\n", file_name, expected[i].file_name); 239 fprintf(stderr, "\t\tLines: %d and %d\n", line_number, expected[i].line_number); 240 fprintf(stderr, "\t\tResult is %d\n", 241 (strcmp(name, expected[i].name) || 242 strcmp(signature, expected[i].signature) || 243 strcmp(file_name, expected[i].file_name) || 244 line_number != expected[i].line_number)); 245 } 246 247 if (strcmp(name, expected[i].name) || 248 strcmp(signature, expected[i].signature) || 249 strcmp(file_name, expected[i].file_name) || 250 line_number != expected[i].line_number) { 251 return FALSE; 252 } 253 } 254 255 return TRUE; 256 } 257 258 static jboolean compare_samples(JNIEnv* env, jvmtiAllocTraceInfo* traces, 259 int trace_count, 260 ExpectedContentFrame* expected_content, 261 size_t size, 262 int print_out_comparisons) { 263 // We expect the code to record correctly the bci, retrieve the line 264 // number, have the right method and the class name of the first frames. 265 int i; 266 if (print_out_comparisons) { 267 fprintf(stderr, "\tNumber of traces: %d\n", print_out_comparisons); 268 } 269 270 for (i = 0; i < trace_count; i++) { 271 jvmtiAllocTraceInfo* trace = traces + i; 272 if (check_sample_content(env, trace, expected_content, size, 273 print_out_comparisons)) { 274 // At least one frame matched what we were looking for. 275 return TRUE; 276 } 277 } 278 279 return FALSE; 280 } 281 282 static jboolean 283 check_samples(JNIEnv* env, 284 ExpectedContentFrame* expected, 285 size_t size, 286 jvmtiError (*const get_traces)(jvmtiEnv*, jvmtiAllocTraceInfo**, jint*), 287 int print_out_comparisons) { 288 jvmtiAllocTraceInfo *traces; 289 jint trace_counter; 290 jvmtiError error = get_traces(jvmti, &traces, &trace_counter); 291 292 if (error != JVMTI_ERROR_NONE) { 293 return FALSE; 294 } 295 296 int result = compare_samples(env, traces, trace_counter, 297 expected, size, print_out_comparisons); 298 299 if ((*jvmti)->Deallocate(jvmti, (unsigned char*) traces) != JVMTI_ERROR_NONE) { 300 return FALSE; 301 } 302 303 return result; 304 } 305 306 static jboolean frames_exist_live(JNIEnv* env, 307 ExpectedContentFrame* expected, 308 size_t size, 309 int print_out_comparisons) { 310 return check_samples(env, expected, size, (*jvmti)->GetObjectAllocTraces, 311 print_out_comparisons); 312 } 313 314 static jboolean frames_exist_recent(JNIEnv* env, 315 ExpectedContentFrame* expected, 316 size_t size, 317 int print_out_comparisons) { 318 return check_samples(env, expected, size, (*jvmti)->GetGarbageTraces, 319 print_out_comparisons); 320 } 321 322 static jboolean frames_exist_frequent(JNIEnv* env, 323 ExpectedContentFrame* expected, 324 size_t size, 325 int print_out_comparisons) { 326 return check_samples(env, expected, size, (*jvmti)->GetFrequentGarbageTraces, 327 print_out_comparisons); 328 } 329 330 // Static native API for various tests. 331 static void fill_native_frames(JNIEnv* env, jobjectArray frames, 332 ExpectedContentFrame* native_frames, size_t size) { 333 size_t i; 334 for(i = 0; i < size; i++) { 335 jobject obj = (*env)->GetObjectArrayElement(env, frames, i); 336 jclass frame_class = (*env)->GetObjectClass(env, obj); 337 jfieldID line_number_field_id = (*env)->GetFieldID(env, frame_class, 338 "lineNumber", "I"); 339 int line_number = (*env)->GetIntField(env, obj, line_number_field_id); 340 341 jfieldID string_id = (*env)->GetFieldID(env, frame_class, "method", 342 "Ljava/lang/String;"); 343 jstring string_object = (jstring) (*env)->GetObjectField(env, obj, 344 string_id); 345 const char* method = (*env)->GetStringUTFChars(env, string_object, 0); 346 347 string_id = (*env)->GetFieldID(env, frame_class, "fileName", 348 "Ljava/lang/String;"); 349 string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); 350 const char* file_name = (*env)->GetStringUTFChars(env, string_object, 0); 351 352 string_id = (*env)->GetFieldID(env, frame_class, "signature", 353 "Ljava/lang/String;"); 354 string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); 355 const char* signature= (*env)->GetStringUTFChars(env, string_object, 0); 356 357 native_frames[i].name = method; 358 native_frames[i].file_name = file_name; 359 native_frames[i].signature = signature; 360 native_frames[i].line_number = line_number; 361 } 362 } 363 364 static jboolean check_and(JNIEnv *env, jobjectArray frames, int live, 365 int recent, int frequent, int print_out) { 366 jobject loader = NULL; 367 368 if (frames == NULL) { 369 return FALSE; 370 } 371 372 // Start by transforming the frames into a C-friendly structure. 373 jsize size = (*env)->GetArrayLength(env, frames); 374 ExpectedContentFrame native_frames[size]; 375 fill_native_frames(env, frames, native_frames, size); 376 377 if (jvmti == NULL) { 378 throw_exc(env, "JVMTI client was not properly loaded!\n"); 379 return FALSE; 380 } 381 382 int result = TRUE; 383 384 if (live) { 385 result = frames_exist_live(env, native_frames, size, print_out); 386 } 387 388 if (recent) { 389 result = result && 390 frames_exist_recent(env, native_frames, size, print_out); 391 } 392 393 if (frequent) { 394 result = result && 395 frames_exist_frequent(env, native_frames, size, print_out); 396 } 397 398 return result; 399 } 400 401 static jboolean check_or(JNIEnv *env, jobjectArray frames, int live, int recent, 402 int frequent, int print_out) { 403 jobject loader = NULL; 404 405 if (frames == NULL) { 406 return FALSE; 407 } 408 409 // Start by transforming the frames into a C-friendly structure. 410 jsize size = (*env)->GetArrayLength(env, frames); 411 ExpectedContentFrame native_frames[size]; 412 fill_native_frames(env, frames, native_frames, size); 413 414 if (jvmti == NULL) { 415 throw_exc(env, "JVMTI client was not properly loaded!\n"); 416 return FALSE; 417 } 418 419 jboolean result = FALSE; 420 421 if (live) { 422 result = frames_exist_live(env, native_frames, size, print_out); 423 } 424 425 if (recent) { 426 result = result || 427 frames_exist_recent(env, native_frames, size, print_out); 428 } 429 430 if (frequent) { 431 result = result || 432 frames_exist_frequent(env, native_frames, size, print_out); 433 } 434 435 return result; 436 } 437 438 static jboolean checkAll(JNIEnv *env, jobjectArray frames, int print_out) { 439 return check_and(env, frames, 1, 1, 1, print_out); 440 } 441 442 static jboolean checkNone(JNIEnv *env, jobjectArray frames, 443 int print_out) { 444 jobject loader = NULL; 445 446 if (frames == NULL) { 447 return FALSE; 448 } 449 450 // Start by transforming the frames into a C-friendly structure. 451 jsize size = (*env)->GetArrayLength(env, frames); 452 ExpectedContentFrame native_frames[size]; 453 fill_native_frames(env, frames, native_frames, size); 454 455 if (jvmti == NULL) { 456 throw_exc(env, "JVMTI client was not properly loaded!\n"); 457 return FALSE ; 458 } 459 460 if ((!frames_exist_live(env, native_frames, size, print_out)) && 461 (!frames_exist_recent(env, native_frames, size, print_out)) && 462 (!frames_exist_frequent(env, native_frames, size, print_out))) { 463 return TRUE; 464 } 465 return FALSE; 466 } 467 468 JNIEXPORT void JNICALL 469 Java_MyPackage_HeapMonitor_enableSampling(JNIEnv *env, jclass cls, int rate, 470 int max_traces) { 471 check_error((*jvmti)->StartHeapSampling(jvmti, rate, max_traces), 472 "Start Heap Sampling"); 473 } 474 475 JNIEXPORT void JNICALL 476 Java_MyPackage_HeapMonitor_disableSampling(JNIEnv *env, jclass cls) { 477 check_error((*jvmti)->StopHeapSampling(jvmti), "Stop Heap Sampling"); 478 } 479 480 JNIEXPORT jboolean JNICALL 481 Java_MyPackage_HeapMonitor_areSamplingStatisticsZero(JNIEnv *env, jclass cls) { 482 jvmtiHeapSamplingStats stats; 483 check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats), 484 "Heap Sampling Statistics"); 485 486 jvmtiHeapSamplingStats zero; 487 memset(&zero, 0, sizeof(zero)); 488 return memcmp(&stats, &zero, sizeof(zero)) == 0; 489 } 490 491 JNIEXPORT jboolean JNICALL 492 Java_MyPackage_HeapMonitor_framesExistEverywhere(JNIEnv *env, jclass cls, 493 jobjectArray frames) { 494 // We want the frames in each part. 495 return checkAll(env, frames, PRINT_OUT); 496 } 497 498 JNIEXPORT jboolean JNICALL 499 Java_MyPackage_HeapMonitor_framesExistNowhere(JNIEnv *env, jclass cls, 500 jobjectArray frames) { 501 // We want the frames in none of the parts. 502 return checkNone(env, frames, PRINT_OUT); 503 } 504 505 JNIEXPORT jboolean JNICALL 506 Java_MyPackage_HeapMonitor_framesExistSomewhere(JNIEnv *env, jclass cls, 507 jobjectArray frames) { 508 return check_or(env, frames, TRUE, TRUE, TRUE, PRINT_OUT); 509 } 510 511 JNIEXPORT jboolean JNICALL 512 Java_MyPackage_HeapMonitorRecentTest_framesNotInLiveOrRecent(JNIEnv *env, 513 jclass cls, 514 jobjectArray frames) { 515 return !check_or(env, frames, TRUE, TRUE, FALSE, PRINT_OUT); 516 } 517 518 JNIEXPORT jboolean JNICALL 519 Java_MyPackage_HeapMonitorRecentTest_framesExistInRecent(JNIEnv *env, 520 jclass cls, 521 jobjectArray frames) { 522 return check_and(env, frames, FALSE, TRUE, FALSE, PRINT_OUT); 523 } 524 525 JNIEXPORT jboolean JNICALL 526 Java_MyPackage_HeapMonitorFrequentTest_framesExistInFrequent(JNIEnv *env, 527 jclass cls, 528 jobjectArray frames) { 529 return check_and(env, frames, FALSE, FALSE, TRUE, PRINT_OUT); 530 } 531 532 JNIEXPORT jboolean JNICALL 533 Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env, 534 jclass cls) { 535 jvmtiCapabilities caps; 536 memset(&caps, 0, sizeof(caps)); 537 caps.can_sample_heap= 1; 538 if (check_error((*jvmti)->RelinquishCapabilities(jvmti, &caps), 539 "Add capabilities\n")){ 540 return FALSE; 541 } 542 543 if (check_capability_error((*jvmti)->StartHeapSampling(jvmti, 1<<19, 544 MAX_TRACES), 545 "Start Heap Sampling")) { 546 return FALSE; 547 } 548 549 if (check_capability_error((*jvmti)->StopHeapSampling(jvmti), 550 "Stop Heap Sampling")) { 551 return FALSE; 552 } 553 554 if (check_capability_error((*jvmti)->GetHeapSamplingStats(jvmti, NULL), 555 "Get Heap Sampling Stats")) { 556 return FALSE; 557 } 558 559 if (check_capability_error((*jvmti)->GetGarbageTraces(jvmti, NULL, NULL), 560 "Get Garbage Traces")) { 561 return FALSE; 562 } 563 564 if (check_capability_error((*jvmti)->GetFrequentGarbageTraces(jvmti, NULL, NULL), 565 "Get Frequent Garbage Traces")) { 566 return FALSE; 567 } 568 569 if (check_capability_error((*jvmti)->GetObjectAllocTraces(jvmti, NULL, NULL), 570 "Get Object Allocated Traces")) { 571 return FALSE; 572 } 573 574 if (check_capability_error((*jvmti)->GetObjectAllocTraces(jvmti, NULL, NULL), 575 "Get Cached Object Allocated Traces")) { 576 return FALSE; 577 } 578 return TRUE; 579 } 580 581 JNIEXPORT jboolean JNICALL 582 Java_MyPackage_HeapMonitor_statsHaveExpectedNumberSamples(JNIEnv *env, 583 jclass cls, 584 int expected, 585 int percent_error) { 586 jvmtiHeapSamplingStats stats; 587 check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats), 588 "Heap Sampling Statistics"); 589 590 double diff_ratio = (stats.sample_count - expected); 591 diff_ratio = (diff_ratio < 0) ? -diff_ratio : diff_ratio; 592 diff_ratio /= expected; 593 594 if (diff_ratio * 100 >= percent_error) { 595 fprintf(stderr, "Problem with sample count, obtained %ld and expected %d\n", 596 stats.sample_count, expected); 597 } 598 return diff_ratio * 100 < percent_error; 599 } 600 601 JNIEXPORT jdouble JNICALL 602 Java_MyPackage_HeapMonitorStatRateTest_getAverageRate(JNIEnv *env, jclass cls) { 603 jvmtiHeapSamplingStats stats; 604 check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats), 605 "Heap Sampling Statistics"); 606 return ((double) stats.sample_rate_accumulation) / stats.sample_rate_count; 607 } 608 609 static double calculate_average_stack_depth( 610 jvmtiError (*const get_traces)(jvmtiEnv*, jvmtiAllocTraceInfo**, jint*)) { 611 jvmtiAllocTraceInfo* traces = NULL; 612 jint trace_counter; 613 614 jvmtiError error = get_traces(jvmti, &traces, &trace_counter);; 615 616 if (error != JVMTI_ERROR_NONE) { 617 return 0; 618 } 619 620 if (trace_counter == 0) { 621 return 0; 622 } 623 624 int i; 625 double sum = 0; 626 for (i = 0; i < trace_counter; i++) { 627 jvmtiAllocTraceInfo* trace = traces + i; 628 jvmtiStackInfo* stack_info = trace->stack_info; 629 sum += stack_info->frame_count; 630 } 631 632 if ((*jvmti)->Deallocate(jvmti, (unsigned char*) traces) != JVMTI_ERROR_NONE) { 633 return 0; 634 } 635 636 return sum / i; 637 } 638 639 JNIEXPORT jdouble JNICALL 640 Java_MyPackage_HeapMonitorStackDepthTest_getAverageStackDepth(JNIEnv *env, 641 jclass cls) { 642 double result = calculate_average_stack_depth((*jvmti)->GetObjectAllocTraces); 643 644 if (result != 0) { 645 return result; 646 } 647 648 // It is possible all the live objects got collected, check the garbage traces 649 // in case. 650 return calculate_average_stack_depth((*jvmti)->GetGarbageTraces); 651 } 652 653 typedef struct sThreadsFound { 654 jint* threads; 655 int num_threads; 656 } ThreadsFound; 657 658 static void find_threads_in_traces(jvmtiAllocTraceInfo* traces, 659 jint trace_counter, 660 ThreadsFound* thread_data) { 661 int i; 662 jint* threads = thread_data->threads; 663 int num_threads = thread_data->num_threads; 664 665 // We are looking for at last expected_num_threads different traces. 666 for (i = 0; i < trace_counter; i++) { 667 jvmtiAllocTraceInfo* trace = traces + i; 668 jvmtiStackInfo* stack_info = trace->stack_info; 669 jint thread_id = trace->thread_id; 670 671 // Check it is the right frame: only accept helper top framed traces. 672 if (stack_info->frame_count == 0) { 673 continue; 674 } 675 676 jmethodID methodid = stack_info->frame_buffer[0].method; 677 char *name = NULL, *signature = NULL, *file_name = NULL; 678 (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0); 679 680 if (strcmp(name, "helper")) { 681 continue; 682 } 683 684 // Really not efficient look-up but it's for a test... 685 int found = 0; 686 int j; 687 for (j = 0; j < num_threads; j++) { 688 if (thread_id == threads[j]) { 689 found = 1; 690 break; 691 } 692 } 693 694 if (!found) { 695 threads[num_threads] = thread_id; 696 num_threads++; 697 } 698 } 699 thread_data->num_threads = num_threads; 700 } 701 702 JNIEXPORT jboolean JNICALL 703 Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls, 704 jintArray threads) { 705 jvmtiAllocTraceInfo* traces; 706 jint trace_counter; 707 708 ThreadsFound thread_data; 709 thread_data.threads = (*env)->GetIntArrayElements(env, threads, 0); 710 thread_data.num_threads = 0; 711 712 // Get live and garbage traces to ensure we capture all the threads that have 713 // been sampled. 714 if ((*jvmti)->GetObjectAllocTraces(jvmti, &traces, &trace_counter) != JVMTI_ERROR_NONE) { 715 return FALSE; 716 } 717 718 find_threads_in_traces(traces, trace_counter, &thread_data); 719 720 if ((*jvmti)->Deallocate(jvmti, (unsigned char*) traces) != JVMTI_ERROR_NONE) { 721 return FALSE; 722 } 723 724 if ((*jvmti)->GetGarbageTraces(jvmti, &traces, &trace_counter) != JVMTI_ERROR_NONE) { 725 return FALSE; 726 } 727 728 find_threads_in_traces(traces, trace_counter, &thread_data); 729 730 if ((*jvmti)->Deallocate(jvmti, (unsigned char*) traces) != JVMTI_ERROR_NONE) { 731 return FALSE; 732 } 733 734 (*env)->ReleaseIntArrayElements(env, threads, thread_data.threads, 0); 735 return TRUE; 736 } 737 738 JNIEXPORT void JNICALL 739 Java_MyPackage_HeapMonitorCachedTest_getLiveTracesToForceGc(JNIEnv *env, 740 jclass cls) { 741 jvmtiAllocTraceInfo* traces; 742 jint trace_counter; 743 744 jvmtiError error = (*jvmti)->GetObjectAllocTraces(jvmti, &traces, 745 &trace_counter); 746 747 if (error != JVMTI_ERROR_NONE) { 748 return; 749 } 750 751 (*jvmti)->Deallocate(jvmti, (unsigned char*) traces); 752 } 753 754 static jboolean compare_traces(jvmtiAllocTraceInfo* traces, 755 int trace_count, 756 jvmtiAllocTraceInfo* other_traces, 757 int other_trace_count, 758 int print_out_comparisons) { 759 if (trace_count != other_trace_count) { 760 return FALSE; 761 } 762 763 int i; 764 for (i = 0; i < trace_count; i++) { 765 jvmtiAllocTraceInfo* trace = traces + i; 766 jvmtiAllocTraceInfo* other_trace = other_traces + i; 767 768 jvmtiStackInfo* stack_info = trace->stack_info; 769 jvmtiStackInfo* other_stack_info = trace->stack_info; 770 771 if (stack_info->frame_count != other_stack_info->frame_count) { 772 return FALSE; 773 } 774 775 if (trace->size != other_trace->size) { 776 return FALSE; 777 } 778 779 if (trace->thread_id != other_trace->thread_id) { 780 return FALSE; 781 } 782 783 jvmtiFrameInfo* frames = stack_info->frame_buffer; 784 jvmtiFrameInfo* other_frames = other_stack_info->frame_buffer; 785 if (memcmp(frames, other_frames, sizeof(*frames) * stack_info->frame_count)) { 786 return FALSE; 787 } 788 } 789 790 return TRUE; 791 } 792 793 JNIEXPORT jboolean JNICALL 794 Java_MyPackage_HeapMonitorCachedTest_cachedAndLiveAreSame(JNIEnv *env, 795 jclass cls) { 796 // Get cached first, then get live (since live performs a GC). 797 jvmtiAllocTraceInfo* cached_traces; 798 jint cached_trace_counter; 799 jvmtiError error = (*jvmti)->GetCachedObjectAllocTraces(jvmti, &cached_traces, 800 &cached_trace_counter); 801 802 if (error != JVMTI_ERROR_NONE) { 803 return FALSE; 804 } 805 806 jvmtiAllocTraceInfo* live_traces; 807 jint live_trace_counter; 808 error = (*jvmti)->GetObjectAllocTraces(jvmti, &live_traces, 809 &live_trace_counter); 810 811 if (error != JVMTI_ERROR_NONE) { 812 return FALSE; 813 } 814 815 int result = compare_traces(cached_traces, cached_trace_counter, 816 live_traces, live_trace_counter, 817 PRINT_OUT); 818 819 if ((*jvmti)->Deallocate(jvmti, (unsigned char*) cached_traces) != JVMTI_ERROR_NONE) { 820 return FALSE; 821 } 822 if ((*jvmti)->Deallocate(jvmti, (unsigned char*) live_traces) != JVMTI_ERROR_NONE) { 823 return FALSE; 824 } 825 return result; 826 } 827 828 static long hash(long hash_code, long value) { 829 return hash_code * 31 + value; 830 } 831 832 static long get_hash_code(jvmtiAllocTraceInfo* traces, jint trace_counter) { 833 int hash_code = 17; 834 int i, j; 835 836 hash_code = hash(hash_code, trace_counter); 837 for (i = 0; i < trace_counter; i++) { 838 jvmtiAllocTraceInfo* trace = traces + i; 839 840 hash_code = hash(hash_code, trace->size); 841 hash_code = hash(hash_code, trace->thread_id); 842 843 jvmtiStackInfo* stack_info = trace->stack_info; 844 hash_code = hash(hash_code, stack_info->frame_count); 845 846 int frame_count = stack_info->frame_count; 847 jvmtiFrameInfo* frames = stack_info->frame_buffer; 848 hash_code = hash(hash_code, frame_count); 849 for (j = 0; j < frame_count; j++) { 850 hash_code = hash(hash_code, (long) frames[i].method); 851 hash_code = hash(hash_code, frames[i].location); 852 } 853 } 854 855 return TRUE; 856 } 857 858 JNIEXPORT jlong JNICALL 859 Java_MyPackage_HeapMonitorCachedTest_getCachedHashCode(JNIEnv *env, 860 jclass cls) { 861 // Get cached first, then get live. 862 jvmtiAllocTraceInfo* cached_traces; 863 jint cached_trace_counter; 864 jvmtiError error = (*jvmti)->GetCachedObjectAllocTraces(jvmti, &cached_traces, 865 &cached_trace_counter); 866 867 if (error != JVMTI_ERROR_NONE) { 868 return 0; 869 } 870 871 long hash_code = get_hash_code(cached_traces, cached_trace_counter); 872 873 if ((*jvmti)->Deallocate(jvmti, (unsigned char*) cached_traces) != JVMTI_ERROR_NONE) { 874 return FALSE; 875 } 876 877 return hash_code; 878 } 879 880 JNIEXPORT jboolean JNICALL 881 Java_MyPackage_HeapMonitorTest_framesAreNotLive(JNIEnv *env, 882 jclass cls, 883 jobjectArray frames) { 884 return !check_and(env, frames, FALSE, FALSE, TRUE, PRINT_OUT); 885 } 886 887 #ifdef __cplusplus 888 } 889 #endif