1 /*
   2  * Copyright (c) 2007, 2018, Oracle 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 <string.h>
  25 #include "jvmti.h"
  26 #include "agent_common.h"
  27 #include "jni_tools.h"
  28 #include "jvmti_tools.h"
  29 
  30 extern "C" {
  31 
  32 /* ============================================================================= */
  33 
  34 static jlong timeout = 0;
  35 
  36 #define INFO_NONE       0x00
  37 #define INFO_ALL        0xFF
  38 #define INFO_OBJREF     0x01
  39 #define INFO_STACKREF   0x02
  40 #define INFO_HEAPROOT   0x04
  41 #define INFO_HEAPOBJ    0x08
  42 
  43 static unsigned int info = INFO_ALL;
  44 
  45 #define DEBUGEE_CLASS_NAME      "nsk/jvmti/unit/FollowReferences/followref003"
  46 #define ROOT_OBJECT_CLASS_NAME  "nsk/jvmti/unit/FollowReferences/followref003RootTestedClass"
  47 #define ROOT_OBJECT_CLASS_SIG   "L" ROOT_OBJECT_CLASS_NAME ";"
  48 #define CHAIN_OBJECT_CLASS_NAME "nsk/jvmti/unit/FollowReferences/followref003TestedClass"
  49 #define CHAIN_OBJECT_CLASS_SIG  "L" CHAIN_OBJECT_CLASS_NAME ";"
  50 
  51 #define OBJECT_FIELD_NAME               "object"
  52 #define REACHABLE_CHAIN_FIELD_NAME      "reachableChain"
  53 #define UNREACHABLE_CHAIN_FIELD_NAME    "unreachableChain"
  54 #define TAIL_FIELD_NAME                 "tail"
  55 
  56 
  57 #define DEFAULT_CHAIN_LENGTH 3
  58 #define MAXDEPTH 50
  59 #define MAXSLOT  16
  60 
  61 typedef struct ObjectDescStruct {
  62     jlong tag;
  63     jlong class_tag;
  64     jlong exp_class_tag;
  65     jint exp_found;
  66     jint found;
  67 } ObjectDesc;
  68 
  69 static int chainLength   = 0;
  70 static int objectsCount  = 0;
  71 static int fakeUserData  = 0;
  72 static int userDataError = 0;
  73 
  74 static ObjectDesc* objectDescList = NULL;
  75 
  76 #define TARG_THREAD_TAG  11
  77 #define FIRST_THREAD_TAG (TARG_THREAD_TAG + 1)
  78 
  79 #define TARG_FRAME_DEPTH  1
  80 
  81 static jlong rootClassTag   = 9;
  82 static jlong chainClassTag  = 99;
  83 static jlong thrObjectTag   = FIRST_THREAD_TAG;
  84 static jlong rootObjectTag  = 55;
  85 static jlong chainObjectTag = 100;
  86 
  87 
  88 /* Java method frame slots interesting to check */
  89 #define ARGV_STRING_ARR_SLOT   1
  90 #define FIRST_PRIM_ARR_SLOT    3
  91 #define LAST_PRIM_ARR_SLOT     10
  92 #define DUMMY_STRING_ARR_SLOT  11
  93 
  94 
  95 static jvmtiHeapCallbacks heapCallbacks;
  96 
  97 static const char* ref_kind_str[28] = {
  98    "unknown_0",
  99    "REFERENCE_CLASS",
 100    "REFERENCE_FIELD",
 101    "REFERENCE_ARRAY_ELEMENT",
 102    "REFERENCE_CLASS_LOADER",
 103    "REFERENCE_SIGNERS",
 104    "REFERENCE_PROTECTION_DOMAIN",
 105    "REFERENCE_INTERFACE",
 106    "REFERENCE_STATIC_FIELD",
 107    "REFERENCE_CONSTANT_POOL",
 108    "unknown_10", "unknown_11", "unknown_12",
 109    "unknown_13", "unknown_14", "unknown_15", "unknown_16",
 110    "unknown_17", "unknown_18", "unknown_19", "unknown_20",
 111    "REFERENCE_JNI_GLOBAL",
 112    "REFERENCE_SYSTEM_CLASS",
 113    "REFERENCE_MONITOR",
 114    "REFERENCE_STACK_LOCAL",
 115    "REFERENCE_JNI_LOCAL",
 116    "REFERENCE_THREAD",
 117    "REFERENCE_OTHER"
 118 };
 119 
 120 
 121 #define DEREF(ptr) (((ptr) == NULL ? 0 : *(ptr)))
 122 
 123 
 124 /* ============================================================================= */
 125 
 126 /** Obtain chain of tested objects and tag them recursively. */
 127 static int getChainObjects(jvmtiEnv* jvmti, JNIEnv* jni, jobject firstObject,
 128                            jfieldID firstField, const char firstFieldName[],
 129                            jfieldID nextField, const char nextFieldName[],
 130                            int count, ObjectDesc objectDescList[],
 131                            jlong tag, int reachable) {
 132     jobject obj = NULL;
 133     jlong objTag = (reachable ? tag : -tag);
 134 
 135     if (count <= 0)
 136         return NSK_TRUE;
 137 
 138     count--;
 139     tag++;
 140 
 141     if (!NSK_JNI_VERIFY(jni, (obj =
 142             NSK_CPP_STUB3(GetObjectField, jni, firstObject, firstField)) != NULL)) {
 143         nsk_jvmti_setFailStatus();
 144         return NSK_FALSE;
 145     }
 146 
 147     objectDescList[count].tag = objTag;
 148 
 149     if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(SetTag, jvmti, obj, objTag))) {
 150         nsk_jvmti_setFailStatus();
 151     }
 152     printf("        tag=%-5ld object=0x%p\n", (long)objTag, (void*)obj);
 153     fflush(0);
 154     if (!getChainObjects(jvmti, jni, obj, nextField, nextFieldName,
 155                                 nextField, nextFieldName,
 156                                 count, objectDescList, tag, reachable)) {
 157         return NSK_FALSE;
 158     }
 159 
 160     NSK_TRACE(NSK_CPP_STUB2(DeleteLocalRef, jni, obj));
 161     return NSK_TRUE;
 162 }
 163 
 164 /** Obtain all tested objects from debugee class and tag them recursively. */
 165 static int getTestedObjects(jvmtiEnv* jvmti, JNIEnv* jni, int chainLength,
 166                             int *objectsCount, ObjectDesc* *objectDescList,
 167                             jobject* rootObject) {
 168     jclass debugeeClass = NULL;
 169     jclass rootObjectClass = NULL;
 170     jclass chainObjectClass = NULL;
 171 
 172     jfieldID objectField = NULL;
 173     jfieldID reachableChainField = NULL;
 174     jfieldID unreachableChainField = NULL;
 175     jfieldID tailField = NULL;
 176 
 177     *objectsCount = 1 + 2 * chainLength;
 178 
 179     printf("Allocate memory for objects list: %d objects\n", *objectsCount);
 180     fflush(0);
 181     if (!NSK_JVMTI_VERIFY(
 182             NSK_CPP_STUB3(Allocate, jvmti, (*objectsCount * sizeof(ObjectDesc)),
 183                                              (unsigned char**)objectDescList))) {
 184         nsk_jvmti_setFailStatus();
 185         return NSK_FALSE;
 186     }
 187     printf("  ... allocated array: 0x%p\n", (void*)objectDescList);
 188     fflush(0);
 189 
 190     {
 191         int k;
 192         for (k = 0; k < *objectsCount; k++) {
 193             (*objectDescList)[k].tag = 0;
 194             (*objectDescList)[k].exp_class_tag = chainClassTag;
 195             (*objectDescList)[k].exp_found = 0;
 196             (*objectDescList)[k].found = 0;
 197         }
 198     }
 199     (*objectDescList)[0].exp_class_tag = rootClassTag;
 200 
 201     printf("Find debugee class: %s\n", DEBUGEE_CLASS_NAME);
 202     fflush(0);
 203     if (!NSK_JNI_VERIFY(jni, (debugeeClass =
 204             NSK_CPP_STUB2(FindClass, jni, DEBUGEE_CLASS_NAME)) != NULL)) {
 205         nsk_jvmti_setFailStatus();
 206         return NSK_FALSE;
 207     }
 208     printf("  ... found class: 0x%p\n", (void*)debugeeClass);
 209 
 210     printf("Find root object class: %s\n", ROOT_OBJECT_CLASS_NAME);
 211     fflush(0);
 212     if (!NSK_JNI_VERIFY(jni, (rootObjectClass =
 213             NSK_CPP_STUB2(FindClass, jni, ROOT_OBJECT_CLASS_NAME)) != NULL)) {
 214         nsk_jvmti_setFailStatus();
 215         return NSK_FALSE;
 216     }
 217     printf("  ... found class: 0x%p\n", (void*)rootObjectClass);
 218 
 219     if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(SetTag, jvmti, rootObjectClass, rootClassTag))) {
 220         nsk_jvmti_setFailStatus();
 221     }
 222     printf("        tag=%-5ld rootClass=0x%p\n",
 223            (long)rootClassTag, (void*)rootObjectClass);
 224 
 225     printf("Find chain object class: %s\n", CHAIN_OBJECT_CLASS_NAME);
 226     fflush(0);
 227     if (!NSK_JNI_VERIFY(jni, (chainObjectClass =
 228             NSK_CPP_STUB2(FindClass, jni, CHAIN_OBJECT_CLASS_NAME)) != NULL)) {
 229         nsk_jvmti_setFailStatus();
 230         return NSK_FALSE;
 231     }
 232     printf("  ... found class: 0x%p\n",
 233            (void*)chainObjectClass);
 234 
 235     if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(SetTag, jvmti, chainObjectClass, chainClassTag))) {
 236         nsk_jvmti_setFailStatus();
 237     }
 238     printf("        tag=%-5ld chainClass=0x%p\n",
 239            (long)chainClassTag, (void*)chainObjectClass);
 240 
 241     printf("Find static field in debugee class: %s\n", OBJECT_FIELD_NAME);
 242     fflush(0);
 243     if (!NSK_JNI_VERIFY(jni, (objectField =
 244             NSK_CPP_STUB4(GetStaticFieldID, jni, debugeeClass,
 245                             OBJECT_FIELD_NAME, ROOT_OBJECT_CLASS_SIG)) != NULL)) {
 246         nsk_jvmti_setFailStatus();
 247         return NSK_FALSE;
 248     }
 249     printf("  ... got fieldID: 0x%p\n", (void*)objectField);
 250 
 251     printf("Find instance field in root object class: %s\n", REACHABLE_CHAIN_FIELD_NAME);
 252     fflush(0);
 253     if (!NSK_JNI_VERIFY(jni, (reachableChainField =
 254             NSK_CPP_STUB4(GetFieldID, jni, rootObjectClass,
 255                         REACHABLE_CHAIN_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
 256         nsk_jvmti_setFailStatus();
 257         return NSK_FALSE;
 258     }
 259     printf("  ... got fieldID: 0x%p\n", (void*)reachableChainField);
 260 
 261     printf("Find instance field in root object class: %s\n", UNREACHABLE_CHAIN_FIELD_NAME);
 262     fflush(0);
 263     if (!NSK_JNI_VERIFY(jni, (unreachableChainField =
 264             NSK_CPP_STUB4(GetFieldID, jni, rootObjectClass,
 265                           UNREACHABLE_CHAIN_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
 266         nsk_jvmti_setFailStatus();
 267         return NSK_FALSE;
 268     }
 269     printf("  ... got fieldID: 0x%p\n", (void*)unreachableChainField);
 270 
 271     printf("Find instance field in chain object class: %s\n", TAIL_FIELD_NAME);
 272     fflush(0);
 273     if (!NSK_JNI_VERIFY(jni, (tailField =
 274             NSK_CPP_STUB4(GetFieldID, jni, chainObjectClass,
 275                           TAIL_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
 276         nsk_jvmti_setFailStatus();
 277         return NSK_FALSE;
 278     }
 279     printf("  ... got fieldID: 0x%p\n", (void*)tailField);
 280 
 281     printf("Get root object from static field: %s\n", OBJECT_FIELD_NAME);
 282     fflush(0);
 283     if (!NSK_JNI_VERIFY(jni, (*rootObject =
 284             NSK_CPP_STUB3(GetStaticObjectField, jni, debugeeClass,
 285                                                     objectField)) != NULL)) {
 286         nsk_jvmti_setFailStatus();
 287         return NSK_FALSE;
 288     }
 289     printf("  ... got object: 0x%p\n", (void*)*rootObject);
 290     fflush(0);
 291 
 292     if (!NSK_JNI_VERIFY(jni, (*rootObject =
 293             NSK_CPP_STUB2(NewGlobalRef, jni, *rootObject)) != NULL)) {
 294         nsk_jvmti_setFailStatus();
 295         return NSK_FALSE;
 296     }
 297     printf("  ... global ref: 0x%p\n", (void*)*rootObject);
 298 
 299     printf("Obtain and tag chain objects:\n");
 300 
 301     printf("    root tested object\n");
 302     fflush(0);
 303     if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(SetTag, jvmti, *rootObject, rootObjectTag))) {
 304         nsk_jvmti_setFailStatus();
 305     }
 306     printf("        tag=%-5ld object=0x%p\n",
 307            (long)rootObjectTag, (void*)*rootObject);
 308 
 309     /* Root object must be reported 1 time */
 310     (*objectDescList)[0].exp_found = 1;
 311     (*objectDescList)[0].tag = rootObjectTag;
 312 
 313     printf("    reachable objects chain: %d objects\n", chainLength);
 314     fflush(0);
 315     if (!getChainObjects(jvmti, jni, *rootObject,
 316                                 reachableChainField, REACHABLE_CHAIN_FIELD_NAME,
 317                                 tailField, TAIL_FIELD_NAME,
 318                                 chainLength, (*objectDescList) + 1,
 319                                 chainObjectTag, NSK_TRUE)) {
 320         nsk_jvmti_setFailStatus();
 321         return NSK_FALSE;
 322     }
 323 
 324     /* First unreachable object must be reported once
 325      * as JVMTI_HEAP_REFERENCE_STACK_LOCAL */
 326     (*objectDescList)[2 * chainLength].exp_found = 1;
 327 
 328     printf("    unreachable objects chain: %d objects\n", chainLength);
 329     if (!getChainObjects(jvmti, jni, *rootObject,
 330                                 unreachableChainField, UNREACHABLE_CHAIN_FIELD_NAME,
 331                                 tailField, TAIL_FIELD_NAME,
 332                                 chainLength, (*objectDescList) + 1 + chainLength,
 333                                 chainObjectTag, NSK_FALSE)) {
 334         nsk_jvmti_setFailStatus();
 335         return NSK_FALSE;
 336     }
 337 
 338     return NSK_TRUE;
 339 }
 340 
 341 /** Check if tagged objects were iterated. */
 342 static int checkTestedObjects(jvmtiEnv* jvmti, JNIEnv* jni,
 343                               int chainLength, ObjectDesc objectDescList[]) {
 344     int success = NSK_TRUE;
 345     int i, idx;
 346 
 347     printf("Following tagged objects were iterated:\n");
 348 
 349     printf("Root tested object:\n");
 350     printf("   tag:                 %ld\n"
 351            "   expected to iterate: %d times\n"
 352            "   iterated:            %d times\n",
 353            (long) objectDescList[0].tag,
 354                   objectDescList[0].exp_found,
 355                   objectDescList[0].found);
 356     if (objectDescList[0].found != objectDescList[0].exp_found) {
 357         NSK_COMPLAIN1("Root tested object unexpectedly iterated %d times\n",
 358                       objectDescList[0].found);
 359         nsk_jvmti_setFailStatus();
 360     }
 361 
 362     printf("\nReachable objects:\n");
 363     fflush(0);
 364     for (i = 0; i < chainLength; i++) {
 365         idx = i + 1;
 366         printf("Reachable object:\n"
 367                "   tag:                 %-3ld\n"
 368                "   expected to iterate: %d times\n"
 369                "   iterated:            %d times\n",
 370                 (long) objectDescList[idx].tag,
 371                        objectDescList[idx].exp_found,
 372                        objectDescList[idx].found);
 373         if (objectDescList[i + 1].found <= 0 && objectDescList[i + 1].exp_found > 0) {
 374             NSK_COMPLAIN0("Reachable object was not iterated\n");
 375             nsk_jvmti_setFailStatus();
 376         }
 377         if (objectDescList[idx].found != objectDescList[idx].exp_found) {
 378             NSK_COMPLAIN0("Reachable object was iterated unexpected number of times\n");
 379             nsk_jvmti_setFailStatus();
 380         }
 381     }
 382 
 383     printf("\nUnreachable objects:\n");
 384     for (i = 0; i < chainLength; i++) {
 385         idx = i + 1 + chainLength;
 386 
 387         printf("Unreachable object:\n"
 388                "   tag:                 %ld\n"
 389                "   expected to iterate: %d times\n"
 390                "   iterated:            %d times\n",
 391                 (long) objectDescList[idx].tag,
 392                        objectDescList[idx].exp_found,
 393                        objectDescList[idx].found);
 394         if (objectDescList[idx].found > 0 && objectDescList[idx].exp_found == 0) {
 395             NSK_COMPLAIN0("Unreachable object was iterated\n");
 396             nsk_jvmti_setFailStatus();
 397         }
 398         fflush(0);
 399     }
 400 
 401     return NSK_TRUE;
 402 }
 403 
 404 /** Release references to the tested objects and free allocated memory. */
 405 static int releaseTestedObjects(jvmtiEnv* jvmti, JNIEnv* jni, int chainLength,
 406                                 ObjectDesc* objectDescList, jobject rootObject) {
 407     if (rootObject != NULL) {
 408         printf("Release object reference to root tested object: 0x%p\n", rootObject);
 409         NSK_TRACE(NSK_CPP_STUB2(DeleteGlobalRef, jni, rootObject));
 410     }
 411 
 412     if (objectDescList != NULL) {
 413         printf("Deallocate objects list: 0x%p\n", (void*)objectDescList);
 414         if (!NSK_JVMTI_VERIFY(
 415             NSK_CPP_STUB2(Deallocate, jvmti, (unsigned char*)objectDescList))) {
 416             nsk_jvmti_setFailStatus();
 417         }
 418     }
 419 
 420     fflush(0);
 421     return NSK_TRUE;
 422 }
 423 
 424 /* ============================================================================= */
 425 
 426 /* Some diagnostics happen in the first FollowReferences call only */
 427 static int first_followref = 1;
 428 
 429 typedef struct ThreadDescStruct {
 430     jlong tag;
 431     jlong id;
 432 } ThreadDesc;
 433 
 434 #define MAX_THREADS 1024
 435 static ThreadDesc thrDesc [MAX_THREADS] = {};
 436 
 437 static jlong registerThread(jlong thr_id, jlong thr_tag) {
 438     if (thr_id <= 0 || thr_id >= MAX_THREADS) {
 439         NSK_COMPLAIN1("Unexpected thread ID: %ld\n", thr_id);
 440         nsk_jvmti_setFailStatus();
 441         return 0;
 442     }
 443     if (thrDesc[thr_id].id == 0) {
 444         /* need to set the first occurence info */
 445         thrDesc[thr_id].id  = thr_id;
 446         thrDesc[thr_id].tag = thr_tag;
 447     } else if (thr_tag != thrDesc[thr_id].tag) {
 448         NSK_COMPLAIN3("Thread tag doesn't match the first occurence: thr_id= %ld\n"
 449                "\t first thr_tag=%#lx, curr thr_tag=%#lx\n",
 450                thr_id, thrDesc[thr_id].tag, thr_tag);
 451         nsk_jvmti_setFailStatus();
 452         return 0;
 453     }
 454     return thr_id;
 455 } /* registerThread */
 456 
 457 typedef struct FrameDescStruct {
 458     jlong     thr_id;
 459     jint      depth;
 460     jmethodID method;
 461 } FrameDesc;
 462 
 463 #define MAX_FRAMES  256
 464 static FrameDesc frameDesc[MAX_FRAMES] = {};
 465 static int curr_frame_id = 0;  /* Index 0 should not be used */
 466 
 467 /* returns frame slot number in the table of frames */
 468 static int registerFrame(jlong thr_id, jint depth, jmethodID method,
 469                          jvmtiHeapReferenceKind ref_kind)
 470 {
 471     int idx;
 472     int failed = 0;
 473 
 474     FrameDesc *fr;
 475     if (depth < 0 || depth > MAXDEPTH) {
 476         NSK_COMPLAIN1("Incorrect frame depth: %ld\n", depth);
 477         failed = 1;
 478     }
 479     /* JNI_LOCAL references from native methods may not have a jmethodID.
 480      * (Do we have to clarify this in the JVMTI spec?)
 481      * Do not consider the test as failing in such a case.
 482      */
 483     if (method == NULL && ref_kind != JVMTI_HEAP_REFERENCE_JNI_LOCAL) {
 484         NSK_COMPLAIN0("methodID must not be NULL\n");
 485         failed = 1;
 486     }
 487     if (failed) {
 488         nsk_jvmti_setFailStatus();
 489         return 0;
 490     }
 491 
 492     /* Check if this frame was registered */
 493     for (idx = 1; idx <= curr_frame_id; idx++) {
 494         fr = &frameDesc[idx];
 495         if (fr->thr_id == thr_id && fr->depth == depth && fr->method == method) {
 496             return idx;
 497         }
 498     }
 499     if (++curr_frame_id >= MAX_FRAMES) {
 500         NSK_COMPLAIN1("Internal: Insufficient frames table size: %ld\n", MAX_FRAMES);
 501         return 0;
 502     }
 503     fr = &frameDesc[curr_frame_id];
 504     fr->thr_id = thr_id;
 505     fr->depth  = depth;
 506     fr->method = method;
 507 
 508     return curr_frame_id;
 509 } /* registerFrame */
 510 
 511 
 512 typedef struct LocalDescStruct {
 513     jint      frame_id;
 514     jlocation location;
 515     jint      slot;
 516     jlong     tag;
 517 } LocalDesc;
 518 
 519 #define MAX_LOCALS   100
 520 static LocalDesc locDesc [MAX_LOCALS] = {};
 521 static int curr_local_idx = 0;  /* Index 0 should not be used */
 522 
 523 /* returns frame slot number in the table of frames */
 524 static int registerLocal(jint frame_id, jlocation location, jint slot, jlong tag) {
 525     int idx;
 526     LocalDesc *loc;
 527     int failed = 0;
 528 
 529     if (slot < 0 || slot > MAXSLOT) {
 530         NSK_COMPLAIN1("Incorrect stack local slot#: %ld\n", slot);
 531         failed = 1;
 532     }
 533     if ((jlong) location == -1L) {
 534         NSK_COMPLAIN0("Location must not be -1\n");
 535         failed = 1;
 536     }
 537 
 538     if (failed) {
 539         nsk_jvmti_setFailStatus();
 540         return 0;
 541     }
 542 
 543     /* Check if this local was registered */
 544     for (idx = 1; idx <= curr_local_idx; idx++) {
 545         loc = &locDesc[idx];
 546         if (loc->frame_id == frame_id &&
 547             loc->slot     == slot) {
 548             if (first_followref) {
 549                 /* Do this check on the first FollowReferences call only */
 550                 FrameDesc *fr = &frameDesc[frame_id];
 551                 printf("Second report of the local: "
 552                        "loc_idx=%d, frame_id=%d, slot=%d\n",
 553                        idx, frame_id, slot);
 554                 printf("\t thr_id=%" LL "d, depth=%d, meth=0x%p\n",
 555                        fr->thr_id, fr->depth, fr->method);
 556                 failed = 1;
 557             }
 558             if (loc->tag != tag) {
 559                 NSK_COMPLAIN2("Tag mismatch:      expected %#lx, passed: %#lx\n",
 560                                loc->tag, tag);
 561                 failed = 1;
 562             }
 563             if (loc->location != location) {
 564                 NSK_COMPLAIN2("Location mismatch: expected %ld, passed: %ld\n",
 565                                (long) loc->location, (long) location);
 566                 failed = 1;
 567             }
 568             if (failed) {
 569                 nsk_jvmti_setFailStatus();
 570                 return 0;
 571             }
 572             return idx;
 573         }
 574     }
 575     if (++curr_local_idx >= MAX_LOCALS) {
 576         printf("Internal: Insufficient locals table size: %d\n", MAX_FRAMES);
 577         return 0;
 578     }
 579     loc = &locDesc[curr_local_idx];
 580     loc->frame_id = frame_id;
 581     loc->location = location;
 582     loc->slot     = slot;
 583     loc->tag      = tag;
 584 
 585     return curr_local_idx;
 586 } /* registerLocal */
 587 
 588 
 589 /** heapReferenceCallback for heap iterator. */
 590 jint JNICALL heapReferenceCallback(
 591      jvmtiHeapReferenceKind        ref_kind,
 592      const jvmtiHeapReferenceInfo* reference_info,
 593      jlong                         class_tag,
 594      jlong                         referrer_class_tag,
 595      jlong                         size,
 596      jlong*                        tag_ptr,
 597      jlong*                        referrer_tag_ptr,
 598      jint                          length,
 599      void*                         user_data)
 600 {
 601     jint depth         = -1;
 602     jint slot          = -1;
 603     jint index         = -1;
 604     jmethodID method   = (jmethodID) NULL;
 605     jlocation location = (jlocation)(-1);
 606     jlong tag          = DEREF(tag_ptr);
 607     jlong ref_tag      = DEREF(referrer_tag_ptr);
 608     jlong thr_tag      = -1;
 609     jlong thr_id       = -1;
 610     jlong thr_idx      = -1;
 611     int res            = -1;
 612     int meth_idx       = -1;
 613 
 614     switch (ref_kind) {
 615         case JVMTI_HEAP_REFERENCE_CONSTANT_POOL:
 616             index = reference_info->constant_pool.index;
 617             break;
 618         case JVMTI_HEAP_REFERENCE_FIELD:
 619         case JVMTI_HEAP_REFERENCE_STATIC_FIELD:
 620             index = reference_info->field.index;
 621             break;
 622         case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT:
 623             index = reference_info->array.index;
 624             break;
 625         case JVMTI_HEAP_REFERENCE_STACK_LOCAL:
 626             thr_tag  = reference_info->stack_local.thread_tag;
 627             thr_id   = reference_info->stack_local.thread_id;
 628             depth    = reference_info->stack_local.depth;
 629             method   = reference_info->stack_local.method;
 630             location = reference_info->stack_local.location;
 631             slot     = reference_info->stack_local.slot;
 632             index    = slot | depth << 16;
 633             break;
 634         case JVMTI_HEAP_REFERENCE_JNI_LOCAL:
 635             thr_tag  = reference_info->jni_local.thread_tag;
 636             thr_id   = reference_info->jni_local.thread_id;
 637             depth    = reference_info->jni_local.depth;
 638             method   = reference_info->jni_local.method;
 639             index    = depth;
 640             break;
 641         default:
 642             // TODO: check that realy should be done w/ other jvmtiHeapReferenceKind
 643             break;
 644     }
 645 
 646     if (ref_kind == JVMTI_HEAP_REFERENCE_OTHER      ||
 647         ref_kind == JVMTI_HEAP_REFERENCE_JNI_GLOBAL ||
 648         ref_kind == JVMTI_HEAP_REFERENCE_SYSTEM_CLASS) {
 649         return 0; /* Skip it as there is a plan to test it differently */
 650     }
 651 
 652     if (ref_kind == JVMTI_HEAP_REFERENCE_THREAD) {
 653         /* Target thread has been tagged already */
 654         if (tag == 0) {
 655             tag = *tag_ptr = thrObjectTag++;
 656             /* Just want to report new tag for thread object */
 657             printf("     heapReferenceCallback: ref=%s, tag=%-3ld, size=%-3ld\n",
 658                    ref_kind_str[ref_kind],
 659                    (long) *tag_ptr,
 660                    (long) size);
 661         }
 662 
 663         fflush(0);
 664         return 0;
 665     }
 666 
 667     printf("     heapReferenceCallback: ref=%s, class_tag=%-3ld, tag=%-3ld,"
 668            " size=%-3ld, len=%-2d\n"
 669            "\t\t ref_tag=%-" LL "d, thr_tag=%-3ld, thr_id=%" LL "d, "
 670            "meth=0x%p, loc=%ld, idx=%#x\n",
 671            ref_kind_str[ref_kind],
 672            (long) class_tag,
 673            (long) tag,
 674            (long) size,
 675            (int ) length,
 676            ref_tag,
 677            (long) thr_tag,
 678            thr_id,
 679            method,
 680            (long) location,
 681            (int ) index);
 682     fflush(0);
 683 
 684     if (tag_ptr == NULL) {
 685         NSK_COMPLAIN1("NULL tag_ptr is passed to heapReferenceCallback:"
 686                       " tag_ptr=0x%p\n", (void*)tag_ptr);
 687         nsk_jvmti_setFailStatus();
 688     }
 689 
 690     if (tag_ptr != NULL && tag != 0) {
 691         int found = 0;
 692         int i;
 693 
 694         for (i = 0; i < objectsCount; i++) {
 695             if (*tag_ptr == objectDescList[i].tag) {
 696                 found++;
 697                 objectDescList[i].found++;
 698 
 699                 if (*tag_ptr < 0 && *tag_ptr != -chainObjectTag &&
 700                     ref_kind != JVMTI_HEAP_REFERENCE_STACK_LOCAL)
 701                 {
 702                     NSK_COMPLAIN0("Unreachable tagged object is "
 703                                   "passed to heapReferenceCallback\n");
 704                     nsk_jvmti_setFailStatus();
 705                     break;
 706                 }
 707                 break;
 708             }
 709         }
 710 
 711         if (ref_kind != JVMTI_HEAP_REFERENCE_CLASS &&
 712             ref_kind != JVMTI_HEAP_REFERENCE_JNI_LOCAL && found <= 0 &&
 713             tag < FIRST_THREAD_TAG && tag > (thrObjectTag - 1))
 714         {
 715             NSK_COMPLAIN0("Unknown tagged object is passed "
 716                           "to heapReferenceCallback\n");
 717             nsk_jvmti_setFailStatus();
 718         }
 719     }
 720 
 721     if (user_data != &fakeUserData && !userDataError) {
 722        NSK_COMPLAIN2("Unexpected user_data is passed "
 723                      "to heapReferenceCallback:\n"
 724                       "   expected:       0x%p\n"
 725                       "   actual:         0x%p\n",
 726                       user_data,
 727                       &fakeUserData);
 728         nsk_jvmti_setFailStatus();
 729         userDataError++;
 730     }
 731 
 732     switch (ref_kind) {
 733         case JVMTI_HEAP_REFERENCE_CLASS: {
 734             int i;
 735             if (tag == 0) {
 736                 return 0;
 737             }
 738             if (tag != rootClassTag && tag != chainClassTag) {
 739                 NSK_COMPLAIN0("Unknown tagged class is passed "
 740                               "to heapReferenceCallback\n");
 741                 nsk_jvmti_setFailStatus();
 742                 break;
 743             }
 744             for (i = 0; i < objectsCount; i++) {
 745                if (ref_tag == objectDescList[i].tag) {
 746                    if (objectDescList[i].exp_class_tag != tag) {
 747                        NSK_COMPLAIN2("Wrong tag in heapReferenceCallback"
 748                                      "/JVMTI_HEAP_REFERENCE_CLASS:\n"
 749                                      "Expected: %-3ld\n"
 750                                      "Passed:   %-3ld\n",
 751                                       objectDescList[i].exp_class_tag,
 752                                       tag);
 753                        nsk_jvmti_setFailStatus();
 754                    }
 755                    break;
 756                }
 757             }
 758             break;
 759         }
 760 
 761         case JVMTI_HEAP_REFERENCE_STATIC_FIELD:
 762             if (tag != rootObjectTag || class_tag != rootClassTag) {
 763                  NSK_COMPLAIN1("This reference kind was not expected: %s\n",
 764                                ref_kind_str[ref_kind]);
 765                  fflush(0);
 766                  nsk_jvmti_setFailStatus();
 767             }
 768             break;
 769 
 770         case JVMTI_HEAP_REFERENCE_STACK_LOCAL:
 771             // Skip local references from non-main (e.g. compiler) threads.
 772             if (thr_tag == TARG_THREAD_TAG) {
 773                 thr_idx  = registerThread(thr_id, thr_tag);
 774                 meth_idx = registerFrame(thr_id, depth, method, ref_kind);
 775                 if (meth_idx > 0) {
 776                     jint loc_idx  = registerLocal(meth_idx, location, slot, tag);
 777                 }
 778             }
 779             /* This part is kind of hack. It has some expectations about stack layout */
 780             if (thr_tag == TARG_THREAD_TAG &&
 781                 reference_info->stack_local.depth == TARG_FRAME_DEPTH) {
 782                if (length != -1) {
 783                    jint exp_len = length;
 784 
 785                    if (reference_info->stack_local.slot == ARGV_STRING_ARR_SLOT) {
 786                        exp_len = 0;
 787                    }
 788                    else if (reference_info->stack_local.slot >= FIRST_PRIM_ARR_SLOT &&
 789                             reference_info->stack_local.slot <= LAST_PRIM_ARR_SLOT) {
 790                        exp_len = 2;
 791                    }
 792                    else if (reference_info->stack_local.slot == DUMMY_STRING_ARR_SLOT) {
 793                        exp_len = 3;
 794                    }
 795                    if (length != exp_len) {
 796                        NSK_COMPLAIN2("Wrong length of the local array:"
 797                                      " expected: %-d, found: %-d\n\n", exp_len, length);
 798                    }
 799                 } else { /* length == -1 */
 800                     if ((reference_info->stack_local.slot >= FIRST_PRIM_ARR_SLOT &&
 801                          reference_info->stack_local.slot <= DUMMY_STRING_ARR_SLOT) ||
 802                          reference_info->stack_local.slot == ARGV_STRING_ARR_SLOT) {
 803                        NSK_COMPLAIN0("Length of array must not be -1\n");
 804                     }
 805                 }
 806                if (length == 0
 807                     && reference_info->stack_local.slot != ARGV_STRING_ARR_SLOT
 808                     && reference_info->stack_local.slot < FIRST_PRIM_ARR_SLOT
 809                     && reference_info->stack_local.slot > DUMMY_STRING_ARR_SLOT) {
 810                    NSK_COMPLAIN1("Wrong length of the local variable:"
 811                                  " expected: -1, found: %-d\n\n", length);
 812                    nsk_jvmti_setFailStatus();
 813                }
 814             }
 815             break;
 816         case JVMTI_HEAP_REFERENCE_JNI_LOCAL:
 817             // Skip JNI local references from non-main (e.g. compiler) threads.
 818             if (thr_tag == TARG_THREAD_TAG) {
 819                 thr_idx  = registerThread(thr_id, thr_tag);
 820                 meth_idx = registerFrame(thr_id, depth, method, ref_kind);
 821             }
 822             break;
 823 
 824         case JVMTI_REFERENCE_ARRAY_ELEMENT:
 825         case JVMTI_HEAP_REFERENCE_JNI_GLOBAL:
 826         case JVMTI_HEAP_REFERENCE_SYSTEM_CLASS:
 827         case JVMTI_HEAP_REFERENCE_MONITOR:
 828         case JVMTI_HEAP_REFERENCE_OTHER:
 829             /* These reference kinds are expected */
 830             break;
 831 
 832         default: {
 833             NSK_COMPLAIN1("This reference kind was not expected: %s\n\n",
 834                            ref_kind_str[ref_kind]);
 835             fflush(0);
 836             nsk_jvmti_setFailStatus();
 837             break;
 838         }
 839     }
 840     return 0;
 841 }
 842 
 843 jint JNICALL primitiveFieldCallback
 844     (jvmtiHeapReferenceKind        ref_kind,
 845      const jvmtiHeapReferenceInfo* reference_info,
 846      jlong                         class_tag,
 847      jlong*                        tag_ptr,
 848      jvalue                        value,
 849      jvmtiPrimitiveType            value_type,
 850      void*                         user_data)
 851 {
 852     printf(" primitiveFieldCallback: ref=%s, class_tag=%-3ld, tag=%-3ld, type=%c\n",
 853            ref_kind_str[ref_kind],
 854            (long) class_tag,
 855            (long) DEREF(tag_ptr),
 856            (int ) value_type);
 857     fflush(0);
 858     return 0;
 859 }
 860 
 861 jint JNICALL arrayPrimitiveValueCallback
 862     (jlong class_tag, jlong size, jlong* tag_ptr, jint element_count,
 863      jvmtiPrimitiveType element_type, const void* elements, void* user_data)
 864 {
 865     printf(" arrayPrimitiveValueCallback: class_tag=%-3ld, tag=%-3ld, len=%d, type=%c\n",
 866            (long) class_tag,
 867            (long) DEREF(tag_ptr),
 868            (int ) element_count,
 869            (int ) element_type);
 870     fflush(0);
 871     return 0;
 872 }
 873 
 874 jint JNICALL stringPrimitiveValueCallback
 875     (jlong class_tag, jlong size, jlong* tag_ptr, const jchar* value,
 876      jint value_length, void* user_data)
 877 {
 878     printf("stringPrimitiveValueCallback: class_tag=%-3ld, tag=%-3ld, len=%d\n",
 879            (long) class_tag,
 880            (long) DEREF(tag_ptr),
 881            (int ) value_length);
 882     fflush(0);
 883     return 0;
 884 }
 885 
 886 
 887 /* ============================================================================= */
 888 static jthread getTargetThread(jvmtiEnv *jvmti) {
 889     static const char *target_thread_name = "main";
 890     jint i;
 891     jint thread_count = -1;
 892     jthread *threads = NULL;
 893 
 894     jvmti->GetAllThreads(&thread_count, &threads);
 895 
 896     for (i = 0; i < thread_count; i++) {
 897         jvmtiThreadInfo thread_info;
 898         jvmti->GetThreadInfo(threads[i], &thread_info);
 899 
 900         if (strcmp(thread_info.name, target_thread_name) == 0) {
 901             return threads[i];
 902         }
 903     }
 904 
 905     return NULL;
 906 }
 907 
 908 static jvmtiError setTagForTargetThread(jvmtiEnv *jvmti, jlong tag) {
 909     jthread target_thread = getTargetThread(jvmti);
 910     return jvmti->SetTag(target_thread, tag);
 911 }
 912 
 913 /** Agent algorithm. */
 914 static void JNICALL
 915 agentProc(jvmtiEnv* jvmti, JNIEnv* jni, void* arg) {
 916     jobject rootObject = NULL;
 917 
 918     printf("Wait for tested objects created\n");
 919     fflush(0);
 920     if (!NSK_VERIFY(nsk_jvmti_waitForSync(timeout))) {
 921         return;
 922     }
 923 
 924     printf(">>> Obtain and tag tested objects from debugee class\n");
 925     fflush(0);
 926     {
 927         if (!NSK_VERIFY(getTestedObjects(jvmti, jni, chainLength, &objectsCount,
 928                                          &objectDescList, &rootObject))) {
 929             return;
 930         }
 931     }
 932 
 933     printf(">>> Let debugee to clean links to unreachable objects\n");
 934     fflush(0);
 935     {
 936         if (!NSK_VERIFY(nsk_jvmti_resumeSync())) {
 937             return;
 938         }
 939         if (!NSK_VERIFY(nsk_jvmti_waitForSync(timeout))) {
 940             return;
 941         }
 942     }
 943 
 944     if (!NSK_JVMTI_VERIFY(setTagForTargetThread(jvmti, TARG_THREAD_TAG))) {
 945         nsk_jvmti_setFailStatus();
 946         return;
 947     }
 948 
 949     printf("\n\n>>> Start 1-st iteration starting from the heap root\n");
 950     fflush(0);
 951     {
 952         if (!NSK_JVMTI_VERIFY(
 953                 NSK_CPP_STUB6(FollowReferences, jvmti,
 954                     (jint)   0,     /* heap_filter    */
 955                     (jclass)  NULL, /* class          */
 956                     (jobject) NULL, /* initial_object */
 957                     &heapCallbacks,
 958                     (const void *) &fakeUserData)))
 959         {
 960              nsk_jvmti_setFailStatus();
 961              return;
 962         }
 963     }
 964 
 965     printf(">>> Check if reachable objects were iterated\n");
 966     fflush(0);
 967     {
 968         if (!checkTestedObjects(jvmti, jni, chainLength, objectDescList)) {
 969             nsk_jvmti_setFailStatus();
 970         }
 971     }
 972 
 973     {            /* Reinstall the expectations */
 974         int k;
 975         for (k = 0; k < objectsCount; k++) {
 976             (objectDescList)[k].exp_found = 0;
 977             (objectDescList)[k].found = 0;
 978         }
 979         /* Heap root object must be reported 2 times */
 980         objectDescList[0].exp_found = 2;
 981 
 982         /* First unreachable object must be reported once
 983          * as JVMTI_HEAP_REFERENCE_STACK_LOCAL */
 984         objectDescList[2 * chainLength].exp_found = 1;
 985     }
 986 
 987     printf("\n\n>>> Start 2-nd iteration starting from the heap root\n");
 988     fflush(0);
 989     first_followref = 0;
 990     {
 991         jint heap_filter = JVMTI_HEAP_FILTER_UNTAGGED
 992                          | JVMTI_HEAP_FILTER_CLASS_UNTAGGED;
 993         if (!NSK_JVMTI_VERIFY(
 994                 NSK_CPP_STUB6(FollowReferences, jvmti,
 995                     heap_filter,
 996                     (jclass)  NULL, /* class          */
 997                     (jobject) NULL, /* initial_object */
 998                     &heapCallbacks,
 999                     (const void *) &fakeUserData)))
1000         {
1001              nsk_jvmti_setFailStatus();
1002              return;
1003         }
1004     }
1005 
1006     printf(">>> Check that both reachable and unreachable "
1007            "objects were not iterated\n");
1008     fflush(0);
1009     {
1010         if (!checkTestedObjects(jvmti, jni, chainLength, objectDescList)) {
1011             nsk_jvmti_setFailStatus();
1012         }
1013     }
1014 
1015 
1016     printf(">>> Clean used data\n");
1017     fflush(0);
1018     {
1019         if (!NSK_VERIFY(releaseTestedObjects(jvmti, jni, chainLength,
1020                         objectDescList, rootObject))) {
1021             return;
1022         }
1023     }
1024 
1025     printf("Let debugee to finish\n");
1026     fflush(0);
1027     if (!NSK_VERIFY(nsk_jvmti_resumeSync()))
1028         return;
1029 }
1030 
1031 /* ============================================================================= */
1032 
1033 /** Agent library initialization. */
1034 #ifdef STATIC_BUILD
1035 JNIEXPORT jint JNICALL Agent_OnLoad_followref003(JavaVM *jvm, char *options, void *reserved) {
1036     return Agent_Initialize(jvm, options, reserved);
1037 }
1038 JNIEXPORT jint JNICALL Agent_OnAttach_followref003(JavaVM *jvm, char *options, void *reserved) {
1039     return Agent_Initialize(jvm, options, reserved);
1040 }
1041 JNIEXPORT jint JNI_OnLoad_followref003(JavaVM *jvm, char *options, void *reserved) {
1042     return JNI_VERSION_1_8;
1043 }
1044 #endif
1045 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
1046     jvmtiEnv* jvmti = NULL;
1047 
1048     if (!NSK_VERIFY(nsk_jvmti_parseOptions(options)))
1049         return JNI_ERR;
1050 
1051     timeout = nsk_jvmti_getWaitTime() * 60 * 1000;
1052 
1053     {
1054         const char* infoOpt = nsk_jvmti_findOptionValue("info");
1055         if (infoOpt != NULL) {
1056             if (strcmp(infoOpt, "none") == 0)
1057                 info = INFO_NONE;
1058             else if (strcmp(infoOpt, "all") == 0)
1059                 info = INFO_ALL;
1060             else if (strcmp(infoOpt, "objref") == 0)
1061                 info = INFO_OBJREF;
1062             else if (strcmp(infoOpt, "stackref") == 0)
1063                 info = INFO_STACKREF;
1064             else if (strcmp(infoOpt, "heaproot") == 0)
1065                 info = INFO_HEAPROOT;
1066             else if (strcmp(infoOpt, "heapobj") == 0)
1067                 info = INFO_HEAPOBJ;
1068             else {
1069                 printf("Unknown option value: info=%s\n", infoOpt);
1070                 fflush(0);
1071                 return JNI_ERR;
1072             }
1073         }
1074     }
1075 
1076     chainLength = nsk_jvmti_findOptionIntValue("objects", DEFAULT_CHAIN_LENGTH);
1077     if (!NSK_VERIFY(chainLength > 0))
1078         return JNI_ERR;
1079 
1080     if (!NSK_VERIFY((jvmti =
1081             nsk_jvmti_createJVMTIEnv(jvm, reserved)) != NULL))
1082         return JNI_ERR;
1083 
1084     {
1085         jvmtiCapabilities caps;
1086 
1087         memset(&caps, 0, sizeof(caps));
1088         caps.can_tag_objects = 1;
1089         if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(AddCapabilities, jvmti, &caps))) {
1090             return JNI_ERR;
1091         }
1092     }
1093 
1094     /* Setting Heap Callbacks */
1095     heapCallbacks.heap_iteration_callback         = NULL;
1096     heapCallbacks.heap_reference_callback         = heapReferenceCallback;
1097     heapCallbacks.primitive_field_callback        = primitiveFieldCallback;
1098     heapCallbacks.array_primitive_value_callback  = arrayPrimitiveValueCallback;
1099     heapCallbacks.string_primitive_value_callback = stringPrimitiveValueCallback;
1100 
1101     if (!NSK_VERIFY(nsk_jvmti_setAgentProc(agentProc, NULL)))
1102         return JNI_ERR;
1103 
1104     return JNI_OK;
1105 }
1106 
1107 /* ============================================================================= */
1108 
1109 }