1 /*
   2  * Copyright (c) 2019 SAP SE. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 #include <stdio.h>
  25 #include <string.h>
  26 #include "jvmti.h"
  27 #include "jni.h"
  28 
  29 #ifndef JNI_ENV_ARG
  30 #define JNI_ENV_ARG(x,y) x, y
  31 #define JNI_ENV_PTR(x) (*x)
  32 #endif
  33 
  34 #define FAILED -1
  35 #define OK      0
  36 
  37 static jvmtiEnv *jvmti;
  38 
  39 static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
  40 
  41 static void ShowErrorMessage(jvmtiEnv *jvmti, jvmtiError errCode, const char *message) {
  42     char *errMsg;
  43     jvmtiError result;
  44 
  45     result = (*jvmti)->GetErrorName(jvmti, errCode, &errMsg);
  46     if (result == JVMTI_ERROR_NONE) {
  47         fprintf(stderr, "%s: %s (%d)\n", message, errMsg, errCode);
  48         (*jvmti)->Deallocate(jvmti, (unsigned char *)errMsg);
  49     } else {
  50         fprintf(stderr, "%s (%d)\n", message, errCode);
  51     }
  52 }
  53 
  54 JNIEXPORT jint JNICALL
  55 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
  56     return Agent_Initialize(jvm, options, reserved);
  57 }
  58 
  59 JNIEXPORT jint JNICALL
  60 Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
  61     return Agent_Initialize(jvm, options, reserved);
  62 }
  63 
  64 JNIEXPORT jint JNICALL
  65 JNI_OnLoad(JavaVM *jvm, void *reserved) {
  66     jint res;
  67     JNIEnv *env;
  68 
  69     res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &env),
  70                                    JNI_VERSION_9);
  71     if (res != JNI_OK || env == NULL) {
  72         fprintf(stderr, "Error: GetEnv call failed(%d)!\n", res);
  73         return JNI_ERR;
  74     }
  75 
  76     return JNI_VERSION_9;
  77 }
  78 
  79 static jint
  80 Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
  81     jint res;
  82     jvmtiError err;
  83     jvmtiCapabilities caps;
  84 
  85     printf("Agent_OnLoad started\n");
  86 
  87     memset(&caps, 0, sizeof(caps));
  88 
  89     res = JNI_ENV_PTR(jvm)->GetEnv(JNI_ENV_ARG(jvm, (void **) &jvmti),
  90                                    JVMTI_VERSION_9);
  91     if (res != JNI_OK || jvmti == NULL) {
  92         fprintf(stderr, "Error: wrong result of a valid call to GetEnv!\n");
  93         return JNI_ERR;
  94     }
  95 
  96     caps.can_tag_objects = 1;
  97 
  98     err = (*jvmti)->AddCapabilities(jvmti, &caps);
  99     if (err != JVMTI_ERROR_NONE) {
 100         ShowErrorMessage(jvmti, err,
 101                          "Agent_OnLoad: error in JVMTI AddCapabilities");
 102         return JNI_ERR;
 103     }
 104 
 105     err = (*jvmti)->GetCapabilities(jvmti, &caps);
 106     if (err != JVMTI_ERROR_NONE) {
 107         ShowErrorMessage(jvmti, err,
 108                          "Agent_OnLoad: error in JVMTI GetCapabilities");
 109         return JNI_ERR;
 110     }
 111 
 112     if (!caps.can_tag_objects) {
 113         fprintf(stderr, "Warning: didn't get the capability can_tag_objects\n");
 114         return JNI_ERR;
 115     }
 116 
 117     printf("Agent_OnLoad finished\n");
 118     return JNI_OK;
 119 }
 120 
 121 static jobject method_IterateOverReachableObjects;
 122 static jobject method_IterateOverHeap;
 123 static jobject method_IterateOverInstancesOfClass;
 124 static jobject method_FollowReferences;
 125 static jobject method_IterateThroughHeap;
 126 
 127 JNIEXPORT jint JNICALL
 128 Java_IterateHeapWithEscapeAnalysisEnabled_registerMethod(JNIEnv *env, jclass cls, jobject method, jstring name) {
 129   const char *name_chars = (*env)->GetStringUTFChars(env, name, 0);
 130   int rc = FAILED;
 131   if (rc != OK && strcmp(name_chars, "IterateOverReachableObjects") == 0) {
 132     method_IterateOverReachableObjects = (*env)->NewGlobalRef(env, method);
 133     rc = OK;
 134   }
 135   if (rc != OK && strcmp(name_chars, "IterateOverHeap") == 0) {
 136     method_IterateOverHeap = (*env)->NewGlobalRef(env, method);
 137     rc = OK;
 138   }
 139   if (rc != OK && strcmp(name_chars, "IterateOverInstancesOfClass") == 0) {
 140     method_IterateOverInstancesOfClass = (*env)->NewGlobalRef(env, method);
 141     rc = OK;
 142   }
 143   if (rc != OK && strcmp(name_chars, "IterateThroughHeap") == 0) {
 144     method_IterateThroughHeap = (*env)->NewGlobalRef(env, method);
 145     rc = OK;
 146   }
 147   if (rc != OK && strcmp(name_chars, "FollowReferences") == 0) {
 148     method_FollowReferences = (*env)->NewGlobalRef(env, method);
 149     rc = OK;
 150   }
 151   (*env)->ReleaseStringUTFChars(env, name, name_chars);
 152   return rc;
 153 }
 154 
 155 JNIEXPORT void JNICALL
 156 Java_IterateHeapWithEscapeAnalysisEnabled_agentTearDown(JNIEnv *env, jclass cls) {
 157   (*env)->DeleteGlobalRef(env, method_IterateOverReachableObjects);
 158   (*env)->DeleteGlobalRef(env, method_IterateOverHeap);
 159   (*env)->DeleteGlobalRef(env, method_IterateOverInstancesOfClass);
 160   (*env)->DeleteGlobalRef(env, method_FollowReferences);
 161   (*env)->DeleteGlobalRef(env, method_IterateThroughHeap);
 162 }
 163 
 164 JNIEXPORT jint JNICALL
 165 Java_IterateHeapWithEscapeAnalysisEnabled_jvmtiTagClass(JNIEnv *env, jclass cls, jclass clsToTag, jlong tag) {
 166     jvmtiError err;
 167     err = (*jvmti)->SetTag(jvmti, clsToTag, tag);
 168     if (err != JVMTI_ERROR_NONE) {
 169         ShowErrorMessage(jvmti, err,
 170                          "jvmtiTagClass: error in JVMTI SetTag");
 171         return FAILED;
 172     }
 173     return OK;
 174 }
 175 
 176 typedef struct Tag_And_Counter {
 177     jlong instance_counter;
 178     jlong class_tag;
 179     jlong instance_tag;
 180 } Tag_And_Counter;
 181 
 182 static jvmtiIterationControl JNICALL
 183 __stackReferenceCallback(jvmtiHeapRootKind root_kind,
 184                        jlong class_tag,
 185                        jlong size,
 186                        jlong* tag_ptr,
 187                        jlong thread_tag,
 188                        jint depth,
 189                        jmethodID method,
 190                        jint slot,
 191                        void* d) {
 192     Tag_And_Counter* data = (Tag_And_Counter*) d;
 193     if (class_tag == data->class_tag && *tag_ptr == 0) {
 194         data->instance_counter++;
 195         *tag_ptr = data->instance_tag;
 196     }
 197     return JVMTI_ITERATION_CONTINUE;
 198 }
 199 
 200 static jvmtiIterationControl JNICALL
 201 __jvmtiHeapObjectCallback(jlong class_tag, jlong size, jlong* tag_ptr, void* d) {
 202     Tag_And_Counter* data = (Tag_And_Counter*) d;
 203     if (class_tag == data->class_tag && *tag_ptr == 0) {
 204         data->instance_counter++;
 205         *tag_ptr = data->instance_tag;
 206     }
 207     return JVMTI_ITERATION_CONTINUE;
 208 }
 209 
 210 static jint JNICALL
 211 __jvmtiHeapReferenceCallback(jvmtiHeapReferenceKind reference_kind,
 212                              const jvmtiHeapReferenceInfo* reference_info,
 213                              jlong class_tag,
 214                              jlong referrer_class_tag,
 215                              jlong size,
 216                              jlong* tag_ptr,
 217                              jlong* referrer_tag_ptr,
 218                              jint length,
 219                              void* d) {
 220     Tag_And_Counter* data = (Tag_And_Counter*) d;
 221     if (class_tag == data->class_tag && *tag_ptr == 0) {
 222         data->instance_counter++;
 223         *tag_ptr = data->instance_tag;
 224     }
 225     return JVMTI_VISIT_OBJECTS;
 226 }
 227 
 228 static jint JNICALL
 229 __jvmtiHeapIterationCallback(jlong class_tag,
 230                              jlong size,
 231                              jlong* tag_ptr,
 232                              jint length,
 233                              void* d) {
 234     Tag_And_Counter* data = (Tag_And_Counter*) d;
 235     if (class_tag == data->class_tag && *tag_ptr == 0) {
 236         data->instance_counter++;
 237         *tag_ptr = data->instance_tag;
 238     }
 239     return JVMTI_VISIT_OBJECTS;
 240 }
 241 
 242 
 243 JNIEXPORT jlong JNICALL
 244 Java_IterateHeapWithEscapeAnalysisEnabled_countAndTagInstancesOfClass(JNIEnv *env,
 245                                                                       jclass cls,
 246                                                                       jclass tagged_class,
 247                                                                       jlong cls_tag,
 248                                                                       jlong instance_tag,
 249                                                                       jobject method) {
 250     jvmtiError err;
 251     Tag_And_Counter data = {0, cls_tag, instance_tag};
 252     jboolean method_found = JNI_FALSE;
 253 
 254     jint idx = 0;
 255 
 256     if ((*env)->IsSameObject(env, method, method_IterateOverReachableObjects)) {
 257         method_found = JNI_TRUE;
 258         err = (*jvmti)->IterateOverReachableObjects(jvmti,
 259                                                     NULL /*jvmtiHeapRootCallback*/,
 260                                                     __stackReferenceCallback,
 261                                                     NULL /* jvmtiObjectReferenceCallback */,
 262                                                     &data);
 263         if (err != JVMTI_ERROR_NONE) {
 264             ShowErrorMessage(jvmti, err,
 265                              "countAndTagInstancesOfClass: error in JVMTI IterateOverReachableObjects");
 266             return FAILED;
 267         }
 268     }
 269     if ((*env)->IsSameObject(env, method, method_IterateOverHeap)) {
 270         method_found = JNI_TRUE;
 271         err = (*jvmti)->IterateOverHeap(jvmti,
 272                                         JVMTI_HEAP_OBJECT_EITHER,
 273                                         __jvmtiHeapObjectCallback,
 274                                         &data);
 275         if (err != JVMTI_ERROR_NONE) {
 276             ShowErrorMessage(jvmti, err,
 277                              "countAndTagInstancesOfClass: error in JVMTI IterateOverHeap");
 278             return FAILED;
 279         }
 280     }
 281     if ((*env)->IsSameObject(env, method, method_IterateOverInstancesOfClass)) {
 282         method_found = JNI_TRUE;
 283         err = (*jvmti)->IterateOverInstancesOfClass(jvmti,
 284                                                     tagged_class,
 285                                                     JVMTI_HEAP_OBJECT_EITHER,
 286                                                     __jvmtiHeapObjectCallback,
 287                                                     &data);
 288         if (err != JVMTI_ERROR_NONE) {
 289             ShowErrorMessage(jvmti, err,
 290                              "countAndTagInstancesOfClass: error in JVMTI IterateOverHeap");
 291             return FAILED;
 292         }
 293     }
 294     if ((*env)->IsSameObject(env, method, method_FollowReferences)) {
 295         method_found = JNI_TRUE;
 296         jvmtiHeapCallbacks callbacks = {0};
 297         callbacks.heap_reference_callback = __jvmtiHeapReferenceCallback;
 298         err = (*jvmti)->FollowReferences(jvmti,
 299                                          0 /* filter nothing */,
 300                                          NULL /* no class filter */,
 301                                          NULL /* no initial object, follow roots */,
 302                                          &callbacks,
 303                                          &data);
 304         if (err != JVMTI_ERROR_NONE) {
 305             ShowErrorMessage(jvmti, err,
 306                              "countAndTagInstancesOfClass: error in JVMTI FollowReferences");
 307             return FAILED;
 308         }
 309     }
 310     if ((*env)->IsSameObject(env, method, method_IterateThroughHeap)) {
 311         method_found = JNI_TRUE;
 312         jvmtiHeapCallbacks callbacks = {0};
 313         callbacks.heap_iteration_callback = __jvmtiHeapIterationCallback;
 314         err = (*jvmti)->IterateThroughHeap(jvmti,
 315                                            0 /* filter nothing */,
 316                                            NULL /* no class filter */,
 317                                            &callbacks,
 318                                            &data);
 319         if (err != JVMTI_ERROR_NONE) {
 320             ShowErrorMessage(jvmti, err,
 321                              "countAndTagInstancesOfClass: error in JVMTI IterateThroughHeap");
 322             return FAILED;
 323         }
 324     }
 325 
 326     if (!method_found) {
 327         fprintf(stderr, "countAndTagInstancesOfClass: unknown method\n");
 328         return FAILED;
 329     }
 330 
 331     return data.instance_counter;
 332 }
 333 
 334 JNIEXPORT jlong JNICALL
 335 Java_IterateHeapWithEscapeAnalysisEnabled_getObjectsWithTag(JNIEnv *env,
 336                                                             jclass cls,
 337                                                             jlong tag,
 338                                                             jarray res_instances) {
 339   jvmtiError  err;
 340   const jlong tags[1] = {tag};
 341   jint        res_count = -1;
 342   jobject*    res_instances_raw;
 343   jlong*      res_tags;
 344   jint        res_instances_length;
 345   jint        idx;
 346 
 347   err = (*jvmti)->GetObjectsWithTags(jvmti,
 348                                      1,
 349                                      tags,
 350                                      &res_count,
 351                                      &res_instances_raw,
 352                                      &res_tags);
 353   if (err != JVMTI_ERROR_NONE) {
 354       ShowErrorMessage(jvmti, err,
 355                        "getObjectsWithTags: error in JVMTI GetObjectsWithTags");
 356       return FAILED;
 357   }
 358 
 359   res_instances_length = (*env)->GetArrayLength(env, res_instances);
 360   if (res_count != res_instances_length) {
 361     fprintf(stderr, "getObjectsWithTags: result array lenght (%d) does not match instance count returned by GetObjectsWithTags (%d) \n",
 362             res_instances_length, res_count);
 363     return FAILED;
 364   }
 365 
 366   for (idx = 0; idx < res_count; idx++) {
 367     (*env)->SetObjectArrayElement(env, res_instances, idx, res_instances_raw[idx]);
 368   }
 369 
 370   (*jvmti)->Deallocate(jvmti, (unsigned char *)res_instances_raw);
 371   (*jvmti)->Deallocate(jvmti, (unsigned char *)res_tags);
 372 
 373   return OK;
 374 }