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