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 <string.h> 26 #include "jvmti.h" 27 28 #ifdef __cplusplus 29 extern "C" { 30 #endif 31 32 #ifndef JNI_ENV_ARG 33 34 #ifdef __cplusplus 35 #define JNI_ENV_ARG(x, y) y 36 #define JNI_ENV_PTR(x) x 37 #else 38 #define JNI_ENV_ARG(x,y) x, y 39 #define JNI_ENV_PTR(x) (*x) 40 #endif 41 42 #endif 43 44 #define PASSED 0 45 #define FAILED 2 46 47 #define MAX_TRACES 400 48 49 static const char *EXC_CNAME = "java/lang/Exception"; 50 static jvmtiEnv *jvmti = NULL; 51 52 static int check_error(jvmtiError err, const char* s) { 53 if (err != JVMTI_ERROR_NONE) { 54 printf(" ## %s error: %d\n", s, err); 55 return 1; 56 } 57 return 0; 58 } 59 60 static int check_capability_error(jvmtiError err, const char* s) { 61 if (err != JVMTI_ERROR_NONE) { 62 if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY) { 63 return 0; 64 } 65 printf(" ## %s error: %d\n", s, err); 66 return 1; 67 } 68 return 1; 69 } 70 71 static 72 jint throw_exc(JNIEnv *env, char *msg) { 73 jclass exc_class = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG(env, EXC_CNAME)); 74 75 if (exc_class == NULL) { 76 printf("throw_exc: Error in FindClass(env, %s)\n", EXC_CNAME); 77 return -1; 78 } 79 return JNI_ENV_PTR(env)->ThrowNew(JNI_ENV_ARG(env, exc_class), msg); 80 } 81 82 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved); 83 84 JNIEXPORT 85 jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { 86 return Agent_Initialize(jvm, options, reserved); 87 } 88 89 JNIEXPORT 90 jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) { 91 return Agent_Initialize(jvm, options, reserved); 92 } 93 94 JNIEXPORT 95 jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { 96 return JNI_VERSION_1_8; 97 } 98 99 JNIEXPORT void JNICALL OnVMInit(jvmtiEnv *jvmti, JNIEnv *jni_env, jthread thread) { 100 } 101 102 JNIEXPORT void JNICALL OnClassLoad(jvmtiEnv *jvmti_env, JNIEnv *jni_env, 103 jthread thread, jclass klass) { 104 // NOP. 105 } 106 107 JNIEXPORT void JNICALL OnClassPrepare(jvmtiEnv *jvmti_env, JNIEnv *jni_env, 108 jthread thread, jclass klass) { 109 // We need to do this to "prime the pump", as it were -- make sure 110 // that all of the methodIDs have been initialized internally, for 111 // AsyncGetCallTrace. 112 jint method_count; 113 jmethodID *methods = 0; 114 jvmtiError err = (*jvmti)->GetClassMethods(jvmti, klass, &method_count, &methods); 115 if ((err != JVMTI_ERROR_NONE) && (err != JVMTI_ERROR_CLASS_NOT_PREPARED)) { 116 // JVMTI_ERROR_CLASS_NOT_PREPARED is okay because some classes may 117 // be loaded but not prepared at this point. 118 throw_exc(jni_env, "Failed to create method IDs for methods in class\n"); 119 } 120 } 121 122 static 123 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) { 124 jint res; 125 126 res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti), 127 JVMTI_VERSION_9); 128 if (res != JNI_OK || jvmti == NULL) { 129 printf(" Error: wrong result of a valid call to GetEnv!\n"); 130 return JNI_ERR; 131 } 132 133 jvmtiEventCallbacks callbacks; 134 memset(&callbacks, 0, sizeof(callbacks)); 135 136 callbacks.VMInit = &OnVMInit; 137 callbacks.ClassLoad = &OnClassLoad; 138 callbacks.ClassPrepare = &OnClassPrepare; 139 140 jvmtiCapabilities caps; 141 memset(&caps, 0, sizeof(caps)); 142 // Get line numbers, sample heap, and filename for the test. 143 caps.can_get_line_numbers = 1; 144 caps.can_sample_heap= 1; 145 caps.can_get_source_file_name = 1; 146 if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), 147 "Add capabilities\n")){ 148 return JNI_ERR; 149 } 150 151 if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks, 152 sizeof(jvmtiEventCallbacks)), 153 " Set Event Callbacks")) { 154 return JNI_ERR; 155 } 156 if (check_error((*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 157 JVMTI_EVENT_VM_INIT, NULL), 158 "Set Event for VM Init")) { 159 return JNI_ERR; 160 } 161 if (check_error((*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 162 JVMTI_EVENT_CLASS_LOAD, NULL), 163 "Set Event for Class Load")) { 164 return JNI_ERR; 165 } 166 if (check_error( (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 167 JVMTI_EVENT_CLASS_PREPARE, NULL), 168 "Set Event for Class Prepare")) { 169 return JNI_ERR; 170 } 171 172 return JNI_OK; 173 } 174 175 // Given a method and a location, this method gets the line number. 176 // Kind of expensive, comparatively. 177 static 178 jint get_line_number(jvmtiEnv *jvmti, jmethodID method, jlocation location) { 179 // The location is -1 if the bci isn't known or -3 for a native method. 180 if (location == -1 || location == -3) { 181 return -1; 182 } 183 184 // Read the line number table. 185 jvmtiLineNumberEntry *table_ptr = 0; 186 jint line_number_table_entries; 187 int jvmti_error = (*jvmti)->GetLineNumberTable(jvmti, method, 188 &line_number_table_entries, 189 &table_ptr); 190 191 if (JVMTI_ERROR_NONE != jvmti_error) { 192 return -1; 193 } 194 if (line_number_table_entries <= 0) { 195 return -1; 196 } 197 if (line_number_table_entries == 1) { 198 return table_ptr[0].line_number; 199 } 200 201 // Go through all the line numbers... 202 jint last_location = table_ptr[0].start_location; 203 int l; 204 for (l = 1; l < line_number_table_entries; l++) { 205 // ... and if you see one that is in the right place for your 206 // location, you've found the line number! 207 if ((location < table_ptr[l].start_location) && 208 (location >= last_location)) { 209 return table_ptr[l - 1].line_number; 210 } 211 last_location = table_ptr[l].start_location; 212 } 213 214 if (location >= last_location) { 215 return table_ptr[line_number_table_entries - 1].line_number; 216 } else { 217 return -1; 218 } 219 } 220 221 typedef struct _ExpectedContentFrame { 222 const char *name; 223 const char *signature; 224 const char *file_name; 225 int line_number; 226 } ExpectedContentFrame; 227 228 static jint check_sample_content(JNIEnv *env, 229 jvmtiStackTrace *trace, 230 ExpectedContentFrame *expected, 231 int expected_count) { 232 int i; 233 234 if (expected_count > trace->frame_count) { 235 return 0; 236 } 237 238 for (i = 0; i < expected_count; i++) { 239 // Get basic information out of the trace. 240 int bci = trace->frames[i].location; 241 jmethodID methodid = trace->frames[i].method; 242 char *name = NULL, *signature = NULL, *file_name = NULL; 243 244 if (bci < 0) { 245 return 0; 246 } 247 248 // Transform into usable information. 249 int line_number = get_line_number(jvmti, methodid, bci); 250 (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0); 251 252 jclass declaring_class; 253 if (JVMTI_ERROR_NONE != 254 (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) { 255 return 0; 256 } 257 258 jvmtiError err = (*jvmti)->GetSourceFileName(jvmti, declaring_class, 259 &file_name); 260 if (err != JVMTI_ERROR_NONE) { 261 return 0; 262 } 263 264 // Compare now, none should be NULL. 265 if (name == NULL) { 266 return 0; 267 } 268 269 if (file_name == NULL) { 270 return 0; 271 } 272 273 if (signature == NULL) { 274 return 0; 275 } 276 277 if (strcmp(name, expected[i].name) || 278 strcmp(signature, expected[i].signature) || 279 strcmp(file_name, expected[i].file_name) || 280 line_number != expected[i].line_number) { 281 return 0; 282 } 283 } 284 285 return 1; 286 } 287 288 static jint compare_samples(JNIEnv* env, jvmtiStackTrace* traces, int trace_count, 289 ExpectedContentFrame* expected_content, size_t size) { 290 // We expect the code to record correctly the bci, retrieve the line 291 // number, have the right method and the class name of the first frames. 292 int i; 293 for (i = 0; i < trace_count; i++) { 294 jvmtiStackTrace *trace = traces + i; 295 if (check_sample_content(env, trace, expected_content, size)) { 296 // At least one frame matched what we were looking for. 297 return 1; 298 } 299 } 300 301 return 0; 302 } 303 304 static jint check_samples(JNIEnv* env, ExpectedContentFrame* expected, 305 size_t size, 306 jvmtiError (*const get_traces)(jvmtiEnv*, jvmtiStackTraces*)) { 307 jvmtiStackTraces traces; 308 jvmtiError error = get_traces(jvmti, &traces); 309 310 if (error != JVMTI_ERROR_NONE) { 311 return 0; 312 } 313 314 int result = compare_samples(env, traces.stack_traces, traces.trace_count, 315 expected, size); 316 (*jvmti)->ReleaseTraces(jvmti, &traces); 317 return result; 318 } 319 320 static jint frames_exist_live(JNIEnv* env, ExpectedContentFrame* expected, 321 size_t size) { 322 return check_samples(env, expected, size, (*jvmti)->GetLiveTraces); 323 } 324 325 static jint frames_exist_recent(JNIEnv* env, ExpectedContentFrame* expected, 326 size_t size) { 327 return check_samples(env, expected, size, (*jvmti)->GetGarbageTraces); 328 } 329 330 static jint frames_exist_frequent(JNIEnv* env, ExpectedContentFrame* expected, 331 size_t size) { 332 return check_samples(env, expected, size, (*jvmti)->GetFrequentGarbageTraces); 333 } 334 335 // Static native API for various tests. 336 static void fill_native_frames(JNIEnv* env, jobjectArray frames, 337 ExpectedContentFrame* native_frames, size_t size) { 338 size_t i; 339 for(i = 0; i < size; i++) { 340 jobject obj = (*env)->GetObjectArrayElement(env, frames, i); 341 jclass frame_class = (*env)->GetObjectClass(env, obj); 342 jfieldID line_number_field_id = (*env)->GetFieldID(env, frame_class, "lineNumber", "I"); 343 int line_number = (*env)->GetIntField(env, obj, line_number_field_id); 344 345 jfieldID string_id = (*env)->GetFieldID(env, frame_class, "method", "Ljava/lang/String;"); 346 jstring string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); 347 const char* method = (*env)->GetStringUTFChars(env, string_object, 0); 348 349 string_id = (*env)->GetFieldID(env, frame_class, "fileName", "Ljava/lang/String;"); 350 string_object = (jstring) (*env)->GetObjectField(env, obj, string_id); 351 const char* file_name = (*env)->GetStringUTFChars(env, string_object, 0); 352 353 string_id = (*env)->GetFieldID(env, frame_class, "signature", "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 jint check_and(JNIEnv *env, jobjectArray frames, int live, int recent, 365 int frequent) { 366 jobject loader = NULL; 367 368 if (frames == NULL) { 369 return 0; 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 0; 380 } 381 382 int result = 1; 383 384 if (live) { 385 result = frames_exist_live(env, native_frames, size); 386 } 387 388 if (recent) { 389 result = result && 390 frames_exist_recent(env, native_frames, size); 391 } 392 393 if (frequent) { 394 result = result && 395 frames_exist_frequent(env, native_frames, size); 396 } 397 398 return result; 399 } 400 401 static jint check_or(JNIEnv *env, jobjectArray frames, int live, int recent, 402 int frequent) { 403 jobject loader = NULL; 404 405 if (frames == NULL) { 406 return 0; 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 0; 417 } 418 419 int result = 0; 420 421 if (live) { 422 result = frames_exist_live(env, native_frames, size); 423 } 424 425 if (recent) { 426 result = result || 427 frames_exist_recent(env, native_frames, size); 428 } 429 430 if (frequent) { 431 result = result || 432 frames_exist_frequent(env, native_frames, size); 433 } 434 435 return result; 436 } 437 438 static jint checkAll(JNIEnv *env, jobjectArray frames) { 439 return check_and(env, frames, 1, 1, 1); 440 } 441 442 static jint checkNone(JNIEnv *env, jobjectArray frames) { 443 jobject loader = NULL; 444 445 if (frames == NULL) { 446 return 0; 447 } 448 449 // Start by transforming the frames into a C-friendly structure. 450 jsize size = (*env)->GetArrayLength(env, frames); 451 ExpectedContentFrame native_frames[size]; 452 fill_native_frames(env, frames, native_frames, size); 453 454 if (jvmti == NULL) { 455 throw_exc(env, "JVMTI client was not properly loaded!\n"); 456 return 0; 457 } 458 459 if ((!frames_exist_live(env, native_frames, size)) && 460 (!frames_exist_recent(env, native_frames, size)) && 461 (!frames_exist_frequent(env, native_frames, size))) { 462 return 1; 463 } 464 return 0; 465 } 466 467 static void enable_sampling() { 468 check_error((*jvmti)->StartHeapSampling(jvmti, 1 << 19, MAX_TRACES), 469 "Start Heap Sampling"); 470 } 471 472 static void enable_sampling_with_rate(int rate) { 473 check_error((*jvmti)->StartHeapSampling(jvmti, rate, MAX_TRACES), 474 "Start Heap Sampling"); 475 } 476 477 static void disable_sampling() { 478 check_error((*jvmti)->StopHeapSampling(jvmti), "Stop Heap Sampling"); 479 } 480 481 // HeapMonitorTest JNI. 482 JNIEXPORT jint JNICALL 483 Java_MyPackage_HeapMonitorTest_checkFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 484 // We want the frames in each part. 485 if (!checkAll(env, frames)) { 486 return FAILED; 487 } 488 return PASSED; 489 } 490 491 JNIEXPORT void JNICALL 492 Java_MyPackage_HeapMonitorTest_enableSampling(JNIEnv *env, jclass cls) { 493 enable_sampling(); 494 } 495 496 // HeapMonitorOnOffTest JNI. 497 JNIEXPORT jint JNICALL 498 Java_MyPackage_HeapMonitorOnOffTest_checkFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 499 // We want the frames in each part. 500 if (!checkAll(env, frames)) { 501 return FAILED; 502 } 503 return PASSED; 504 } 505 506 JNIEXPORT jint JNICALL 507 Java_MyPackage_HeapMonitorOnOffTest_checkWipeOut(JNIEnv *env, jclass cls, jobjectArray frames) { 508 // We want the frames in none of the parts. 509 if (!checkNone(env, frames)) { 510 return FAILED; 511 } 512 return PASSED; 513 } 514 515 JNIEXPORT void JNICALL 516 Java_MyPackage_HeapMonitorOnOffTest_enableSampling(JNIEnv *env, jclass cls) { 517 enable_sampling(); 518 } 519 520 JNIEXPORT void JNICALL 521 Java_MyPackage_HeapMonitorOnOffTest_disableSampling(JNIEnv *env, jclass cls) { 522 disable_sampling(); 523 } 524 525 // HeapMonitorRecentTest JNI. 526 JNIEXPORT jint JNICALL 527 Java_MyPackage_HeapMonitorRecentTest_checkFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 528 // We want the frames in each part. 529 if (!checkAll(env, frames)) { 530 return FAILED; 531 } 532 return PASSED; 533 } 534 535 JNIEXPORT jint JNICALL 536 Java_MyPackage_HeapMonitorRecentTest_checkLiveOrRecentFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 537 if (check_or(env, frames, 1, 1, 0)) { 538 return FAILED; 539 } 540 return PASSED; 541 } 542 543 JNIEXPORT jint JNICALL 544 Java_MyPackage_HeapMonitorRecentTest_checkLiveAndRecentFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 545 if (check_and(env, frames, 1, 1, 0)) { 546 return FAILED; 547 } 548 return PASSED; 549 } 550 551 JNIEXPORT void JNICALL 552 Java_MyPackage_HeapMonitorRecentTest_enableSampling(JNIEnv *env, jclass cls) { 553 enable_sampling(); 554 } 555 556 // HeapMonitorFrequentTest JNI. 557 JNIEXPORT jint JNICALL 558 Java_MyPackage_HeapMonitorFrequentTest_checkFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 559 // We want the frames in each part. 560 if (!checkAll(env, frames)) { 561 return FAILED; 562 } 563 return PASSED; 564 } 565 566 JNIEXPORT jint JNICALL 567 Java_MyPackage_HeapMonitorFrequentTest_checkFrequentFrames(JNIEnv *env, jclass cls, jobjectArray frames) { 568 if (check_and(env, frames, 0, 0, 1)) { 569 return PASSED; 570 } 571 return FAILED; 572 } 573 574 JNIEXPORT void JNICALL 575 Java_MyPackage_HeapMonitorFrequentTest_enableSampling(JNIEnv *env, jclass cls) { 576 enable_sampling(); 577 } 578 579 JNIEXPORT jboolean JNICALL 580 Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env, jclass cls) { 581 jvmtiCapabilities caps; 582 memset(&caps, 0, sizeof(caps)); 583 caps.can_sample_heap= 1; 584 if (check_error((*jvmti)->RelinquishCapabilities(jvmti, &caps), 585 "Add capabilities\n")){ 586 return 0; 587 } 588 589 if (check_capability_error((*jvmti)->StartHeapSampling(jvmti, 1<<19, 590 MAX_TRACES), 591 "Start Heap Sampling")) { 592 return 0; 593 } 594 595 if (check_capability_error((*jvmti)->StopHeapSampling(jvmti), 596 "Stop Heap Sampling")) { 597 return 0; 598 } 599 600 if (check_capability_error((*jvmti)->ReleaseTraces(jvmti, NULL), 601 "Release Traces")) { 602 return 0; 603 } 604 605 if (check_capability_error((*jvmti)->GetHeapSamplingStats(jvmti, NULL), 606 "Get Heap Sampling Stats")) { 607 return 0; 608 } 609 610 if (check_capability_error((*jvmti)->GetGarbageTraces(jvmti, NULL), 611 "Get Garbage Traces")) { 612 return 0; 613 } 614 615 if (check_capability_error((*jvmti)->GetFrequentGarbageTraces(jvmti, NULL), 616 "Get Frequent Garbage Traces")) { 617 return 0; 618 } 619 620 if (check_capability_error((*jvmti)->GetLiveTraces(jvmti, NULL), 621 "Get Live Traces")) { 622 return 0; 623 } 624 625 // Calling enable sampling should fail now. 626 return 1; 627 } 628 629 JNIEXPORT void JNICALL 630 Java_MyPackage_HeapMonitorStatSimpleTest_enableSampling(JNIEnv *env, jclass cls) { 631 enable_sampling(); 632 } 633 634 static jint stats_are_zero() { 635 jvmtiHeapSamplingStats stats; 636 check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats), 637 "Heap Sampling Statistics"); 638 639 jvmtiHeapSamplingStats zero; 640 memset(&zero, 0, sizeof(zero)); 641 return memcmp(&stats, &zero, sizeof(zero)) == 0; 642 } 643 644 JNIEXPORT jint JNICALL 645 Java_MyPackage_HeapMonitorStatSimpleTest_statsNull(JNIEnv *env, jclass cls) { 646 return stats_are_zero(); 647 } 648 649 JNIEXPORT void JNICALL 650 Java_MyPackage_HeapMonitorStatCorrectnessTest_disableSampling(JNIEnv *env, jclass cls) { 651 disable_sampling(); 652 } 653 654 JNIEXPORT void JNICALL 655 Java_MyPackage_HeapMonitorStatCorrectnessTest_enableSampling(JNIEnv *env, jclass cls, jint rate) { 656 enable_sampling_with_rate(rate); 657 } 658 659 JNIEXPORT jint JNICALL 660 Java_MyPackage_HeapMonitorStatCorrectnessTest_statsNull(JNIEnv *env, jclass cls) { 661 return stats_are_zero(); 662 } 663 664 JNIEXPORT jint JNICALL 665 Java_MyPackage_HeapMonitorStatCorrectnessTest_statsHaveSamples(JNIEnv *env, 666 jclass cls, 667 int expected, 668 int percent_error) { 669 jvmtiHeapSamplingStats stats; 670 check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats), 671 "Heap Sampling Statistics"); 672 673 double diff_ratio = (stats.sample_count - expected); 674 diff_ratio = (diff_ratio < 0) ? -diff_ratio : diff_ratio; 675 diff_ratio /= expected; 676 677 return diff_ratio * 100 > percent_error; 678 } 679 680 JNIEXPORT void JNICALL 681 Java_MyPackage_HeapMonitorStatRateTest_enableSampling(JNIEnv *env, jclass cls, jint rate) { 682 enable_sampling_with_rate(rate); 683 } 684 685 JNIEXPORT void JNICALL 686 Java_MyPackage_HeapMonitorStatRateTest_disableSampling(JNIEnv *env, jclass cls) { 687 disable_sampling(); 688 } 689 690 JNIEXPORT jdouble JNICALL 691 Java_MyPackage_HeapMonitorStatRateTest_getAverageRate(JNIEnv *env, jclass cls) { 692 jvmtiHeapSamplingStats stats; 693 check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats), 694 "Heap Sampling Statistics"); 695 (int) stats.sample_rate_accumulation, (int) stats.sample_rate_count); 696 697 return ((double) stats.sample_rate_accumulation) / stats.sample_rate_count; 698 } 699 700 #ifdef __cplusplus 701 } 702 #endif