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                                      jvmtiStackTrace *trace,
 182                                      ExpectedContentFrame *expected,
 183                                      int expected_count,
 184                                      int print_out_comparisons) {
 185   int i;
 186 
 187   if (expected_count > trace->frame_count) {
 188     return FALSE;
 189   }
 190 
 191   for (i = 0; i < expected_count; i++) {
 192     // Get basic information out of the trace.
 193     int bci = trace->frames[i].location;
 194     jmethodID methodid = trace->frames[i].method;
 195     char *name = NULL, *signature = NULL, *file_name = NULL;
 196 
 197     if (bci < 0) {
 198       return FALSE;
 199     }
 200 
 201     // Transform into usable information.
 202     int line_number = get_line_number(jvmti, methodid, bci);
 203     (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0);
 204 
 205     jclass declaring_class;
 206     if (JVMTI_ERROR_NONE !=
 207         (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) {
 208       return FALSE;
 209     }
 210 
 211     jvmtiError err = (*jvmti)->GetSourceFileName(jvmti, declaring_class,
 212                                                  &file_name);
 213     if (err != JVMTI_ERROR_NONE) {
 214       return FALSE;
 215     }
 216 
 217     // Compare now, none should be NULL.
 218     if (name == NULL) {
 219       return FALSE;
 220     }
 221 
 222     if (file_name == NULL) {
 223       return FALSE;
 224     }
 225 
 226     if (signature == NULL) {
 227       return FALSE;
 228     }
 229 
 230     if (print_out_comparisons) {
 231       fprintf(stderr, "Comparing:\n");
 232       fprintf(stderr, "\tNames: %s and %s\n", name, expected[i].name);
 233       fprintf(stderr, "\tSignatures: %s and %s\n", signature, expected[i].signature);
 234       fprintf(stderr, "\tFile name: %s and %s\n", file_name, expected[i].file_name);
 235       fprintf(stderr, "\tLines: %d and %d\n", line_number, expected[i].line_number);
 236       fprintf(stderr, "\tResult is %d\n",
 237               (strcmp(name, expected[i].name) ||
 238                strcmp(signature, expected[i].signature) ||
 239                strcmp(file_name, expected[i].file_name) ||
 240                line_number != expected[i].line_number));
 241     }
 242 
 243     if (strcmp(name, expected[i].name) ||
 244         strcmp(signature, expected[i].signature) ||
 245         strcmp(file_name, expected[i].file_name) ||
 246         line_number != expected[i].line_number) {
 247       return FALSE;
 248     }
 249   }
 250 
 251   return TRUE;
 252 }
 253 
 254 static jboolean compare_samples(JNIEnv* env, jvmtiStackTrace* traces,
 255                                 int trace_count,
 256                                 ExpectedContentFrame* expected_content,
 257                                 size_t size,
 258                                 int print_out_comparisons) {
 259   // We expect the code to record correctly the bci, retrieve the line
 260   // number, have the right method and the class name of the first frames.
 261   int i;
 262   for (i = 0; i < trace_count; i++) {
 263     jvmtiStackTrace *trace = traces + i;
 264     if (check_sample_content(env, trace, expected_content, size,
 265                              print_out_comparisons)) {
 266       // At least one frame matched what we were looking for.
 267       return TRUE;
 268     }
 269   }
 270 
 271   return FALSE;
 272 }
 273 
 274 static jboolean
 275 check_samples(JNIEnv* env,
 276               ExpectedContentFrame* expected,
 277               size_t size,
 278               jvmtiError (*const get_traces)(jvmtiEnv*, jvmtiStackTraces*),
 279               int print_out_comparisons) {
 280   jvmtiStackTraces traces;
 281   jvmtiError error = get_traces(jvmti, &traces);
 282 
 283   if (error != JVMTI_ERROR_NONE) {
 284     return FALSE;
 285   }
 286 
 287   int result = compare_samples(env, traces.stack_traces, traces.trace_count,
 288                                expected, size, print_out_comparisons);
 289   (*jvmti)->ReleaseTraces(jvmti, &traces);
 290   return result;
 291 }
 292 
 293 static jboolean frames_exist_live(JNIEnv* env,
 294                                   ExpectedContentFrame* expected,
 295                                   size_t size,
 296                                   int print_out_comparisons) {
 297   return check_samples(env, expected, size, (*jvmti)->GetLiveTraces,
 298                        print_out_comparisons);
 299 }
 300 
 301 static jboolean frames_exist_recent(JNIEnv* env,
 302                                     ExpectedContentFrame* expected,
 303                                     size_t size,
 304                                     int print_out_comparisons) {
 305   return check_samples(env, expected, size, (*jvmti)->GetGarbageTraces,
 306                        print_out_comparisons);
 307 }
 308 
 309 static jboolean frames_exist_frequent(JNIEnv* env,
 310                                       ExpectedContentFrame* expected,
 311                                       size_t size,
 312                                       int print_out_comparisons) {
 313   return check_samples(env, expected, size, (*jvmti)->GetFrequentGarbageTraces,
 314                        print_out_comparisons);
 315 }
 316 
 317 // Static native API for various tests.
 318 static void fill_native_frames(JNIEnv* env, jobjectArray frames,
 319                                ExpectedContentFrame* native_frames, size_t size) {
 320   size_t i;
 321   for(i = 0; i < size; i++) {
 322     jobject obj = (*env)->GetObjectArrayElement(env, frames, i);
 323     jclass frame_class = (*env)->GetObjectClass(env, obj);
 324     jfieldID line_number_field_id = (*env)->GetFieldID(env, frame_class,
 325                                                        "lineNumber", "I");
 326     int line_number = (*env)->GetIntField(env, obj, line_number_field_id);
 327 
 328     jfieldID string_id = (*env)->GetFieldID(env, frame_class, "method",
 329                                             "Ljava/lang/String;");
 330     jstring string_object = (jstring) (*env)->GetObjectField(env, obj,
 331                                                              string_id);
 332     const char* method = (*env)->GetStringUTFChars(env, string_object, 0);
 333 
 334     string_id = (*env)->GetFieldID(env, frame_class, "fileName",
 335                                    "Ljava/lang/String;");
 336     string_object = (jstring) (*env)->GetObjectField(env, obj, string_id);
 337     const char* file_name = (*env)->GetStringUTFChars(env, string_object, 0);
 338 
 339     string_id = (*env)->GetFieldID(env, frame_class, "signature",
 340                                    "Ljava/lang/String;");
 341     string_object = (jstring) (*env)->GetObjectField(env, obj, string_id);
 342     const char* signature= (*env)->GetStringUTFChars(env, string_object, 0);
 343 
 344     native_frames[i].name = method;
 345     native_frames[i].file_name = file_name;
 346     native_frames[i].signature = signature;
 347     native_frames[i].line_number = line_number;
 348   }
 349 }
 350 
 351 static jboolean check_and(JNIEnv *env, jobjectArray frames, int live,
 352                           int recent, int frequent, int print_out) {
 353   jobject loader = NULL;
 354 
 355   if (frames == NULL) {
 356     return FALSE;
 357   }
 358 
 359   // Start by transforming the frames into a C-friendly structure.
 360   jsize size = (*env)->GetArrayLength(env, frames);
 361   ExpectedContentFrame native_frames[size];
 362   fill_native_frames(env, frames, native_frames, size);
 363 
 364   if (jvmti == NULL) {
 365     throw_exc(env, "JVMTI client was not properly loaded!\n");
 366     return FALSE;
 367   }
 368 
 369   int result = TRUE;
 370 
 371   if (live) {
 372     result = frames_exist_live(env, native_frames, size, print_out);
 373   }
 374 
 375   if (recent) {
 376     result = result &&
 377         frames_exist_recent(env, native_frames, size, print_out);
 378   }
 379 
 380   if (frequent) {
 381     result = result &&
 382         frames_exist_frequent(env, native_frames, size, print_out);
 383   }
 384 
 385   return result;
 386 }
 387 
 388 static jboolean check_or(JNIEnv *env, jobjectArray frames, int live, int recent,
 389                          int frequent, int print_out) {
 390   jobject loader = NULL;
 391 
 392   if (frames == NULL) {
 393     return FALSE;
 394   }
 395 
 396   // Start by transforming the frames into a C-friendly structure.
 397   jsize size = (*env)->GetArrayLength(env, frames);
 398   ExpectedContentFrame native_frames[size];
 399   fill_native_frames(env, frames, native_frames, size);
 400 
 401   if (jvmti == NULL) {
 402     throw_exc(env, "JVMTI client was not properly loaded!\n");
 403     return FALSE;
 404   }
 405 
 406   jboolean result = FALSE;
 407 
 408   if (live) {
 409     result = frames_exist_live(env, native_frames, size, print_out);
 410   }
 411 
 412   if (recent) {
 413     result = result ||
 414         frames_exist_recent(env, native_frames, size, print_out);
 415   }
 416 
 417   if (frequent) {
 418     result = result ||
 419         frames_exist_frequent(env, native_frames, size, print_out);
 420   }
 421 
 422   return result;
 423 }
 424 
 425 static jboolean checkAll(JNIEnv *env, jobjectArray frames, int print_out) {
 426   return check_and(env, frames, 1, 1, 1, print_out);
 427 }
 428 
 429 static jboolean checkNone(JNIEnv *env, jobjectArray frames,
 430                           int print_out) {
 431   jobject loader = NULL;
 432 
 433   if (frames == NULL) {
 434     return FALSE;
 435   }
 436 
 437   // Start by transforming the frames into a C-friendly structure.
 438   jsize size = (*env)->GetArrayLength(env, frames);
 439   ExpectedContentFrame native_frames[size];
 440   fill_native_frames(env, frames, native_frames, size);
 441 
 442   if (jvmti == NULL) {
 443     throw_exc(env, "JVMTI client was not properly loaded!\n");
 444     return FALSE ;
 445   }
 446 
 447   if ((!frames_exist_live(env, native_frames, size, print_out)) &&
 448       (!frames_exist_recent(env, native_frames, size, print_out)) &&
 449       (!frames_exist_frequent(env, native_frames, size, print_out))) {
 450     return TRUE;
 451   }
 452   return FALSE;
 453 }
 454 
 455 JNIEXPORT void JNICALL
 456 Java_MyPackage_HeapMonitor_enableSampling(JNIEnv *env, jclass cls, int rate,
 457                                           int max_traces) {
 458   check_error((*jvmti)->StartHeapSampling(jvmti, rate, max_traces),
 459               "Start Heap Sampling");
 460 }
 461 
 462 JNIEXPORT void JNICALL
 463 Java_MyPackage_HeapMonitor_disableSampling(JNIEnv *env, jclass cls) {
 464   check_error((*jvmti)->StopHeapSampling(jvmti), "Stop Heap Sampling");
 465 }
 466 
 467 JNIEXPORT jboolean JNICALL
 468 Java_MyPackage_HeapMonitor_areSamplingStatisticsZero(JNIEnv *env, jclass cls) {
 469   jvmtiHeapSamplingStats stats;
 470   check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats),
 471               "Heap Sampling Statistics");
 472 
 473   jvmtiHeapSamplingStats zero;
 474   memset(&zero, 0, sizeof(zero));
 475   return memcmp(&stats, &zero, sizeof(zero)) == 0;
 476 }
 477 
 478 JNIEXPORT jboolean JNICALL
 479 Java_MyPackage_HeapMonitor_framesExistEverywhere(JNIEnv *env, jclass cls,
 480                                                  jobjectArray frames) {
 481   // We want the frames in each part.
 482   return checkAll(env, frames, PRINT_OUT);
 483 }
 484 
 485 JNIEXPORT jboolean JNICALL
 486 Java_MyPackage_HeapMonitor_framesExistNowhere(JNIEnv *env, jclass cls,
 487                                               jobjectArray frames) {
 488   // We want the frames in none of the parts.
 489   return checkNone(env, frames, PRINT_OUT);
 490 }
 491 
 492 JNIEXPORT jboolean JNICALL
 493 Java_MyPackage_HeapMonitorRecentTest_framesNotInLiveOrRecent(JNIEnv *env,
 494                                                              jclass cls,
 495                                                              jobjectArray frames) {
 496   return !check_or(env, frames, TRUE, TRUE, FALSE, PRINT_OUT);
 497 }
 498 
 499 JNIEXPORT jboolean JNICALL
 500 Java_MyPackage_HeapMonitorRecentTest_framesExistInLiveAndRecent(JNIEnv *env,
 501                                                                 jclass cls,
 502                                                                 jobjectArray frames) {
 503   return check_and(env, frames, TRUE, TRUE, FALSE, PRINT_OUT);
 504 }
 505 
 506 JNIEXPORT jboolean JNICALL
 507 Java_MyPackage_HeapMonitorFrequentTest_framesExistInFrequent(JNIEnv *env,
 508                                                              jclass cls,
 509                                                              jobjectArray frames) {
 510   return check_and(env, frames, FALSE, FALSE, TRUE, PRINT_OUT);
 511 }
 512 
 513 JNIEXPORT jboolean JNICALL
 514 Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env,
 515                                                                   jclass cls) {
 516   jvmtiCapabilities caps;
 517   memset(&caps, 0, sizeof(caps));
 518   caps.can_sample_heap= 1;
 519   if (check_error((*jvmti)->RelinquishCapabilities(jvmti, &caps),
 520                   "Add capabilities\n")){
 521     return FALSE;
 522   }
 523 
 524   if (check_capability_error((*jvmti)->StartHeapSampling(jvmti, 1<<19,
 525                                                          MAX_TRACES),
 526                              "Start Heap Sampling")) {
 527     return FALSE;
 528   }
 529 
 530   if (check_capability_error((*jvmti)->StopHeapSampling(jvmti),
 531                              "Stop Heap Sampling")) {
 532     return FALSE;
 533   }
 534 
 535   if (check_capability_error((*jvmti)->ReleaseTraces(jvmti, NULL),
 536                              "Release Traces")) {
 537     return FALSE;
 538   }
 539 
 540   if (check_capability_error((*jvmti)->GetHeapSamplingStats(jvmti, NULL),
 541                              "Get Heap Sampling Stats")) {
 542     return FALSE;
 543   }
 544 
 545   if (check_capability_error((*jvmti)->GetGarbageTraces(jvmti, NULL),
 546                              "Get Garbage Traces")) {
 547     return FALSE;
 548   }
 549 
 550   if (check_capability_error((*jvmti)->GetFrequentGarbageTraces(jvmti, NULL),
 551                              "Get Frequent Garbage Traces")) {
 552     return FALSE;
 553   }
 554 
 555   if (check_capability_error((*jvmti)->GetLiveTraces(jvmti, NULL),
 556                              "Get Live Traces")) {
 557     return FALSE;
 558   }
 559   return TRUE;
 560 }
 561 
 562 JNIEXPORT jboolean JNICALL
 563 Java_MyPackage_HeapMonitor_statsHaveExpectedNumberSamples(JNIEnv *env,
 564                                                           jclass cls,
 565                                                           int expected,
 566                                                           int percent_error) {
 567   jvmtiHeapSamplingStats stats;
 568   check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats),
 569               "Heap Sampling Statistics");
 570 
 571   fprintf(stderr, "Sample data count %ld, rate %ld, rate count %ld\n",
 572           stats.sample_count, stats.sample_rate_accumulation, stats.sample_rate_count);
 573   double diff_ratio = (stats.sample_count - expected);
 574   diff_ratio = (diff_ratio < 0) ? -diff_ratio : diff_ratio;
 575   diff_ratio /= expected;
 576 
 577   return diff_ratio * 100 < percent_error;
 578 }
 579 
 580 JNIEXPORT jdouble JNICALL
 581 Java_MyPackage_HeapMonitorStatRateTest_getAverageRate(JNIEnv *env, jclass cls) {
 582   jvmtiHeapSamplingStats stats;
 583   check_error((*jvmti)->GetHeapSamplingStats(jvmti, &stats),
 584               "Heap Sampling Statistics");
 585   return ((double) stats.sample_rate_accumulation) / stats.sample_rate_count;
 586 }
 587 
 588 JNIEXPORT jdouble JNICALL
 589 Java_MyPackage_HeapMonitorStackDepthTest_getAverageStackDepth(JNIEnv *env,
 590                                                               jclass cls) {
 591   jvmtiStackTraces traces;
 592   jvmtiError error = (*jvmti)->GetLiveTraces(jvmti, &traces);;
 593 
 594   if (error != JVMTI_ERROR_NONE) {
 595     return 0;
 596   }
 597 
 598   int trace_count = traces.trace_count;
 599 
 600   if (trace_count == 0) {
 601     return 0;
 602   }
 603 
 604   int i;
 605   jvmtiStackTrace* stack_traces = traces.stack_traces;
 606   double sum = 0;
 607   for (i = 0; i < trace_count; i++) {
 608     jvmtiStackTrace *stack_trace = stack_traces + i;
 609     sum += stack_trace->frame_count;
 610   }
 611 
 612   return sum / i;
 613 }
 614 
 615 typedef struct sThreadsFound {
 616   jint *threads;
 617   int num_threads;
 618 } ThreadsFound;
 619 
 620 static void find_threads_in_traces(jvmtiStackTraces* traces,
 621                                    ThreadsFound* thread_data) {
 622   int i;
 623   jvmtiStackTrace* stack_traces = traces->stack_traces;
 624   int trace_count = traces->trace_count;
 625 
 626   jint *threads = thread_data->threads;
 627   int num_threads = thread_data->num_threads;
 628 
 629   // We are looking for at last expected_num_threads different traces.
 630   for (i = 0; i < trace_count; i++) {
 631     jvmtiStackTrace *stack_trace = stack_traces + i;
 632     jlong thread_id = stack_trace->thread_id;
 633 
 634     // Check it is the right frame: only accept helper top framed traces.
 635     jmethodID methodid = stack_trace->frames[0].method;
 636     char *name = NULL, *signature = NULL, *file_name = NULL;
 637     (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0);
 638 
 639     if (strcmp(name, "helper")) {
 640       continue;
 641     }
 642 
 643     // Really not efficient look-up but it's for a test...
 644     int found = 0;
 645     int j;
 646     for (j = 0; j < num_threads; j++) {
 647       if (thread_id == threads[j]) {
 648         found = 1;
 649         break;
 650       }
 651     }
 652 
 653     if (!found) {
 654       threads[num_threads] = thread_id;
 655       num_threads++;
 656     }
 657   }
 658   thread_data->num_threads = num_threads;
 659 }
 660 
 661 JNIEXPORT jboolean JNICALL
 662 Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls,
 663                                                   jintArray threads) {
 664   jvmtiStackTraces traces;
 665   ThreadsFound thread_data;
 666   thread_data.threads = (*env)->GetIntArrayElements(env, threads, 0);
 667   thread_data.num_threads = 0;
 668 
 669   // Get live and garbage traces to ensure we capture all the threads that have
 670   // been sampled.
 671   if ((*jvmti)->GetLiveTraces(jvmti, &traces) != JVMTI_ERROR_NONE) {
 672     return FALSE;
 673   }
 674 
 675   find_threads_in_traces(&traces, &thread_data);
 676 
 677   if ((*jvmti)->ReleaseTraces(jvmti, &traces) != JVMTI_ERROR_NONE) {
 678     return FALSE;
 679   }
 680 
 681   if ((*jvmti)->GetGarbageTraces(jvmti, &traces) != JVMTI_ERROR_NONE) {
 682     return FALSE;
 683   }
 684 
 685   find_threads_in_traces(&traces, &thread_data);
 686 
 687   if ((*jvmti)->ReleaseTraces(jvmti, &traces) != JVMTI_ERROR_NONE) {
 688     return FALSE;
 689   }
 690 
 691   (*env)->ReleaseIntArrayElements(env, threads, thread_data.threads, 0);
 692   return TRUE;
 693 }
 694 
 695 JNIEXPORT void JNICALL
 696 Java_MyPackage_HeapMonitorCachedTest_getLiveTracesToForceGc(JNIEnv *env,
 697                                                             jclass cls) {
 698   jvmtiStackTraces live_traces;
 699   jvmtiError error = (*jvmti)->GetLiveTraces(jvmti, &live_traces);
 700 
 701   if (error != JVMTI_ERROR_NONE) {
 702     return;
 703   }
 704 
 705   (*jvmti)->ReleaseTraces(jvmti, &live_traces);
 706 }
 707 
 708 static jboolean compare_traces(jvmtiStackTraces* traces,
 709                                jvmtiStackTraces* other_traces,
 710                                int print_out_comparisons) {
 711   int trace_count = traces->trace_count;
 712   if (trace_count != other_traces->trace_count) {
 713     return FALSE;
 714   }
 715 
 716   int i;
 717   for (i = 0; i < trace_count; i++) {
 718     jvmtiStackTrace* trace = traces->stack_traces + i;
 719     jvmtiStackTrace* other_trace = other_traces->stack_traces + i;
 720 
 721     if (trace->frame_count != other_trace->frame_count) {
 722       return FALSE;
 723     }
 724 
 725     if (trace->size != other_trace->size) {
 726       return FALSE;
 727     }
 728 
 729     if (trace->thread_id != other_trace->thread_id) {
 730       return FALSE;
 731     }
 732 
 733     jvmtiFrameInfo* frames = trace->frames;
 734     jvmtiFrameInfo* other_frames = other_trace->frames;
 735     if (memcmp(frames, other_frames, sizeof(*frames) * trace->frame_count)) {
 736       return FALSE;
 737     }
 738   }
 739 
 740   return TRUE;
 741 }
 742 
 743 JNIEXPORT jboolean JNICALL
 744 Java_MyPackage_HeapMonitorCachedTest_cachedAndLiveAreSame(JNIEnv *env,
 745                                                           jclass cls) {
 746   // Get cached first, then get live (since live performs a GC).
 747   jvmtiStackTraces cached_traces;
 748   jvmtiError error = (*jvmti)->GetCachedTraces(jvmti, &cached_traces);
 749 
 750   if (error != JVMTI_ERROR_NONE) {
 751     return FALSE;
 752   }
 753 
 754   jvmtiStackTraces live_traces;
 755   error = (*jvmti)->GetLiveTraces(jvmti, &live_traces);
 756 
 757   if (error != JVMTI_ERROR_NONE) {
 758     return FALSE;
 759   }
 760 
 761   int result = compare_traces(&cached_traces, &live_traces, PRINT_OUT);
 762 
 763   (*jvmti)->ReleaseTraces(jvmti, &cached_traces);
 764   (*jvmti)->ReleaseTraces(jvmti, &live_traces);
 765   return result;
 766 }
 767 
 768 static long hash(long hash_code, long value) {
 769   return hash_code * 31 + value;
 770 }
 771 
 772 static long get_hash_code(jvmtiStackTraces* traces) {
 773   int trace_count = traces->trace_count;
 774   int hash_code = 17;
 775 
 776   int i;
 777   hash_code = hash(hash_code, trace_count);
 778   for (i = 0; i < trace_count; i++) {
 779     jvmtiStackTrace* trace = traces->stack_traces + i;
 780     hash_code = hash(hash_code, trace->frame_count);
 781     hash_code = hash(hash_code, trace->size);
 782     hash_code = hash(hash_code, trace->thread_id);
 783 
 784     int j;
 785     int frame_count = trace->frame_count;
 786     jvmtiFrameInfo* frames = trace->frames;
 787     hash_code = hash(hash_code, frame_count);
 788     for (j = 0; j < frame_count; j++) {
 789       hash_code = hash(hash_code, (long) frames[i].method);
 790       hash_code = hash(hash_code, frames[i].location);
 791     }
 792   }
 793 
 794   return TRUE;
 795 }
 796 
 797 JNIEXPORT jlong JNICALL
 798 Java_MyPackage_HeapMonitorCachedTest_getCachedHashCode(JNIEnv *env,
 799                                                        jclass cls) {
 800   // Get cached first, then get live.
 801   jvmtiStackTraces cached_traces;
 802   jvmtiError error = (*jvmti)->GetCachedTraces(jvmti, &cached_traces);
 803 
 804   if (error != JVMTI_ERROR_NONE) {
 805     return 0;
 806   }
 807 
 808   long hash_code = get_hash_code(&cached_traces);
 809   (*jvmti)->ReleaseTraces(jvmti, &cached_traces);
 810 
 811   return hash_code;
 812 }
 813 
 814 JNIEXPORT jboolean JNICALL
 815 Java_MyPackage_HeapMonitorTest_framesAreNotLive(JNIEnv *env,
 816                                                 jclass cls,
 817                                                 jobjectArray frames) {
 818   return !check_and(env, frames, FALSE, FALSE, TRUE, PRINT_OUT);
 819 }
 820 
 821 #ifdef __cplusplus
 822 }
 823 #endif