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 PASSED 0 46 #define FAILED 2 47 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 JNIEXPORT void JNICALL OnVMInit(jvmtiEnv *jvmti, JNIEnv *jni_env, jthread thread) { 101 } 102 103 JNIEXPORT void JNICALL OnClassLoad(jvmtiEnv *jvmti_env, JNIEnv *jni_env, 104 jthread thread, jclass klass) { 105 // NOP. 106 } 107 108 JNIEXPORT void JNICALL OnClassPrepare(jvmtiEnv *jvmti_env, JNIEnv *jni_env, 109 jthread thread, jclass klass) { 110 // We need to do this to "prime the pump", as it were -- make sure 111 // that all of the methodIDs have been initialized internally, for 112 // AsyncGetCallTrace. 113 jint method_count; 114 jmethodID *methods = 0; 115 jvmtiError err = (*jvmti)->GetClassMethods(jvmti, klass, &method_count, &methods); 116 if ((err != JVMTI_ERROR_NONE) && (err != JVMTI_ERROR_CLASS_NOT_PREPARED)) { 117 // JVMTI_ERROR_CLASS_NOT_PREPARED is okay because some classes may 118 // be loaded but not prepared at this point. 119 throw_exc(jni_env, "Failed to create method IDs for methods in class\n"); 120 } 121 } 122 123 static 124 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { 125 jint res; 126 127 res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), 128 JVMTI_VERSION_9); 129 if (res != JNI_OK || jvmti == NULL) { 130 printf(" Error: wrong result of a valid call to GetEnv!\n"); 131 return JNI_ERR; 132 } 133 134 jvmtiEventCallbacks callbacks; 135 memset(&callbacks, 0, sizeof(callbacks)); 136 137 callbacks.VMInit = &OnVMInit; 138 callbacks.ClassLoad = &OnClassLoad; 139 callbacks.ClassPrepare = &OnClassPrepare; 140 141 jvmtiCapabilities caps; 142 memset(&caps, 0, sizeof(caps)); 143 // Get line numbers, sample heap, and filename for the test. 144 caps.can_get_line_numbers = 1; 145 caps.can_sample_heap = 1; 146 caps.can_get_source_file_name = 1; 147 if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), 148 "Add capabilities\n")){ 149 return JNI_ERR; 150 } 151 152 if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks, 153 sizeof(jvmtiEventCallbacks)), 154 " Set Event Callbacks")) { 155 return JNI_ERR; 156 } 157 if (check_error((*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 158 JVMTI_EVENT_VM_INIT, NULL), 159 "Set Event for VM Init")) { 160 return JNI_ERR; 161 } 162 if (check_error((*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 163 JVMTI_EVENT_CLASS_LOAD, NULL), 164 "Set Event for Class Load")) { 165 return JNI_ERR; 166 } 167 if (check_error( (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 168 JVMTI_EVENT_CLASS_PREPARE, NULL), 169 "Set Event for Class Prepare")) { 170 return JNI_ERR; 171 } 172 173 return JNI_OK; 174 } 175 176 // Given a method and a location, this method gets the line number. 177 // Kind of expensive, comparatively. 178 static 179 jint get_line_number(jvmtiEnv *jvmti, jmethodID method, jlocation location) { 180 // The location is -1 if the bci isn't known or -3 for a native method. 181 if (location == -1 || location == -3) { 182 return -1; 183 } 184 185 // Read the line number table. 186 jvmtiLineNumberEntry *table_ptr = 0; 187 jint line_number_table_entries; 188 int jvmti_error = (*jvmti)->GetLineNumberTable(jvmti, method, 189 &line_number_table_entries, 190 &table_ptr); 191 192 if (JVMTI_ERROR_NONE != jvmti_error) { 193 return -1; 194 } 195 if (line_number_table_entries <= 0) { 196 return -1; 197 } 198 if (line_number_table_entries == 1) { 199 return table_ptr[0].line_number; 200 } 201 202 // Go through all the line numbers... 203 jint last_location = table_ptr[0].start_location; 204 int l; 205 for (l = 1; l < line_number_table_entries; l++) { 206 // ... and if you see one that is in the right place for your 207 // location, you've found the line number! 208 if ((location < table_ptr[l].start_location) && 209 (location >= last_location)) { 210 return table_ptr[l - 1].line_number; 211 } 212 last_location = table_ptr[l].start_location; 213 } 214 215 if (location >= last_location) { 216 return table_ptr[line_number_table_entries - 1].line_number; 217 } else { 218 return -1; 219 } 220 } 221 222 typedef struct _ExpectedContentFrame { 223 const char *name; 224 const char *signature; 225 const char *file_name; 226 int line_number; 227 } ExpectedContentFrame; 228 229 static jint check_sample_content(JNIEnv *env, 230 jvmtiStackTrace *trace, 231 ExpectedContentFrame *expected, 232 int expected_count) { 233 int i; 234 235 if (expected_count > trace->frame_count) { 236 return 0; 237 } 238 239 for (i = 0; i < expected_count; i++) { 240 // Get basic information out of the trace. 241 int bci = trace->frames[i].location; 242 jmethodID methodid = trace->frames[i].method; 243 char *name = NULL, *signature = NULL, *file_name = NULL; 244 245 if (bci < 0) { 246 return 0; 247 } 248 249 // Transform into usable information. 250 int line_number = get_line_number(jvmti, methodid, bci); 251 (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0); 252 253 jclass declaring_class; 254 if (JVMTI_ERROR_NONE != 255 (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) { 256 return 0; 257 } 258 259 jvmtiError err = (*jvmti)->GetSourceFileName(jvmti, declaring_class, 260 &file_name); 261 if (err != JVMTI_ERROR_NONE) { 262 return 0; 263 } 264 265 // Compare now, none should be NULL. 266 if (name == NULL) { 267 return 0; 268 } 269 270 if (file_name == NULL) { 271 return 0; 272 } 273 274 if (signature == NULL) { 275 return 0; 276 } 277 278 if (strcmp(name, expected[i].name) || 279 strcmp(signature, expected[i].signature) || 280 strcmp(file_name, expected[i].file_name) || 281 line_number != expected[i].line_number) { 282 return 0; 283 } 284 } 285 286 return 1; 287 } 288 289 static jint compare_samples(JNIEnv* env, jvmtiStackTrace* traces, int trace_count, 290 ExpectedContentFrame* expected_content, size_t size) { 291 // We expect the code to record correctly the bci, retrieve the line 292 // number, have the right method and the class name of the first frames. 293 int i; 294 for (i = 0; i < trace_count; i++) { 295 jvmtiStackTrace *trace = traces + i; 296 if (check_sample_content(env, trace, expected_content, size)) { 297 // At least one frame matched what we were looking for. 298 return 1; 299 } 300 } 301 302 return 0; 303 } 304 305 static jint check_samples(JNIEnv* env, ExpectedContentFrame* expected, 306 size_t size, 307 jvmtiError (*const get_traces)(jvmtiEnv*, jvmtiStackTraces*)) { 308 jvmtiStackTraces traces; 309 jvmtiError error = get_traces(jvmti, &traces); 310 311 if (error != JVMTI_ERROR_NONE) { 312 return 0; 313 } 314 315 int result = compare_samples(env, traces.stack_traces, traces.trace_count, 316 expected, size); 317 (*jvmti)->ReleaseTraces(jvmti, &traces); 318 return result; 319 } 320 321 static jint frames_exist_live(JNIEnv* env, ExpectedContentFrame* expected, 322 size_t size) { 323 return check_samples(env, expected, size, (*jvmti)->GetLiveTraces); 324 } 325 326 static jint frames_exist_recent(JNIEnv* env, ExpectedContentFrame* expected, 327 size_t size) { 328 return check_samples(env, expected, size, (*jvmti)->GetGarbageTraces); 329 } 330 331 static jint frames_exist_frequent(JNIEnv* env, ExpectedContentFrame* expected, 332 size_t size) { 333 return check_samples(env, expected, size, (*jvmti)->GetFrequentGarbageTraces); 334 } 335 336 // Static native API for various tests. 337 static void fill_native_frames(JNIEnv* env, jobjectArray frames, 338 ExpectedContentFrame* native_frames, size_t size) { 339 size_t i; 340 for(i = 0; i < size; i++) { 341 jobject obj = (*env)->GetObjectArrayElement(env, frames, i); 342 jclass frame_class = (*env)->GetObjectClass(env, obj); 343 jfieldID line_number_field_id = (*env)->GetFieldID(env, frame_class, "lineNumber", "I"); 344 int line_number = (*env)->GetIntField(env, obj, line_number_field_id); 345 346 jfieldID string_id = (*env)->GetFieldID(env, frame_class, "method", "Ljava/lang/String;"); 347 jstring string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); 348 const char* method = (*env)->GetStringUTFChars(env, string_object, 0); 349 350 string_id = (*env)->GetFieldID(env, frame_class, "fileName", "Ljava/lang/String;"); 351 string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); 352 const char* file_name = (*env)->GetStringUTFChars(env, string_object, 0); 353 354 string_id = (*env)->GetFieldID(env, frame_class, "signature", "Ljava/lang/String;"); 355 string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); 356 const char* signature= (*env)->GetStringUTFChars(env, string_object, 0); 357 358 native_frames[i].name = method; 359 native_frames[i].file_name = file_name; 360 native_frames[i].signature = signature; 361 native_frames[i].line_number = line_number; 362 } 363 } 364 365 static jint check_and(JNIEnv *env, jobjectArray frames, int live, int recent, 366 int frequent) { 367 jobject loader = NULL; 368 369 if (frames == NULL) { 370 return 0; 371 } 372 373 // Start by transforming the frames into a C-friendly structure. 374 jsize size = (*env)->GetArrayLength(env, frames); 375 ExpectedContentFrame native_frames[size]; 376 fill_native_frames(env, frames, native_frames, size); 377 378 if (jvmti == NULL) { 379 throw_exc(env, "JVMTI client was not properly loaded!\n"); 380 return 0; 381 } 382 383 int result = 1; 384 385 if (live) { 386 result = frames_exist_live(env, native_frames, size); 387 } 388 389 if (recent) { 390 result = result && 391 frames_exist_recent(env, native_frames, size); 392 } 393 394 if (frequent) { 395 result = result && 396 frames_exist_frequent(env, native_frames, size); 397 } 398 399 return result; 400 } 401 402 static jint check_or(JNIEnv *env, jobjectArray frames, int live, int recent, 403 int frequent) { 404 jobject loader = NULL; 405 406 if (frames == NULL) { 407 return 0; 408 } 409 410 // Start by transforming the frames into a C-friendly structure. 411 jsize size = (*env)->GetArrayLength(env, frames); 412 ExpectedContentFrame native_frames[size]; 413 fill_native_frames(env, frames, native_frames, size); 414 415 if (jvmti == NULL) { 416 throw_exc(env, "JVMTI client was not properly loaded!\n"); 417 return 0; 418 } 419 420 int result = 0; 421 422 if (live) { 423 result = frames_exist_live(env, native_frames, size); 424 } 425 426 if (recent) { 427 result = result || 428 frames_exist_recent(env, native_frames, size); 429 } 430 431 if (frequent) { 432 result = result || 433 frames_exist_frequent(env, native_frames, size); 434 } 435 436 return result; 437 } 438 439 static jint checkAll(JNIEnv *env, jobjectArray frames) { 440 return check_and(env, frames, 1, 1, 1); 441 } 442 443 static jint checkNone(JNIEnv *env, jobjectArray frames) { 444 jobject loader = NULL; 445 446 if (frames == NULL) { 447 return 0; 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 0; 458 } 459 460 if ((!frames_exist_live(env, native_frames, size)) && 461 (!frames_exist_recent(env, native_frames, size)) && 462 (!frames_exist_frequent(env, native_frames, size))) { 463 return 1; 464 } 465 return 0; 466 } 467 468 static void enable_sampling() { 469 check_error((*jvmti)->StartHeapSampling(jvmti, 1 << 19, MAX_TRACES), 470 "Start Heap Sampling"); 471 } 472 473 static void enable_sampling_with_rate(int rate) { 474 check_error((*jvmti)->StartHeapSampling(jvmti, rate, MAX_TRACES), 475 "Start Heap Sampling"); 476 } 477 478 static void enable_sampling_with_max_traces(int max_traces) { 479 check_error((*jvmti)->StartHeapSampling(jvmti, 1 << 19, max_traces), 480 "Start Heap Sampling"); 481 } 482 483 static void disable_sampling() { 484 check_error((*jvmti)->StopHeapSampling(jvmti), "Stop Heap Sampling"); 485 } 486 487 JNIEXPORT jint JNICALL 488 Java_MyPackage_HeapMonitorTest_checkFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 489 // We want the frames in each part. 490 if (!checkAll(env, frames)) { 491 return FAILED; 492 } 493 return PASSED; 494 } 495 496 JNIEXPORT void JNICALL 497 Java_MyPackage_HeapMonitorTest_enableSampling(JNIEnv *env, jclass cls) { 498 enable_sampling(); 499 } 500 501 JNIEXPORT jint JNICALL 502 Java_MyPackage_HeapMonitorOnOffTest_checkFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 503 // We want the frames in each part. 504 if (!checkAll(env, frames)) { 505 return FAILED; 506 } 507 return PASSED; 508 } 509 510 JNIEXPORT jint JNICALL 511 Java_MyPackage_HeapMonitorOnOffTest_checkWipeOut(JNIEnv *env, jclass cls, jobjectArray frames) { 512 // We want the frames in none of the parts. 513 if (!checkNone(env, frames)) { 514 return FAILED; 515 } 516 return PASSED; 517 } 518 519 JNIEXPORT void JNICALL 520 Java_MyPackage_HeapMonitorOnOffTest_enableSampling(JNIEnv *env, jclass cls) { 521 enable_sampling(); 522 } 523 524 JNIEXPORT void JNICALL 525 Java_MyPackage_HeapMonitorOnOffTest_disableSampling(JNIEnv *env, jclass cls) { 526 disable_sampling(); 527 } 528 529 JNIEXPORT jint JNICALL 530 Java_MyPackage_HeapMonitorRecentTest_checkFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 531 // We want the frames in each part. 532 if (!checkAll(env, frames)) { 533 return FAILED; 534 } 535 return PASSED; 536 } 537 538 JNIEXPORT jint JNICALL 539 Java_MyPackage_HeapMonitorRecentTest_checkLiveOrRecentFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 540 if (check_or(env, frames, 1, 1, 0)) { 541 return FAILED; 542 } 543 return PASSED; 544 } 545 546 JNIEXPORT jint JNICALL 547 Java_MyPackage_HeapMonitorRecentTest_checkLiveAndRecentFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 548 if (check_and(env, frames, 1, 1, 0)) { 549 return FAILED; 550 } 551 return PASSED; 552 } 553 554 JNIEXPORT void JNICALL 555 Java_MyPackage_HeapMonitorRecentTest_enableSampling(JNIEnv *env, jclass cls) { 556 enable_sampling(); 557 } 558 559 JNIEXPORT jint JNICALL 560 Java_MyPackage_HeapMonitorFrequentTest_checkFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 561 // We want the frames in each part. 562 if (!checkAll(env, frames)) { 563 return FAILED; 564 } 565 return PASSED; 566 } 567 568 JNIEXPORT jint JNICALL 569 Java_MyPackage_HeapMonitorFrequentTest_checkFrequentFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 570 if (check_and(env, frames, 0, 0, 1)) { 571 return PASSED; 572 } 573 return FAILED; 574 } 575 576 JNIEXPORT void JNICALL 577 Java_MyPackage_HeapMonitorFrequentTest_enableSampling(JNIEnv *env, jclass cls) { 578 enable_sampling(); 579 } 580 581 JNIEXPORT jboolean JNICALL 582 Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env, jclass cls) { 583 jvmtiCapabilities caps; 584 memset(&caps, 0, sizeof(caps)); 585 caps.can_sample_heap= 1; 586 if (check_error((*jvmti)->RelinquishCapabilities(jvmti, &caps), 587 "Add capabilities\n")){ 588 return 0; 589 } 590 591 if (check_capability_error((*jvmti)->StartHeapSampling(jvmti, 1<<19, 592 MAX_TRACES), 593 "Start Heap Sampling")) { 594 return 0; 595 } 596 597 if (check_capability_error((*jvmti)->StopHeapSampling(jvmti), 598 "Stop Heap Sampling")) { 599 return 0; 600 } 601 602 if (check_capability_error((*jvmti)->ReleaseTraces(jvmti, NULL), 603 "Release Traces")) { 604 return 0; 605 } 606 607 if (check_capability_error((*jvmti)->GetHeapSamplingStats(jvmti, NULL), 608 "Get Heap Sampling Stats")) { 609 return 0; 610 } 611 612 if (check_capability_error((*jvmti)->GetGarbageTraces(jvmti, NULL), 613 "Get Garbage Traces")) { 614 return 0; 615 } 616 617 if (check_capability_error((*jvmti)->GetFrequentGarbageTraces(jvmti, NULL), 618 "Get Frequent Garbage Traces")) { 619 return 0; 620 } 621 622 if (check_capability_error((*jvmti)->GetLiveTraces(jvmti, NULL), 623 "Get Live Traces")) { 624 return 0; 625 } 626 627 // Calling enable sampling should fail now. 628 return 1; 629 } 630 631 JNIEXPORT void JNICALL 632 Java_MyPackage_HeapMonitorStatSimpleTest_enableSampling(JNIEnv *env, jclass cls) { 633 enable_sampling(); 634 } 635 636 static jint stats_are_zero() { 637 jvmtiHeapSamplingStats stats; 638 check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats), 639 "Heap Sampling Statistics"); 640 641 jvmtiHeapSamplingStats zero; 642 memset(&zero, 0, sizeof(zero)); 643 return memcmp(&stats, &zero, sizeof(zero)) == 0; 644 } 645 646 JNIEXPORT jint JNICALL 647 Java_MyPackage_HeapMonitorStatSimpleTest_statsNull(JNIEnv *env, jclass cls) { 648 return stats_are_zero(); 649 } 650 651 JNIEXPORT void JNICALL 652 Java_MyPackage_HeapMonitorStatCorrectnessTest_disableSampling(JNIEnv *env, jclass cls) { 653 disable_sampling(); 654 } 655 656 JNIEXPORT void JNICALL 657 Java_MyPackage_HeapMonitorStatCorrectnessTest_enableSampling(JNIEnv *env, jclass cls, jint rate) { 658 enable_sampling_with_rate(rate); 659 } 660 661 JNIEXPORT jint JNICALL 662 Java_MyPackage_HeapMonitorStatCorrectnessTest_statsNull(JNIEnv *env, jclass cls) { 663 return stats_are_zero(); 664 } 665 666 JNIEXPORT jint JNICALL 667 Java_MyPackage_HeapMonitorStatCorrectnessTest_statsHaveSamples(JNIEnv *env, 668 jclass cls, 669 int expected, 670 int percent_error) { 671 jvmtiHeapSamplingStats stats; 672 check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats), 673 "Heap Sampling Statistics"); 674 675 double diff_ratio = (stats.sample_count - expected); 676 diff_ratio = (diff_ratio < 0) ? -diff_ratio : diff_ratio; 677 diff_ratio /= expected; 678 679 return diff_ratio * 100 > percent_error; 680 } 681 682 JNIEXPORT void JNICALL 683 Java_MyPackage_HeapMonitorStatRateTest_enableSampling(JNIEnv *env, jclass cls, jint rate) { 684 enable_sampling_with_rate(rate); 685 } 686 687 JNIEXPORT void JNICALL 688 Java_MyPackage_HeapMonitorStatRateTest_disableSampling(JNIEnv *env, jclass cls) { 689 disable_sampling(); 690 } 691 692 JNIEXPORT jdouble JNICALL 693 Java_MyPackage_HeapMonitorStatRateTest_getAverageRate(JNIEnv *env, jclass cls) { 694 jvmtiHeapSamplingStats stats; 695 check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats), 696 "Heap Sampling Statistics"); 697 return ((double) stats.sample_rate_accumulation) / stats.sample_rate_count; 698 } 699 700 JNIEXPORT void JNICALL 701 Java_MyPackage_HeapMonitorStackDepthTest_enableSampling(JNIEnv *env, jclass cls) { 702 enable_sampling(); 703 } 704 705 JNIEXPORT void JNICALL 706 Java_MyPackage_HeapMonitorStackDepthTest_disableSampling(JNIEnv *env, jclass cls) { 707 disable_sampling(); 708 } 709 710 JNIEXPORT jdouble JNICALL 711 Java_MyPackage_HeapMonitorStackDepthTest_getAverageStackDepth(JNIEnv *env, jclass cls) { 712 jvmtiStackTraces traces; 713 jvmtiError error = (*jvmti)->GetLiveTraces(jvmti, &traces);; 714 715 if (error != JVMTI_ERROR_NONE) { 716 return 0; 717 } 718 719 int trace_count = traces.trace_count; 720 721 if (trace_count == 0) { 722 return 0; 723 } 724 725 int i; 726 jvmtiStackTrace* stack_traces = traces.stack_traces; 727 double sum = 0; 728 for (i = 0; i < trace_count; i++) { 729 jvmtiStackTrace *stack_trace = stack_traces + i; 730 sum += stack_trace->frame_count; 731 } 732 733 return sum / i; 734 } 735 736 JNIEXPORT void JNICALL 737 Java_MyPackage_HeapMonitorThreadTest_enableSampling(JNIEnv *env, jclass cls) { 738 // Remember a lot of the garbage collected samples to ensure test correctness. 739 enable_sampling_with_max_traces(100000); 740 } 741 742 typedef struct sThreadsFound { 743 jint *threads; 744 int num_threads; 745 }ThreadsFound; 746 747 static void find_threads_in_traces(jvmtiStackTraces* traces, ThreadsFound* thread_data) { 748 int i; 749 jvmtiStackTrace* stack_traces = traces->stack_traces; 750 int trace_count = traces->trace_count; 751 752 jint *threads = thread_data->threads; 753 int num_threads = thread_data->num_threads; 754 755 // We are looking for at last expected_num_threads different traces. 756 for (i = 0; i < trace_count; i++) { 757 jvmtiStackTrace *stack_trace = stack_traces + i; 758 jlong thread_id = stack_trace->thread_id; 759 760 // Check it is the right frame: only accept helper top framed traces. 761 jmethodID methodid = stack_trace->frames[0].method; 762 char *name = NULL, *signature = NULL, *file_name = NULL; 763 (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0); 764 765 if (strcmp(name, "helper")) { 766 continue; 767 } 768 769 // Really not efficient look-up but it's for a test... 770 int found = 0; 771 int j; 772 for (j = 0; j < num_threads; j++) { 773 if (thread_id == threads[j]) { 774 found = 1; 775 break; 776 } 777 } 778 779 if (!found) { 780 threads[num_threads] = thread_id; 781 num_threads++; 782 } 783 } 784 thread_data->num_threads = num_threads; 785 } 786 787 JNIEXPORT jboolean JNICALL 788 Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls, jintArray threads) { 789 jvmtiStackTraces traces; 790 ThreadsFound thread_data; 791 thread_data.threads = (*env)->GetIntArrayElements(env, threads, 0); 792 thread_data.num_threads = 0; 793 794 // Get live and garbage traces to ensure we capture all the threads that have 795 // been sampled. 796 if ((*jvmti)->GetLiveTraces(jvmti, &traces) != JVMTI_ERROR_NONE) { 797 return 0; 798 } 799 800 find_threads_in_traces(&traces, &thread_data); 801 802 if ((*jvmti)->ReleaseTraces(jvmti, &traces) != JVMTI_ERROR_NONE) { 803 return 0; 804 } 805 806 if ((*jvmti)->GetGarbageTraces(jvmti, &traces) != JVMTI_ERROR_NONE) { 807 return 0; 808 } 809 810 find_threads_in_traces(&traces, &thread_data); 811 812 if ((*jvmti)->ReleaseTraces(jvmti, &traces) != JVMTI_ERROR_NONE) { 813 return 0; 814 } 815 816 (*env)->ReleaseIntArrayElements(env, threads, thread_data.threads, 0); 817 return 1; 818 } 819 820 JNIEXPORT void JNICALL 821 Java_MyPackage_Switch_enableSampling(JNIEnv *env, jclass cls) { 822 enable_sampling(); 823 } 824 825 JNIEXPORT void JNICALL 826 Java_MyPackage_Switch_disableSampling(JNIEnv *env, jclass cls) { 827 disable_sampling(); 828 } 829 830 #ifdef __cplusplus 831 } 832 #endif