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