1 /*
   2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
   3  * Copyright (c) 2018, Google and/or its affiliates. All rights reserved.
   4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5  *
   6  * This code is free software; you can redistribute it and/or modify it
   7  * under the terms of the GNU General Public License version 2 only, as
   8  * published by the Free Software Foundation.
   9  *
  10  * This code is distributed in the hope that it will be useful, but WITHOUT
  11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13  * version 2 for more details (a copy is included in the LICENSE file that
  14  * accompanied this code).
  15  *
  16  * You should have received a copy of the GNU General Public License version
  17  * 2 along with this work; if not, write to the Free Software Foundation,
  18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  19  *
  20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  21  * or visit www.oracle.com if you need additional information or have any
  22  * questions.
  23  */
  24 
  25 #include <assert.h>
  26 #include <stdio.h>
  27 #include <stdlib.h>
  28 #include <string.h>
  29 #include "jvmti.h"
  30 
  31 #ifdef __cplusplus
  32 extern "C" {
  33 #endif
  34 
  35 #ifndef JNI_ENV_ARG
  36 
  37 #ifdef __cplusplus
  38 #define JNI_ENV_ARG(x)
  39 #define JNI_ENV_ARG2(x, y) y
  40 #define JNI_ENV_ARG3(x, y, z) y, z
  41 #define JNI_ENV_ARG4(x, y, z, w) y, z, w
  42 #define JNI_ENV_PTR(x) x
  43 #else
  44 #define JNI_ENV_ARG(x) x
  45 #define JNI_ENV_ARG2(x, y) x, y
  46 #define JNI_ENV_ARG3(x, y, z) x, y, z
  47 #define JNI_ENV_ARG4(x, y, z, w) x, y, z, w
  48 #define JNI_ENV_PTR(x) (*x)
  49 #endif
  50 
  51 #endif
  52 
  53 #define TRUE 1
  54 #define FALSE 0
  55 #define PRINT_OUT 0
  56 
  57 static jvmtiEnv *jvmti = NULL;
  58 static jvmtiEnv *second_jvmti = NULL;
  59 
  60 typedef struct _ObjectTrace{
  61   jweak object;
  62   jlong size;
  63   jvmtiFrameInfo* frames;
  64   size_t frame_count;
  65   jthread thread;
  66 } ObjectTrace;
  67 
  68 typedef struct _EventStorage {
  69   int live_object_additions;
  70   int live_object_size;
  71   int live_object_count;
  72   ObjectTrace** live_objects;
  73 
  74   int garbage_history_size;
  75   int garbage_history_index;
  76   ObjectTrace** garbage_collected_objects;
  77 
  78   // Two separate monitors to separate storage data race and the compaction field
  79   // data race.
  80   jrawMonitorID storage_monitor;
  81 
  82   int compaction_required;
  83   jrawMonitorID compaction_monitor;
  84 } EventStorage;
  85 
  86 typedef struct _ExpectedContentFrame {
  87   const char *name;
  88   const char *signature;
  89   const char *file_name;
  90   int line_number;
  91 } ExpectedContentFrame;
  92 
  93 static
  94 void event_storage_lock(EventStorage* storage) {
  95   (*jvmti)->RawMonitorEnter(jvmti, storage->storage_monitor);
  96 }
  97 
  98 static
  99 void event_storage_unlock(EventStorage* storage) {
 100   (*jvmti)->RawMonitorExit(jvmti, storage->storage_monitor);
 101 }
 102 
 103 static
 104 void event_storage_lock_compaction(EventStorage* storage) {
 105   (*jvmti)->RawMonitorEnter(jvmti, storage->compaction_monitor);
 106 }
 107 
 108 static
 109 void event_storage_unlock_compaction(EventStorage* storage) {
 110   (*jvmti)->RawMonitorExit(jvmti, storage->compaction_monitor);
 111 }
 112 
 113 // Given a method and a location, this method gets the line number.
 114 static
 115 jint get_line_number(jvmtiEnv* jvmti, jmethodID method,
 116                      jlocation location) {
 117   // Read the line number table.
 118   jvmtiLineNumberEntry *table_ptr = 0;
 119   jint line_number_table_entries;
 120   int l;
 121   jlocation last_location;
 122   int jvmti_error = (*jvmti)->GetLineNumberTable(jvmti, method,
 123                                                  &line_number_table_entries,
 124                                                  &table_ptr);
 125 
 126   if (JVMTI_ERROR_NONE != jvmti_error) {
 127     return -1;
 128   }
 129   if (line_number_table_entries <= 0) {
 130     return -1;
 131   }
 132   if (line_number_table_entries == 1) {
 133     return table_ptr[0].line_number;
 134   }
 135 
 136   // Go through all the line numbers...
 137   last_location = table_ptr[0].start_location;
 138   for (l = 1; l < line_number_table_entries; l++) {
 139     // ... and if you see one that is in the right place for your
 140     // location, you've found the line number!
 141     if ((location < table_ptr[l].start_location) &&
 142         (location >= last_location)) {
 143       return table_ptr[l - 1].line_number;
 144     }
 145     last_location = table_ptr[l].start_location;
 146   }
 147 
 148   if (location >= last_location) {
 149     return table_ptr[line_number_table_entries - 1].line_number;
 150   } else {
 151     return -1;
 152   }
 153 }
 154 
 155 static void print_out_frames(JNIEnv* env, ObjectTrace* trace) {
 156   jvmtiFrameInfo* frames = trace->frames;
 157   size_t i;
 158   for (i = 0; i < trace->frame_count; i++) {
 159     // Get basic information out of the trace.
 160     jlocation bci = frames[i].location;
 161     jmethodID methodid = frames[i].method;
 162     char *name = NULL, *signature = NULL, *file_name = NULL;
 163     jclass declaring_class;
 164     int line_number;
 165     jvmtiError err;
 166 
 167     if (bci < 0) {
 168       fprintf(stderr, "\tNative frame\n");
 169       continue;
 170     }
 171 
 172     // Transform into usable information.
 173     line_number = get_line_number(jvmti, methodid, bci);
 174     if (JVMTI_ERROR_NONE !=
 175         (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0)) {
 176       fprintf(stderr, "\tUnknown method name\n");
 177       continue;
 178     }
 179 
 180     if (JVMTI_ERROR_NONE !=
 181         (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) {
 182       fprintf(stderr, "\tUnknown class\n");
 183       continue;
 184     }
 185 
 186     err = (*jvmti)->GetSourceFileName(jvmti, declaring_class,
 187                                       &file_name);
 188     if (err != JVMTI_ERROR_NONE) {
 189       fprintf(stderr, "\tUnknown file\n");
 190       continue;
 191     }
 192 
 193     // Compare now, none should be NULL.
 194     if (name == NULL) {
 195       fprintf(stderr, "\tUnknown name\n");
 196       continue;
 197     }
 198 
 199     if (file_name == NULL) {
 200       fprintf(stderr, "\tUnknown file\n");
 201       continue;
 202     }
 203 
 204     if (signature == NULL) {
 205       fprintf(stderr, "\tUnknown signature\n");
 206       continue;
 207     }
 208 
 209     fprintf(stderr, "\t%s%s (%s: %d)\n",
 210             name, signature, file_name, line_number);
 211   }
 212 }
 213 
 214 static jboolean check_sample_content(JNIEnv* env,
 215                                      ObjectTrace* trace,
 216                                      ExpectedContentFrame *expected,
 217                                      size_t expected_count,
 218                                      jboolean check_lines,
 219                                      int print_out_comparisons) {
 220   jvmtiFrameInfo* frames;
 221   size_t i;
 222 
 223   if (expected_count > trace->frame_count) {
 224     return FALSE;
 225   }
 226 
 227   frames = trace->frames;
 228   for (i = 0; i < expected_count; i++) {
 229     // Get basic information out of the trace.
 230     jlocation bci = frames[i].location;
 231     jmethodID methodid = frames[i].method;
 232     char *name = NULL, *signature = NULL, *file_name = NULL;
 233     jclass declaring_class;
 234     int line_number;
 235     jboolean differ;
 236     jvmtiError err;
 237 
 238     if (bci < 0 && expected[i].line_number != -1) {
 239       return FALSE;
 240     }
 241 
 242     // Transform into usable information.
 243     line_number = get_line_number(jvmti, methodid, bci);
 244     (*jvmti)->GetMethodName(jvmti, methodid, &name, &signature, 0);
 245 
 246     if (JVMTI_ERROR_NONE !=
 247         (*jvmti)->GetMethodDeclaringClass(jvmti, methodid, &declaring_class)) {
 248       return FALSE;
 249     }
 250 
 251     err = (*jvmti)->GetSourceFileName(jvmti, declaring_class,
 252                                       &file_name);
 253     if (err != JVMTI_ERROR_NONE) {
 254       return FALSE;
 255     }
 256 
 257     // Compare now, none should be NULL.
 258     if (name == NULL) {
 259       return FALSE;
 260     }
 261 
 262     if (file_name == NULL) {
 263       return FALSE;
 264     }
 265 
 266     if (signature == NULL) {
 267       return FALSE;
 268     }
 269 
 270     differ = (strcmp(name, expected[i].name) ||
 271               strcmp(signature, expected[i].signature) ||
 272               strcmp(file_name, expected[i].file_name) ||
 273               (check_lines && line_number != expected[i].line_number));
 274 
 275     if (print_out_comparisons) {
 276       fprintf(stderr, "\tComparing: (check_lines: %d)\n", check_lines);
 277       fprintf(stderr, "\t\tNames: %s and %s\n", name, expected[i].name);
 278       fprintf(stderr, "\t\tSignatures: %s and %s\n", signature, expected[i].signature);
 279       fprintf(stderr, "\t\tFile name: %s and %s\n", file_name, expected[i].file_name);
 280       fprintf(stderr, "\t\tLines: %d and %d\n", line_number, expected[i].line_number);
 281       fprintf(stderr, "\t\tResult is %d\n", differ);
 282     }
 283 
 284     if (differ) {
 285       return FALSE;
 286     }
 287   }
 288 
 289   return TRUE;
 290 }
 291 
 292 // Static native API for various tests.
 293 static int fill_native_frames(JNIEnv* env, jobjectArray frames,
 294                               ExpectedContentFrame* native_frames, size_t size) {
 295   size_t i;
 296   for (i = 0; i < size; i++) {
 297     jclass frame_class = NULL;
 298     jfieldID line_number_field_id = 0;
 299     int line_number = 0;
 300     jfieldID string_id = 0;
 301     jstring string_object = NULL;
 302     const char* method = NULL;
 303     const char* file_name = NULL;
 304     const char* signature = NULL;
 305 
 306     jobject obj = JNI_ENV_PTR(env)->GetObjectArrayElement(
 307         JNI_ENV_ARG3(env, frames, (jsize) i));
 308 
 309     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 310       fprintf(stderr, "fill_native_frames: Exception in jni GetObjectArrayElement\n");
 311       return -1;
 312     }
 313 
 314     frame_class = JNI_ENV_PTR(env)->GetObjectClass(JNI_ENV_ARG2(env, obj));
 315 
 316     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 317       fprintf(stderr, "fill_native_frames: Exception in jni GetObjectClass\n");
 318       return -1;
 319     }
 320 
 321     line_number_field_id =
 322         JNI_ENV_PTR(env)->GetFieldID(JNI_ENV_ARG4(env, frame_class, "lineNumber", "I"));
 323 
 324     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 325       fprintf(stderr, "fill_native_frames: Exception in jni GetFieldID\n");
 326       return -1;
 327     }
 328 
 329     line_number = JNI_ENV_PTR(env)->GetIntField(JNI_ENV_ARG3(env, obj, line_number_field_id));
 330 
 331     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 332       fprintf(stderr, "fill_native_frames: Exception in jni GetIntField\n");
 333       return -1;
 334     }
 335 
 336     string_id = JNI_ENV_PTR(env)->GetFieldID(
 337         JNI_ENV_ARG4(env, frame_class, "method", "Ljava/lang/String;"));
 338 
 339     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 340       fprintf(stderr, "fill_native_frames: Exception in jni GetFieldID\n");
 341       return -1;
 342     }
 343 
 344     string_object = (jstring) JNI_ENV_PTR(env)->GetObjectField(
 345         JNI_ENV_ARG3(env, obj, string_id));
 346 
 347     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 348       fprintf(stderr, "fill_native_frames: Exception in jni GetObjectField\n");
 349       return -1;
 350     }
 351 
 352     method = JNI_ENV_PTR(env)->GetStringUTFChars(JNI_ENV_ARG3(env, string_object, 0));
 353 
 354     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 355       fprintf(stderr, "Exception in jni GetStringUTFChars\n");
 356       return -1;
 357     }
 358 
 359     string_id = JNI_ENV_PTR(env)->GetFieldID(
 360         JNI_ENV_ARG4(env, frame_class, "fileName", "Ljava/lang/String;"));
 361 
 362     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 363       fprintf(stderr, "Exception in jni GetFieldID\n");
 364       return -1;
 365     }
 366 
 367     string_object =
 368         (jstring) (JNI_ENV_PTR(env)->GetObjectField(
 369             JNI_ENV_ARG3(env, obj, string_id)));
 370 
 371     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 372       fprintf(stderr, "fill_native_frames: Exception in second jni GetObjectField\n");
 373       return -1;
 374     }
 375 
 376     file_name = JNI_ENV_PTR(env)->GetStringUTFChars(
 377         JNI_ENV_ARG3(env, string_object, 0));
 378 
 379     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 380       fprintf(stderr, "fill_native_frames: Exception in jni GetStringUTFChars\n");
 381       return -1;
 382     }
 383 
 384     string_id = JNI_ENV_PTR(env)->GetFieldID(
 385         JNI_ENV_ARG4(env, frame_class, "signature", "Ljava/lang/String;"));
 386 
 387     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 388       fprintf(stderr, "fill_native_frames: Exception in second jni GetFieldID\n");
 389       return -1;
 390     }
 391 
 392     string_object =
 393         (jstring) (JNI_ENV_PTR(env)->GetObjectField(
 394             JNI_ENV_ARG3(env, obj, string_id)));
 395 
 396     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 397       fprintf(stderr, "fill_native_frames: Exception in third jni GetObjectField\n");
 398       return -1;
 399     }
 400 
 401     signature = JNI_ENV_PTR(env)->GetStringUTFChars(
 402         JNI_ENV_ARG3(env, string_object, 0));
 403 
 404     if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 405       fprintf(stderr, "fill_native_frames: Exception in jni GetStringUTFChars\n");
 406       return -1;
 407     }
 408 
 409     native_frames[i].name = method;
 410     native_frames[i].file_name = file_name;
 411     native_frames[i].signature = signature;
 412     native_frames[i].line_number = line_number;
 413   }
 414 
 415   return 0;
 416 }
 417 
 418 // Internal storage system implementation.
 419 static EventStorage global_event_storage;
 420 static EventStorage second_global_event_storage;
 421 
 422 static void event_storage_set_compaction_required(EventStorage* storage) {
 423   event_storage_lock_compaction(storage);
 424   storage->compaction_required = 1;
 425   event_storage_unlock_compaction(storage);
 426 }
 427 
 428 static int event_storage_get_compaction_required(EventStorage* storage) {
 429   int result;
 430   event_storage_lock_compaction(storage);
 431   result = storage->compaction_required;
 432   event_storage_unlock_compaction(storage);
 433   return result;
 434 }
 435 
 436 static void event_storage_set_garbage_history(EventStorage* storage, int value) {
 437   size_t size;
 438   event_storage_lock(storage);
 439   global_event_storage.garbage_history_size = value;
 440   free(global_event_storage.garbage_collected_objects);
 441   size = sizeof(*global_event_storage.garbage_collected_objects) * value;
 442   global_event_storage.garbage_collected_objects = malloc(size);
 443   memset(global_event_storage.garbage_collected_objects, 0, size);
 444   event_storage_unlock(storage);
 445 }
 446 
 447 // No mutex here, it is handled by the caller.
 448 static void event_storage_add_garbage_collected_object(EventStorage* storage,
 449                                                        ObjectTrace* object) {
 450   int idx = storage->garbage_history_index;
 451   ObjectTrace* old_object = storage->garbage_collected_objects[idx];
 452   if (old_object != NULL) {
 453     free(old_object->frames);
 454     free(storage->garbage_collected_objects[idx]);
 455   }
 456 
 457   storage->garbage_collected_objects[idx] = object;
 458   storage->garbage_history_index = (idx + 1) % storage->garbage_history_size;
 459 }
 460 
 461 static int event_storage_get_count(EventStorage* storage) {
 462   int result;
 463   event_storage_lock(storage);
 464   result = storage->live_object_count;
 465   event_storage_unlock(storage);
 466   return result;
 467 }
 468 
 469 static double event_storage_get_average_size(EventStorage* storage) {
 470   double accumulation = 0;
 471   int max_size;
 472   int i;
 473 
 474   event_storage_lock(storage);
 475   max_size = storage->live_object_count;
 476 
 477   for (i = 0; i < max_size; i++) {
 478     accumulation += storage->live_objects[i]->size;
 479   }
 480 
 481   event_storage_unlock(storage);
 482   return accumulation / max_size;
 483 }
 484 
 485 static jboolean event_storage_contains(JNIEnv* env,
 486                                        EventStorage* storage,
 487                                        ExpectedContentFrame* frames,
 488                                        size_t size,
 489                                        jboolean check_lines) {
 490   int i;
 491   event_storage_lock(storage);
 492   fprintf(stderr, "Checking storage count %d\n", storage->live_object_count);
 493   for (i = 0; i < storage->live_object_count; i++) {
 494     ObjectTrace* trace = storage->live_objects[i];
 495 
 496     if (check_sample_content(env, trace, frames, size, check_lines, PRINT_OUT)) {
 497       event_storage_unlock(storage);
 498       return TRUE;
 499     }
 500   }
 501   event_storage_unlock(storage);
 502   return FALSE;
 503 }
 504 
 505 static jboolean event_storage_garbage_contains(JNIEnv* env,
 506                                                EventStorage* storage,
 507                                                ExpectedContentFrame* frames,
 508                                                size_t size,
 509                                                jboolean check_lines) {
 510   int i;
 511   event_storage_lock(storage);
 512   fprintf(stderr, "Checking garbage storage count %d\n",
 513           storage->garbage_history_size);
 514   for (i = 0; i < storage->garbage_history_size; i++) {
 515     ObjectTrace* trace = storage->garbage_collected_objects[i];
 516 
 517     if (trace == NULL) {
 518       continue;
 519     }
 520 
 521     if (check_sample_content(env, trace, frames, size, check_lines, PRINT_OUT)) {
 522       event_storage_unlock(storage);
 523       return TRUE;
 524     }
 525   }
 526   event_storage_unlock(storage);
 527   return FALSE;
 528 }
 529 
 530 // No mutex here, handled by the caller.
 531 static void event_storage_augment_storage(EventStorage* storage) {
 532   int new_max = (storage->live_object_size * 2) + 1;
 533   ObjectTrace** new_objects = malloc(new_max * sizeof(*new_objects));
 534 
 535   int current_count = storage->live_object_count;
 536   memcpy(new_objects, storage->live_objects, current_count * sizeof(*new_objects));
 537   free(storage->live_objects);
 538   storage->live_objects = new_objects;
 539   storage->live_object_size = new_max;
 540 }
 541 
 542 static void event_storage_add(EventStorage* storage,
 543                               JNIEnv* jni,
 544                               jthread thread,
 545                               jobject object,
 546                               jclass klass,
 547                               jlong size) {
 548   jvmtiFrameInfo frames[64];
 549   jint count;
 550   jvmtiError err;
 551 
 552   err = (*jvmti)->GetStackTrace(jvmti, thread, 0, 64, frames, &count);
 553   if (err == JVMTI_ERROR_NONE && count >= 1) {
 554     ObjectTrace* live_object;
 555     jvmtiFrameInfo* allocated_frames = (jvmtiFrameInfo*) malloc(count * sizeof(*allocated_frames));
 556     memcpy(allocated_frames, frames, count * sizeof(*allocated_frames));
 557 
 558     live_object = (ObjectTrace*) malloc(sizeof(*live_object));
 559     live_object->frames = allocated_frames;
 560     live_object->frame_count = count;
 561     live_object->size = size;
 562     live_object->thread = thread;
 563     live_object->object = (*jni)->NewWeakGlobalRef(jni, object);
 564 
 565     if (JNI_ENV_PTR(jni)->ExceptionOccurred(JNI_ENV_ARG(jni))) {
 566       JNI_ENV_PTR(jni)->FatalError(
 567           JNI_ENV_ARG2(jni, "Error in event_storage_add: Exception in jni NewWeakGlobalRef"));
 568     }
 569 
 570     // Only now lock and get things done quickly.
 571     event_storage_lock(storage);
 572 
 573     storage->live_object_additions++;
 574 
 575     if (storage->live_object_count >= storage->live_object_size) {
 576       event_storage_augment_storage(storage);
 577     }
 578     assert(storage->live_object_count < storage->live_object_size);
 579 
 580     if (PRINT_OUT) {
 581       fprintf(stderr, "Adding trace for thread %p, frame_count %d, storage %p\n",
 582               thread, count, storage);
 583       print_out_frames(jni, live_object);
 584     }
 585     storage->live_objects[storage->live_object_count] = live_object;
 586     storage->live_object_count++;
 587 
 588     event_storage_unlock(storage);
 589   }
 590 }
 591 
 592 static void event_storage_compact(EventStorage* storage, JNIEnv* jni) {
 593   int max, i, dest;
 594   ObjectTrace** live_objects;
 595 
 596   event_storage_lock_compaction(storage);
 597   storage->compaction_required = 0;
 598   event_storage_unlock_compaction(storage);
 599 
 600   event_storage_lock(storage);
 601 
 602   max = storage->live_object_count;
 603   live_objects = storage->live_objects;
 604 
 605   for (i = 0, dest = 0; i < max; i++) {
 606     ObjectTrace* live_object = live_objects[i];
 607     jweak object = live_object->object;
 608 
 609     if (!(*jni)->IsSameObject(jni, object, NULL)) {
 610       if (dest != i) {
 611         live_objects[dest] = live_object;
 612         dest++;
 613       }
 614     } else {
 615       (*jni)->DeleteWeakGlobalRef(jni, object);
 616       live_object->object = NULL;
 617 
 618       event_storage_add_garbage_collected_object(storage, live_object);
 619     }
 620   }
 621 
 622   storage->live_object_count = dest;
 623   event_storage_unlock(storage);
 624 }
 625 
 626 static void event_storage_free_objects(ObjectTrace** array, int max) {
 627   int i;
 628   for (i = 0; i < max; i++) {
 629     free(array[i]), array[i] = NULL;
 630   }
 631 }
 632 
 633 static void event_storage_reset(EventStorage* storage) {
 634   event_storage_lock(storage);
 635 
 636   // Reset everything except the mutex and the garbage collection.
 637   event_storage_free_objects(storage->live_objects,
 638                              storage->live_object_count);
 639   storage->live_object_additions = 0;
 640   storage->live_object_size = 0;
 641   storage->live_object_count = 0;
 642   free(storage->live_objects), storage->live_objects = NULL;
 643 
 644   event_storage_free_objects(storage->garbage_collected_objects,
 645                              storage->garbage_history_size);
 646 
 647   storage->compaction_required = 0;
 648   storage->garbage_history_index = 0;
 649 
 650   event_storage_unlock(storage);
 651 }
 652 
 653 static int event_storage_number_additions(EventStorage* storage) {
 654   int result;
 655   event_storage_lock(storage);
 656   result = storage->live_object_additions;
 657   event_storage_unlock(storage);
 658   return result;
 659 }
 660 
 661 // Start of the JVMTI agent code.
 662 static const char *EXC_CNAME = "java/lang/Exception";
 663 
 664 static int check_error(jvmtiError err, const char *s) {
 665   if (err != JVMTI_ERROR_NONE) {
 666     printf("  ## %s error: %d\n", s, err);
 667     return 1;
 668   }
 669   return 0;
 670 }
 671 
 672 static int check_capability_error(jvmtiError err, const char *s) {
 673   if (err != JVMTI_ERROR_NONE) {
 674     if (err == JVMTI_ERROR_MUST_POSSESS_CAPABILITY) {
 675       return 0;
 676     }
 677     fprintf(stderr, "  ## %s error: %d\n", s, err);
 678   }
 679   return 1;
 680 }
 681 
 682 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
 683 
 684 JNIEXPORT
 685 jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
 686   return Agent_Initialize(jvm, options, reserved);
 687 }
 688 
 689 JNIEXPORT
 690 jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
 691   return Agent_Initialize(jvm, options, reserved);
 692 }
 693 
 694 JNIEXPORT
 695 jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
 696   return JNI_VERSION_1_8;
 697 }
 698 
 699 #define MAX_THREADS 500
 700 
 701 typedef struct ThreadStats {
 702   int number_threads;
 703   int counts[MAX_THREADS];
 704   int not_helper_counts[MAX_THREADS];
 705   int index[MAX_THREADS];
 706   jthread threads[MAX_THREADS];
 707 
 708   int method_resolution_problem;
 709 } ThreadStats;
 710 
 711 static ThreadStats thread_stats;
 712 
 713 static void add_thread_count(jthread thread, int lock, int helper) {
 714   int i;
 715   jvmtiThreadInfo info;
 716   const char* name;
 717   char* end;
 718   int idx;
 719   int err;
 720 
 721   if (lock) {
 722     event_storage_lock(&global_event_storage);
 723   }
 724 
 725   for (i = 0; i < thread_stats.number_threads; i++) {
 726     if (thread_stats.threads[i] == thread) {
 727       if (helper) {
 728         thread_stats.counts[i]++;
 729       } else {
 730         thread_stats.not_helper_counts[i]++;
 731       }
 732 
 733       if (lock) {
 734         event_storage_unlock(&global_event_storage);
 735       }
 736       return;
 737     }
 738   }
 739 
 740   thread_stats.threads[thread_stats.number_threads] = thread;
 741 
 742   err = (*jvmti)->GetThreadInfo(jvmti, thread, &info);
 743   if (err != JVMTI_ERROR_NONE) {
 744     if (lock) {
 745       event_storage_unlock(&global_event_storage);
 746     }
 747 
 748     // Just to have it accounted as an error...
 749     info.name = "Allocator99";
 750   }
 751 
 752   if (!strstr(info.name, "Allocator")) {
 753     if (lock) {
 754       event_storage_unlock(&global_event_storage);
 755     }
 756 
 757     // Just to have it accounted as an error...
 758     info.name = "Allocator98";
 759   }
 760 
 761   name = info.name + 9;
 762   end = NULL;
 763   idx = strtol(name, &end, 0);
 764 
 765   if (*end == '\0') {
 766     if (helper) {
 767       thread_stats.counts[thread_stats.number_threads]++;
 768     } else {
 769       thread_stats.not_helper_counts[thread_stats.number_threads]++;
 770     }
 771 
 772     thread_stats.index[thread_stats.number_threads] = idx;
 773     thread_stats.number_threads++;
 774   } else {
 775     fprintf(stderr, "Problem with thread name...: %p %s\n", thread, name);
 776   }
 777 
 778   if (PRINT_OUT) {
 779     fprintf(stderr, "Added %s - %p - %d - lock: %d\n", info.name, thread, idx, lock);
 780   }
 781 
 782   if (lock) {
 783     event_storage_unlock(&global_event_storage);
 784   }
 785 }
 786 
 787 static void print_thread_stats() {
 788   int i;
 789   event_storage_lock(&global_event_storage);
 790   fprintf(stderr, "Method resolution problem: %d\n", thread_stats.method_resolution_problem);
 791   fprintf(stderr, "Thread count:\n");
 792   for (i = 0; i < thread_stats.number_threads; i++) {
 793     fprintf(stderr, "\t%p: %d: %d - %d\n", thread_stats.threads[i],
 794             thread_stats.index[i],
 795             thread_stats.counts[i],
 796             thread_stats.not_helper_counts[i]);
 797   }
 798   event_storage_unlock(&global_event_storage);
 799 }
 800 
 801 JNIEXPORT
 802 void JNICALL SampledObjectAlloc(jvmtiEnv *jvmti_env,
 803                                 JNIEnv* jni_env,
 804                                 jthread thread,
 805                                 jobject object,
 806                                 jclass object_klass,
 807                                 jlong size) {
 808   add_thread_count(thread, 1, 1);
 809 
 810   if (event_storage_get_compaction_required(&global_event_storage)) {
 811     event_storage_compact(&global_event_storage, jni_env);
 812   }
 813 
 814   event_storage_add(&global_event_storage, jni_env, thread, object,
 815                     object_klass, size);
 816 }
 817 
 818 JNIEXPORT
 819 void JNICALL VMObjectAlloc(jvmtiEnv *jvmti_env,
 820                            JNIEnv* jni_env,
 821                            jthread thread,
 822                            jobject object,
 823                            jclass object_klass,
 824                            jlong size) {
 825   event_storage_add(&second_global_event_storage, jni_env, thread, object,
 826                     object_klass, size);
 827 }
 828 
 829 JNIEXPORT
 830 void JNICALL GarbageCollectionFinish(jvmtiEnv *jvmti_env) {
 831   event_storage_set_compaction_required(&global_event_storage);
 832 }
 833 
 834 static int enable_notifications() {
 835   if (check_error((*jvmti)->SetEventNotificationMode(
 836       jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL),
 837                      "Set event notifications")) {
 838     return 1;
 839   }
 840 
 841   return check_error((*jvmti)->SetEventNotificationMode(
 842       jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL),
 843                      "Set event notifications");
 844 }
 845 
 846 static int enable_notifications_for_two_threads(jthread first, jthread second) {
 847   if (check_error((*jvmti)->SetEventNotificationMode(
 848       jvmti, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL),
 849                            "Set event notifications")) {
 850     return 0;
 851   }
 852 
 853   if (check_error((*jvmti)->SetEventNotificationMode(
 854       jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, first),
 855                   "Set event notifications")) {
 856     return 0;
 857   }
 858 
 859   // Second thread should fail.
 860   if (check_error((*jvmti)->SetEventNotificationMode(
 861       jvmti, JVMTI_ENABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, second),
 862                   "Set event notifications")) {
 863     return 0;
 864   }
 865 
 866   return 1;
 867 }
 868 
 869 static
 870 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
 871   jint res;
 872   jvmtiEventCallbacks callbacks;
 873   jvmtiCapabilities caps;
 874 
 875   res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG3(jvm, (void **) &jvmti,
 876                                  JVMTI_VERSION_9));
 877   if (res != JNI_OK || jvmti == NULL) {
 878     fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n");
 879     return JNI_ERR;
 880   }
 881 
 882   // Get second jvmti environment.
 883   res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG3(jvm, (void **) &second_jvmti,
 884                                  JVMTI_VERSION_9));
 885   if (res != JNI_OK || second_jvmti == NULL) {
 886     fprintf(stderr, "Error: wrong result of a valid second call to GetEnv!\n");
 887     return JNI_ERR;
 888   }
 889 
 890   if (PRINT_OUT) {
 891     fprintf(stderr, "Storage is at %p, secondary is at %p\n",
 892             &global_event_storage, &second_global_event_storage);
 893   }
 894 
 895   (*jvmti)->CreateRawMonitor(jvmti, "storage_monitor",
 896                              &global_event_storage.storage_monitor);
 897   (*jvmti)->CreateRawMonitor(jvmti, "second_storage_monitor",
 898                              &second_global_event_storage.storage_monitor);
 899 
 900   (*jvmti)->CreateRawMonitor(jvmti, "compaction_monitor",
 901                              &global_event_storage.compaction_monitor);
 902   (*jvmti)->CreateRawMonitor(jvmti, "second_compaction_monitor",
 903                              &second_global_event_storage.compaction_monitor);
 904 
 905   event_storage_set_garbage_history(&global_event_storage, 200);
 906   event_storage_set_garbage_history(&second_global_event_storage, 200);
 907 
 908   memset(&callbacks, 0, sizeof(callbacks));
 909   callbacks.SampledObjectAlloc = &SampledObjectAlloc;
 910   callbacks.VMObjectAlloc = &VMObjectAlloc;
 911   callbacks.GarbageCollectionFinish = &GarbageCollectionFinish;
 912 
 913   memset(&caps, 0, sizeof(caps));
 914   // Get line numbers, sample events, filename, and gc events for the tests.
 915   caps.can_get_line_numbers = 1;
 916   caps.can_get_source_file_name = 1;
 917   caps.can_generate_garbage_collection_events = 1;
 918   caps.can_generate_sampled_object_alloc_events = 1;
 919   caps.can_generate_vm_object_alloc_events = 1;
 920   if (check_error((*jvmti)->AddCapabilities(jvmti, &caps), "Add capabilities")) {
 921     return JNI_ERR;
 922   }
 923 
 924   if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks,
 925                                               sizeof(jvmtiEventCallbacks)),
 926                   " Set Event Callbacks")) {
 927     return JNI_ERR;
 928   }
 929   return JNI_OK;
 930 }
 931 
 932 JNIEXPORT void JNICALL
 933 Java_MyPackage_HeapMonitor_setSamplingInterval(JNIEnv* env, jclass cls, jint value) {
 934   (*jvmti)->SetHeapSamplingInterval(jvmti, value);
 935 }
 936 
 937 JNIEXPORT jboolean JNICALL
 938 Java_MyPackage_HeapMonitor_eventStorageIsEmpty(JNIEnv* env, jclass cls) {
 939   return event_storage_get_count(&global_event_storage) == 0;
 940 }
 941 
 942 JNIEXPORT jint JNICALL
 943 Java_MyPackage_HeapMonitor_getEventStorageElementCount(JNIEnv* env, jclass cls) {
 944   return event_storage_get_count(&global_event_storage);
 945 }
 946 
 947 JNIEXPORT void JNICALL
 948 Java_MyPackage_HeapMonitor_enableSamplingEvents(JNIEnv* env, jclass cls) {
 949   enable_notifications();
 950 }
 951 
 952 JNIEXPORT jboolean JNICALL
 953 Java_MyPackage_HeapMonitor_enableSamplingEventsForTwoThreads(JNIEnv* env,
 954                                                              jclass cls,
 955                                                              jthread first,
 956                                                              jthread second) {
 957   return enable_notifications_for_two_threads(first, second);
 958 }
 959 
 960 JNIEXPORT void JNICALL
 961 Java_MyPackage_HeapMonitor_disableSamplingEvents(JNIEnv* env, jclass cls) {
 962   check_error((*jvmti)->SetEventNotificationMode(
 963       jvmti, JVMTI_DISABLE, JVMTI_EVENT_SAMPLED_OBJECT_ALLOC, NULL),
 964               "Set event notifications");
 965 
 966   check_error((*jvmti)->SetEventNotificationMode(
 967       jvmti, JVMTI_DISABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, NULL),
 968               "Garbage Collection Finish");
 969 }
 970 
 971 JNIEXPORT jboolean JNICALL
 972 Java_MyPackage_HeapMonitor_obtainedEvents(JNIEnv* env, jclass cls,
 973                                           jobjectArray frames,
 974                                           jboolean check_lines) {
 975   jboolean result;
 976   jsize size = JNI_ENV_PTR(env)->GetArrayLength(JNI_ENV_ARG2(env, frames));
 977   ExpectedContentFrame *native_frames;
 978 
 979   if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
 980     JNI_ENV_PTR(env)->FatalError(
 981         JNI_ENV_ARG2(env, "obtainedEvents failed with the GetArrayLength call"));
 982   }
 983 
 984   native_frames = malloc(size * sizeof(*native_frames));
 985 
 986   if (native_frames == NULL) {
 987     JNI_ENV_PTR(env)->FatalError(
 988         JNI_ENV_ARG2(env, "Error in obtainedEvents: malloc returned NULL for native_frames allocation\n"));
 989   }
 990 
 991   if (fill_native_frames(env, frames, native_frames, size) != 0) {
 992     JNI_ENV_PTR(env)->FatalError(
 993         JNI_ENV_ARG2(env, "Error in obtainedEvents: fill_native_frames returned failed status\n"));
 994   }
 995   result = event_storage_contains(env, &global_event_storage, native_frames,
 996                                   size, check_lines);
 997 
 998   free(native_frames), native_frames = NULL;
 999   return result;
1000 }
1001 
1002 JNIEXPORT jboolean JNICALL
1003 Java_MyPackage_HeapMonitor_garbageContains(JNIEnv* env, jclass cls,
1004                                            jobjectArray frames,
1005                                            jboolean check_lines) {
1006   jboolean result;
1007   jsize size = JNI_ENV_PTR(env)->GetArrayLength(JNI_ENV_ARG2(env, frames));
1008   ExpectedContentFrame *native_frames;
1009 
1010   if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env))) {
1011     JNI_ENV_PTR(env)->FatalError(
1012         JNI_ENV_ARG2(env, "garbageContains failed with the GetArrayLength call"));
1013   }
1014 
1015   native_frames = malloc(size * sizeof(*native_frames));
1016 
1017   if (native_frames == NULL) {
1018     JNI_ENV_PTR(env)->FatalError(
1019         JNI_ENV_ARG2(env, "Error in garbageContains: malloc returned NULL for native_frames allocation\n"));
1020   }
1021 
1022   if (fill_native_frames(env, frames, native_frames, size) != 0) {
1023     JNI_ENV_PTR(env)->FatalError(
1024         JNI_ENV_ARG2(env, "Error in garbageContains: fill_native_frames returned failed status\n"));
1025   }
1026   result = event_storage_garbage_contains(env, &global_event_storage,
1027                                           native_frames, size, check_lines);
1028 
1029   free(native_frames), native_frames = NULL;
1030   return result;
1031 }
1032 
1033 JNIEXPORT void JNICALL
1034 Java_MyPackage_HeapMonitor_forceGarbageCollection(JNIEnv* env, jclass cls) {
1035   check_error((*jvmti)->ForceGarbageCollection(jvmti),
1036               "Forced Garbage Collection");
1037 }
1038 
1039 JNIEXPORT void JNICALL
1040 Java_MyPackage_HeapMonitor_resetEventStorage(JNIEnv* env, jclass cls) {
1041   event_storage_reset(&global_event_storage);
1042   event_storage_reset(&second_global_event_storage);
1043 }
1044 
1045 JNIEXPORT jboolean JNICALL
1046 Java_MyPackage_HeapMonitorNoCapabilityTest_allSamplingMethodsFail(JNIEnv *env,
1047                                                                   jclass cls) {
1048   jvmtiCapabilities caps;
1049   memset(&caps, 0, sizeof(caps));
1050   caps.can_generate_sampled_object_alloc_events = 1;
1051   if (check_error((*jvmti)->RelinquishCapabilities(jvmti, &caps),
1052                   "Add capabilities\n")){
1053     return FALSE;
1054   }
1055 
1056   if (check_capability_error((*jvmti)->SetHeapSamplingInterval(jvmti, 1<<19),
1057                              "Set Heap Sampling Interval")) {
1058     return FALSE;
1059   }
1060   return TRUE;
1061 }
1062 
1063 JNIEXPORT jboolean JNICALL
1064 Java_MyPackage_HeapMonitorIllegalArgumentTest_testIllegalArgument(JNIEnv *env,
1065                                                                   jclass cls) {
1066   if (check_error((*jvmti)->SetHeapSamplingInterval(jvmti, 0),
1067                   "Sampling interval 0 failed\n")){
1068     return FALSE;
1069   }
1070 
1071   if (check_error((*jvmti)->SetHeapSamplingInterval(jvmti, 1024),
1072                   "Sampling interval 1024 failed\n")){
1073     return FALSE;
1074   }
1075 
1076   if (!check_error((*jvmti)->SetHeapSamplingInterval(jvmti, -1),
1077                    "Sampling interval -1 passed\n")){
1078     return FALSE;
1079   }
1080 
1081   if (!check_error((*jvmti)->SetHeapSamplingInterval(jvmti, -1024),
1082                    "Sampling interval -1024 passed\n")){
1083     return FALSE;
1084   }
1085 
1086   return TRUE;
1087 }
1088 
1089 JNIEXPORT jdouble JNICALL
1090 Java_MyPackage_HeapMonitor_getAverageSize(JNIEnv *env, jclass cls) {
1091   return event_storage_get_average_size(&global_event_storage);
1092 }
1093 
1094 typedef struct sThreadsFound {
1095   jthread* threads;
1096   int num_threads;
1097 } ThreadsFound;
1098 
1099 JNIEXPORT jboolean JNICALL
1100 Java_MyPackage_HeapMonitorThreadTest_checkSamples(JNIEnv* env, jclass cls,
1101                                                   jint num_threads) {
1102 
1103   print_thread_stats();
1104   // Ensure we got stacks from at least num_threads.
1105   return thread_stats.number_threads >= num_threads;
1106 }
1107 
1108 JNIEXPORT
1109 void JNICALL SampledObjectAlloc2(jvmtiEnv *jvmti_env,
1110                                  JNIEnv* jni_env,
1111                                  jthread thread,
1112                                  jobject object,
1113                                  jclass object_klass,
1114                                  jlong size) {
1115   // Nop for now, two agents are not yet implemented.
1116   assert(0);
1117 }
1118 
1119 JNIEXPORT jboolean JNICALL
1120 Java_MyPackage_HeapMonitorTwoAgentsTest_enablingSamplingInSecondaryAgent(
1121     JNIEnv* env, jclass cls) {
1122   // Currently this method should be failing directly at the AddCapability step
1123   // but the implementation is correct for when multi-agent support is enabled.
1124   jvmtiCapabilities caps;
1125   jvmtiEventCallbacks callbacks;
1126 
1127   memset(&caps, 0, sizeof(caps));
1128   caps.can_generate_sampled_object_alloc_events = 1;
1129   if (check_error((*second_jvmti)->AddCapabilities(second_jvmti, &caps),
1130                   "Set the capability for second agent")) {
1131     return FALSE;
1132   }
1133 
1134   memset(&callbacks, 0, sizeof(callbacks));
1135   callbacks.SampledObjectAlloc = &SampledObjectAlloc2;
1136 
1137   if (check_error((*second_jvmti)->SetEventCallbacks(second_jvmti, &callbacks,
1138                                                      sizeof(jvmtiEventCallbacks)),
1139                   " Set Event Callbacks for second agent")) {
1140     return FALSE;
1141   }
1142 
1143   return TRUE;
1144 }
1145 
1146 JNIEXPORT void JNICALL
1147 Java_MyPackage_HeapMonitor_enableVMEvents(JNIEnv* env, jclass cls) {
1148   check_error((*jvmti)->SetEventNotificationMode(
1149       jvmti, JVMTI_ENABLE, JVMTI_EVENT_VM_OBJECT_ALLOC, NULL),
1150               "Set vm event notifications");
1151 }
1152 
1153 JNIEXPORT jint JNICALL
1154 Java_MyPackage_HeapMonitorVMEventsTest_vmEvents(JNIEnv* env, jclass cls) {
1155   return event_storage_number_additions(&second_global_event_storage);
1156 }
1157 
1158 JNIEXPORT jint JNICALL
1159 Java_MyPackage_HeapMonitor_sampledEvents(JNIEnv* env, jclass cls) {
1160   return event_storage_number_additions(&global_event_storage);
1161 }
1162 
1163 static jobject allocate_object(JNIEnv* env) {
1164   // Construct an Object.
1165   jclass cls = JNI_ENV_PTR(env)->FindClass(JNI_ENV_ARG2(env, "java/lang/Object"));
1166   jmethodID constructor;
1167   jobject result;
1168 
1169   if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env)) || cls == NULL) {
1170     JNI_ENV_PTR(env)->FatalError(
1171         JNI_ENV_ARG2(env, "Error in jni FindClass: Cannot find Object class\n"));
1172   }
1173 
1174   constructor = JNI_ENV_PTR(env)->GetMethodID(JNI_ENV_ARG4(env, cls, "<init>", "()V"));
1175   if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env)) || constructor == NULL) {
1176     JNI_ENV_PTR(env)->FatalError(
1177         JNI_ENV_ARG2(env, "Error in jni GetMethodID: Cannot find Object class constructor\n"));
1178   }
1179 
1180   // Call back constructor to allocate a new instance, with an int argument
1181   result = JNI_ENV_PTR(env)->NewObject(JNI_ENV_ARG3(env, cls, constructor));
1182 
1183   if (JNI_ENV_PTR(env)->ExceptionOccurred(JNI_ENV_ARG(env)) || result == NULL) {
1184     JNI_ENV_PTR(env)->FatalError(
1185         JNI_ENV_ARG2(env, "Error in jni NewObject: Cannot allocate an object\n"));
1186   }
1187   return result;
1188 }
1189 
1190 // Ensure we got a callback for the test.
1191 static int did_recursive_callback_test;
1192 
1193 JNIEXPORT
1194 void JNICALL RecursiveSampledObjectAlloc(jvmtiEnv *jvmti_env,
1195                                          JNIEnv* jni_env,
1196                                          jthread thread,
1197                                          jobject object,
1198                                          jclass object_klass,
1199                                          jlong size) {
1200   // Basically ensure that if we were to allocate objects, we would not have an
1201   // infinite recursion here.
1202   int i;
1203   for (i = 0; i < 1000; i++) {
1204     if (allocate_object(jni_env) == NULL) {
1205       JNI_ENV_PTR(jni_env)->FatalError(JNI_ENV_ARG2(jni_env, "allocate_object returned NULL\n"));
1206     }
1207   }
1208 
1209   did_recursive_callback_test = 1;
1210 }
1211 
1212 JNIEXPORT jboolean JNICALL
1213 Java_MyPackage_HeapMonitorRecursiveTest_didCallback(JNIEnv* env, jclass cls) {
1214   return did_recursive_callback_test != 0;
1215 }
1216 
1217 JNIEXPORT void JNICALL
1218 Java_MyPackage_HeapMonitorRecursiveTest_setCallbackToCallAllocateSomeMore(JNIEnv* env, jclass cls) {
1219   jvmtiEventCallbacks callbacks;
1220 
1221   memset(&callbacks, 0, sizeof(callbacks));
1222   callbacks.SampledObjectAlloc = &RecursiveSampledObjectAlloc;
1223 
1224   if (check_error((*jvmti)->SetEventCallbacks(jvmti, &callbacks,
1225                                               sizeof(jvmtiEventCallbacks)),
1226                   " Set Event Callbacks")) {
1227     JNI_ENV_PTR(env)->FatalError(JNI_ENV_ARG2(env, "Cannot reset the callback."));
1228   }
1229 }
1230 
1231 #ifdef __cplusplus
1232 }
1233 #endif