test/hotspot/jtreg/vmTestbase/nsk/jvmti/unit/FollowReferences/followref003/followref003.c
changeset 50260 46c67f5e27c2
child 51492 fc80fa0ecac8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/vmTestbase/nsk/jvmti/unit/FollowReferences/followref003/followref003.c	Thu May 24 17:12:15 2018 -0700
@@ -0,0 +1,1107 @@
+/*
+ * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <string.h>
+#include "jvmti.h"
+#include "agent_common.h"
+#include "jni_tools.h"
+#include "jvmti_tools.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ============================================================================= */
+
+static jlong timeout = 0;
+
+#define INFO_NONE       0x00
+#define INFO_ALL        0xFF
+#define INFO_OBJREF     0x01
+#define INFO_STACKREF   0x02
+#define INFO_HEAPROOT   0x04
+#define INFO_HEAPOBJ    0x08
+
+static unsigned int info = INFO_ALL;
+
+#define DEBUGEE_CLASS_NAME      "nsk/jvmti/unit/FollowReferences/followref003"
+#define ROOT_OBJECT_CLASS_NAME  "nsk/jvmti/unit/FollowReferences/followref003RootTestedClass"
+#define ROOT_OBJECT_CLASS_SIG   "L"ROOT_OBJECT_CLASS_NAME";"
+#define CHAIN_OBJECT_CLASS_NAME "nsk/jvmti/unit/FollowReferences/followref003TestedClass"
+#define CHAIN_OBJECT_CLASS_SIG  "L"CHAIN_OBJECT_CLASS_NAME";"
+
+#define OBJECT_FIELD_NAME               "object"
+#define REACHABLE_CHAIN_FIELD_NAME      "reachableChain"
+#define UNREACHABLE_CHAIN_FIELD_NAME    "unreachableChain"
+#define TAIL_FIELD_NAME                 "tail"
+
+
+#define DEFAULT_CHAIN_LENGTH 3
+#define MAXDEPTH 50
+#define MAXSLOT  16
+
+typedef struct ObjectDescStruct {
+    jlong tag;
+    jlong class_tag;
+    jlong exp_class_tag;
+    jint exp_found;
+    jint found;
+} ObjectDesc;
+
+static int chainLength   = 0;
+static int objectsCount  = 0;
+static int fakeUserData  = 0;
+static int userDataError = 0;
+
+static ObjectDesc* objectDescList = NULL;
+
+#define TARG_THREAD_TAG  11
+#define FIRST_THREAD_TAG (TARG_THREAD_TAG + 1)
+
+#define TARG_FRAME_DEPTH  1
+
+static jlong rootClassTag   = 9;
+static jlong chainClassTag  = 99;
+static jlong thrObjectTag   = FIRST_THREAD_TAG;
+static jlong rootObjectTag  = 55;
+static jlong chainObjectTag = 100;
+
+
+/* Java method frame slots interesting to check */
+#define ARGV_STRING_ARR_SLOT   1
+#define FIRST_PRIM_ARR_SLOT    3
+#define LAST_PRIM_ARR_SLOT     10
+#define DUMMY_STRING_ARR_SLOT  11
+
+
+static jvmtiHeapCallbacks heapCallbacks = {0};
+
+static const char* ref_kind_str[28] = {
+   "unknown_0",
+   "REFERENCE_CLASS",
+   "REFERENCE_FIELD",
+   "REFERENCE_ARRAY_ELEMENT",
+   "REFERENCE_CLASS_LOADER",
+   "REFERENCE_SIGNERS",
+   "REFERENCE_PROTECTION_DOMAIN",
+   "REFERENCE_INTERFACE",
+   "REFERENCE_STATIC_FIELD",
+   "REFERENCE_CONSTANT_POOL",
+   "unknown_10", "unknown_11", "unknown_12",
+   "unknown_13", "unknown_14", "unknown_15", "unknown_16",
+   "unknown_17", "unknown_18", "unknown_19", "unknown_20",
+   "REFERENCE_JNI_GLOBAL",
+   "REFERENCE_SYSTEM_CLASS",
+   "REFERENCE_MONITOR",
+   "REFERENCE_STACK_LOCAL",
+   "REFERENCE_JNI_LOCAL",
+   "REFERENCE_THREAD",
+   "REFERENCE_OTHER"
+};
+
+
+#define DEREF(ptr) (((ptr) == NULL ? 0 : *(ptr)))
+
+
+/* ============================================================================= */
+
+/** Obtain chain of tested objects and tag them recursively. */
+static int getChainObjects(jvmtiEnv* jvmti, JNIEnv* jni, jobject firstObject,
+                           jfieldID firstField, const char firstFieldName[],
+                           jfieldID nextField, const char nextFieldName[],
+                           int count, ObjectDesc objectDescList[],
+                           jlong tag, int reachable) {
+    jobject obj = NULL;
+    jlong objTag = (reachable ? tag : -tag);
+
+    if (count <= 0)
+        return NSK_TRUE;
+
+    count--;
+    tag++;
+
+    if (!NSK_JNI_VERIFY(jni, (obj =
+            NSK_CPP_STUB3(GetObjectField, jni, firstObject, firstField)) != NULL)) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+
+    objectDescList[count].tag = objTag;
+
+    if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(SetTag, jvmti, obj, objTag))) {
+        nsk_jvmti_setFailStatus();
+    }
+    printf("        tag=%-5ld object=0x%p\n", (long)objTag, (void*)obj);
+    fflush(0);
+    if (!getChainObjects(jvmti, jni, obj, nextField, nextFieldName,
+                                nextField, nextFieldName,
+                                count, objectDescList, tag, reachable)) {
+        return NSK_FALSE;
+    }
+
+    NSK_TRACE(NSK_CPP_STUB2(DeleteLocalRef, jni, obj));
+    return NSK_TRUE;
+}
+
+/** Obtain all tested objects from debugee class and tag them recursively. */
+static int getTestedObjects(jvmtiEnv* jvmti, JNIEnv* jni, int chainLength,
+                            int *objectsCount, ObjectDesc* *objectDescList,
+                            jobject* rootObject) {
+    jclass debugeeClass = NULL;
+    jclass rootObjectClass = NULL;
+    jclass chainObjectClass = NULL;
+
+    jfieldID objectField = NULL;
+    jfieldID reachableChainField = NULL;
+    jfieldID unreachableChainField = NULL;
+    jfieldID tailField = NULL;
+
+    *objectsCount = 1 + 2 * chainLength;
+
+    printf("Allocate memory for objects list: %d objects\n", *objectsCount);
+    fflush(0);
+    if (!NSK_JVMTI_VERIFY(
+            NSK_CPP_STUB3(Allocate, jvmti, (*objectsCount * sizeof(ObjectDesc)),
+                                             (unsigned char**)objectDescList))) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+    printf("  ... allocated array: 0x%p\n", (void*)objectDescList);
+    fflush(0);
+
+    {
+        int k;
+        for (k = 0; k < *objectsCount; k++) {
+            (*objectDescList)[k].tag = 0;
+            (*objectDescList)[k].exp_class_tag = chainClassTag;
+            (*objectDescList)[k].exp_found = 0;
+            (*objectDescList)[k].found = 0;
+        }
+    }
+    (*objectDescList)[0].exp_class_tag = rootClassTag;
+
+    printf("Find debugee class: %s\n", DEBUGEE_CLASS_NAME);
+    fflush(0);
+    if (!NSK_JNI_VERIFY(jni, (debugeeClass =
+            NSK_CPP_STUB2(FindClass, jni, DEBUGEE_CLASS_NAME)) != NULL)) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+    printf("  ... found class: 0x%p\n", (void*)debugeeClass);
+
+    printf("Find root object class: %s\n", ROOT_OBJECT_CLASS_NAME);
+    fflush(0);
+    if (!NSK_JNI_VERIFY(jni, (rootObjectClass =
+            NSK_CPP_STUB2(FindClass, jni, ROOT_OBJECT_CLASS_NAME)) != NULL)) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+    printf("  ... found class: 0x%p\n", (void*)rootObjectClass);
+
+    if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(SetTag, jvmti, rootObjectClass, rootClassTag))) {
+        nsk_jvmti_setFailStatus();
+    }
+    printf("        tag=%-5ld rootClass=0x%p\n",
+           (long)rootClassTag, (void*)rootObjectClass);
+
+    printf("Find chain object class: %s\n", CHAIN_OBJECT_CLASS_NAME);
+    fflush(0);
+    if (!NSK_JNI_VERIFY(jni, (chainObjectClass =
+            NSK_CPP_STUB2(FindClass, jni, CHAIN_OBJECT_CLASS_NAME)) != NULL)) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+    printf("  ... found class: 0x%p\n",
+           (void*)chainObjectClass);
+
+    if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(SetTag, jvmti, chainObjectClass, chainClassTag))) {
+        nsk_jvmti_setFailStatus();
+    }
+    printf("        tag=%-5ld chainClass=0x%p\n",
+           (long)chainClassTag, (void*)chainObjectClass);
+
+    printf("Find static field in debugee class: %s\n", OBJECT_FIELD_NAME);
+    fflush(0);
+    if (!NSK_JNI_VERIFY(jni, (objectField =
+            NSK_CPP_STUB4(GetStaticFieldID, jni, debugeeClass,
+                            OBJECT_FIELD_NAME, ROOT_OBJECT_CLASS_SIG)) != NULL)) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+    printf("  ... got fieldID: 0x%p\n", (void*)objectField);
+
+    printf("Find instance field in root object class: %s\n", REACHABLE_CHAIN_FIELD_NAME);
+    fflush(0);
+    if (!NSK_JNI_VERIFY(jni, (reachableChainField =
+            NSK_CPP_STUB4(GetFieldID, jni, rootObjectClass,
+                        REACHABLE_CHAIN_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+    printf("  ... got fieldID: 0x%p\n", (void*)reachableChainField);
+
+    printf("Find instance field in root object class: %s\n", UNREACHABLE_CHAIN_FIELD_NAME);
+    fflush(0);
+    if (!NSK_JNI_VERIFY(jni, (unreachableChainField =
+            NSK_CPP_STUB4(GetFieldID, jni, rootObjectClass,
+                          UNREACHABLE_CHAIN_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+    printf("  ... got fieldID: 0x%p\n", (void*)unreachableChainField);
+
+    printf("Find instance field in chain object class: %s\n", TAIL_FIELD_NAME);
+    fflush(0);
+    if (!NSK_JNI_VERIFY(jni, (tailField =
+            NSK_CPP_STUB4(GetFieldID, jni, chainObjectClass,
+                          TAIL_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+    printf("  ... got fieldID: 0x%p\n", (void*)tailField);
+
+    printf("Get root object from static field: %s\n", OBJECT_FIELD_NAME);
+    fflush(0);
+    if (!NSK_JNI_VERIFY(jni, (*rootObject =
+            NSK_CPP_STUB3(GetStaticObjectField, jni, debugeeClass,
+                                                    objectField)) != NULL)) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+    printf("  ... got object: 0x%p\n", (void*)*rootObject);
+    fflush(0);
+
+    if (!NSK_JNI_VERIFY(jni, (*rootObject =
+            NSK_CPP_STUB2(NewGlobalRef, jni, *rootObject)) != NULL)) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+    printf("  ... global ref: 0x%p\n", (void*)*rootObject);
+
+    printf("Obtain and tag chain objects:\n");
+
+    printf("    root tested object\n");
+    fflush(0);
+    if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB3(SetTag, jvmti, *rootObject, rootObjectTag))) {
+        nsk_jvmti_setFailStatus();
+    }
+    printf("        tag=%-5ld object=0x%p\n",
+           (long)rootObjectTag, (void*)*rootObject);
+
+    /* Root object must be reported 1 time */
+    (*objectDescList)[0].exp_found = 1;
+    (*objectDescList)[0].tag = rootObjectTag;
+
+    printf("    reachable objects chain: %d objects\n", chainLength);
+    fflush(0);
+    if (!getChainObjects(jvmti, jni, *rootObject,
+                                reachableChainField, REACHABLE_CHAIN_FIELD_NAME,
+                                tailField, TAIL_FIELD_NAME,
+                                chainLength, (*objectDescList) + 1,
+                                chainObjectTag, NSK_TRUE)) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+
+    /* First unreachable object must be reported once
+     * as JVMTI_HEAP_REFERENCE_STACK_LOCAL */
+    (*objectDescList)[2 * chainLength].exp_found = 1;
+
+    printf("    unreachable objects chain: %d objects\n", chainLength);
+    if (!getChainObjects(jvmti, jni, *rootObject,
+                                unreachableChainField, UNREACHABLE_CHAIN_FIELD_NAME,
+                                tailField, TAIL_FIELD_NAME,
+                                chainLength, (*objectDescList) + 1 + chainLength,
+                                chainObjectTag, NSK_FALSE)) {
+        nsk_jvmti_setFailStatus();
+        return NSK_FALSE;
+    }
+
+    return NSK_TRUE;
+}
+
+/** Check if tagged objects were iterated. */
+static int checkTestedObjects(jvmtiEnv* jvmti, JNIEnv* jni,
+                              int chainLength, ObjectDesc objectDescList[]) {
+    int success = NSK_TRUE;
+    int i, idx;
+
+    printf("Following tagged objects were iterated:\n");
+
+    printf("Root tested object:\n");
+    printf("   tag:                 %ld\n"
+           "   expected to iterate: %d times\n"
+           "   iterated:            %d times\n",
+           (long) objectDescList[0].tag,
+                  objectDescList[0].exp_found,
+                  objectDescList[0].found);
+    if (objectDescList[0].found != objectDescList[0].exp_found) {
+        NSK_COMPLAIN1("Root tested object unexpectedly iterated %d times\n",
+                      objectDescList[0].found);
+        nsk_jvmti_setFailStatus();
+    }
+
+    printf("\nReachable objects:\n");
+    fflush(0);
+    for (i = 0; i < chainLength; i++) {
+        idx = i + 1;
+        printf("Reachable object:\n"
+               "   tag:                 %-3ld\n"
+               "   expected to iterate: %d times\n"
+               "   iterated:            %d times\n",
+                (long) objectDescList[idx].tag,
+                       objectDescList[idx].exp_found,
+                       objectDescList[idx].found);
+        if (objectDescList[i + 1].found <= 0 && objectDescList[i + 1].exp_found > 0) {
+            NSK_COMPLAIN0("Reachable object was not iterated\n");
+            nsk_jvmti_setFailStatus();
+        }
+        if (objectDescList[idx].found != objectDescList[idx].exp_found) {
+            NSK_COMPLAIN0("Reachable object was iterated unexpected number of times\n");
+            nsk_jvmti_setFailStatus();
+        }
+    }
+
+    printf("\nUnreachable objects:\n");
+    for (i = 0; i < chainLength; i++) {
+        idx = i + 1 + chainLength;
+
+        printf("Unreachable object:\n"
+               "   tag:                 %ld\n"
+               "   expected to iterate: %d times\n"
+               "   iterated:            %d times\n",
+                (long) objectDescList[idx].tag,
+                       objectDescList[idx].exp_found,
+                       objectDescList[idx].found);
+        if (objectDescList[idx].found > 0 && objectDescList[idx].exp_found == 0) {
+            NSK_COMPLAIN0("Unreachable object was iterated\n");
+            nsk_jvmti_setFailStatus();
+        }
+        fflush(0);
+    }
+
+    return NSK_TRUE;
+}
+
+/** Release references to the tested objects and free allocated memory. */
+static int releaseTestedObjects(jvmtiEnv* jvmti, JNIEnv* jni, int chainLength,
+                                ObjectDesc* objectDescList, jobject rootObject) {
+    if (rootObject != NULL) {
+        printf("Release object reference to root tested object: 0x%p\n", rootObject);
+        NSK_TRACE(NSK_CPP_STUB2(DeleteGlobalRef, jni, rootObject));
+    }
+
+    if (objectDescList != NULL) {
+        printf("Deallocate objects list: 0x%p\n", (void*)objectDescList);
+        if (!NSK_JVMTI_VERIFY(
+            NSK_CPP_STUB2(Deallocate, jvmti, (unsigned char*)objectDescList))) {
+            nsk_jvmti_setFailStatus();
+        }
+    }
+
+    fflush(0);
+    return NSK_TRUE;
+}
+
+/* ============================================================================= */
+
+/* Some diagnostics happen in the first FollowReferences call only */
+static int first_followref = 1;
+
+typedef struct ThreadDescStruct {
+    jlong tag;
+    jlong id;
+} ThreadDesc;
+
+#define MAX_THREADS 1024
+static ThreadDesc thrDesc [MAX_THREADS] = {{0}};
+
+static jlong registerThread(jlong thr_id, jlong thr_tag) {
+    if (thr_id <= 0 || thr_id >= MAX_THREADS) {
+        NSK_COMPLAIN1("Unexpected thread ID: %ld\n", thr_id);
+        nsk_jvmti_setFailStatus();
+        return 0;
+    }
+    if (thrDesc[thr_id].id == 0) {
+        /* need to set the first occurence info */
+        thrDesc[thr_id].id  = thr_id;
+        thrDesc[thr_id].tag = thr_tag;
+    } else if (thr_tag != thrDesc[thr_id].tag) {
+        NSK_COMPLAIN3("Thread tag doesn't match the first occurence: thr_id= %ld\n"
+               "\t first thr_tag=%#lx, curr thr_tag=%#lx\n",
+               thr_id, thrDesc[thr_id].tag, thr_tag);
+        nsk_jvmti_setFailStatus();
+        return 0;
+    }
+    return thr_id;
+} /* registerThread */
+
+typedef struct FrameDescStruct {
+    jlong     thr_id;
+    jint      depth;
+    jmethodID method;
+} FrameDesc;
+
+#define MAX_FRAMES  256
+static FrameDesc frameDesc[MAX_FRAMES] = {{0}};
+static int curr_frame_id = 0;  /* Index 0 should not be used */
+
+/* returns frame slot number in the table of frames */
+static int registerFrame(jlong thr_id, jint depth, jmethodID method,
+                         jvmtiHeapReferenceKind ref_kind)
+{
+    int idx;
+    int failed = 0;
+
+    FrameDesc *fr;
+    if (depth < 0 || depth > MAXDEPTH) {
+        NSK_COMPLAIN1("Incorrect frame depth: %ld\n", depth);
+        failed = 1;
+    }
+    /* JNI_LOCAL references from native methods may not have a jmethodID.
+     * (Do we have to clarify this in the JVMTI spec?)
+     * Do not consider the test as failing in such a case.
+     */
+    if (method == NULL && ref_kind != JVMTI_HEAP_REFERENCE_JNI_LOCAL) {
+        NSK_COMPLAIN0("methodID must not be NULL\n");
+        failed = 1;
+    }
+    if (failed) {
+        nsk_jvmti_setFailStatus();
+        return 0;
+    }
+
+    /* Check if this frame was registered */
+    for (idx = 1; idx <= curr_frame_id; idx++) {
+        fr = &frameDesc[idx];
+        if (fr->thr_id == thr_id && fr->depth == depth && fr->method == method) {
+            return idx;
+        }
+    }
+    if (++curr_frame_id >= MAX_FRAMES) {
+        NSK_COMPLAIN1("Internal: Insufficient frames table size: %ld\n", MAX_FRAMES);
+        return 0;
+    }
+    fr = &frameDesc[curr_frame_id];
+    fr->thr_id = thr_id;
+    fr->depth  = depth;
+    fr->method = method;
+
+    return curr_frame_id;
+} /* registerFrame */
+
+
+typedef struct LocalDescStruct {
+    jint      frame_id;
+    jlocation location;
+    jint      slot;
+    jlong     tag;
+} LocalDesc;
+
+#define MAX_LOCALS   100
+static LocalDesc locDesc [MAX_LOCALS] = {{0}};
+static int curr_local_idx = 0;  /* Index 0 should not be used */
+
+/* returns frame slot number in the table of frames */
+static int registerLocal(jint frame_id, jlocation location, jint slot, jlong tag) {
+    int idx;
+    LocalDesc *loc;
+    int failed = 0;
+
+    if (slot < 0 || slot > MAXSLOT) {
+        NSK_COMPLAIN1("Incorrect stack local slot#: %ld\n", slot);
+        failed = 1;
+    }
+    if ((jlong) location == -1L) {
+        NSK_COMPLAIN0("Location must not be -1\n");
+        failed = 1;
+    }
+
+    if (failed) {
+        nsk_jvmti_setFailStatus();
+        return 0;
+    }
+
+    /* Check if this local was registered */
+    for (idx = 1; idx <= curr_local_idx; idx++) {
+        loc = &locDesc[idx];
+        if (loc->frame_id == frame_id &&
+            loc->slot     == slot) {
+            if (first_followref) {
+                /* Do this check on the first FollowReferences call only */
+                FrameDesc *fr = &frameDesc[frame_id];
+                printf("Second report of the local: "
+                       "loc_idx=%d, frame_id=%d, slot=%d\n",
+                       idx, frame_id, slot);
+                printf("\t thr_id=%"LL"d, depth=%d, meth=0x%p\n",
+                       fr->thr_id, fr->depth, fr->method);
+                failed = 1;
+            }
+            if (loc->tag != tag) {
+                NSK_COMPLAIN2("Tag mismatch:      expected %#lx, passed: %#lx\n",
+                               loc->tag, tag);
+                failed = 1;
+            }
+            if (loc->location != location) {
+                NSK_COMPLAIN2("Location mismatch: expected %ld, passed: %ld\n",
+                               (long) loc->location, (long) location);
+                failed = 1;
+            }
+            if (failed) {
+                nsk_jvmti_setFailStatus();
+                return 0;
+            }
+            return idx;
+        }
+    }
+    if (++curr_local_idx >= MAX_LOCALS) {
+        printf("Internal: Insufficient locals table size: %d\n", MAX_FRAMES);
+        return 0;
+    }
+    loc = &locDesc[curr_local_idx];
+    loc->frame_id = frame_id;
+    loc->location = location;
+    loc->slot     = slot;
+    loc->tag      = tag;
+
+    return curr_local_idx;
+} /* registerLocal */
+
+
+/** heapReferenceCallback for heap iterator. */
+jint JNICALL heapReferenceCallback(
+     jvmtiHeapReferenceKind        ref_kind,
+     const jvmtiHeapReferenceInfo* reference_info,
+     jlong                         class_tag,
+     jlong                         referrer_class_tag,
+     jlong                         size,
+     jlong*                        tag_ptr,
+     jlong*                        referrer_tag_ptr,
+     jint                          length,
+     void*                         user_data)
+{
+    jint depth         = -1;
+    jint slot          = -1;
+    jint index         = -1;
+    jmethodID method   = (jmethodID) NULL;
+    jlocation location = (jlocation)(-1);
+    jlong tag          = DEREF(tag_ptr);
+    jlong ref_tag      = DEREF(referrer_tag_ptr);
+    jlong thr_tag      = -1;
+    jlong thr_id       = -1;
+    jlong thr_idx      = -1;
+    int res            = -1;
+    int meth_idx       = -1;
+
+    switch (ref_kind) {
+        case JVMTI_HEAP_REFERENCE_CONSTANT_POOL:
+            index = reference_info->constant_pool.index;
+            break;
+        case JVMTI_HEAP_REFERENCE_FIELD:
+        case JVMTI_HEAP_REFERENCE_STATIC_FIELD:
+            index = reference_info->field.index;
+            break;
+        case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT:
+            index = reference_info->array.index;
+            break;
+        case JVMTI_HEAP_REFERENCE_STACK_LOCAL:
+            thr_tag  = reference_info->stack_local.thread_tag;
+            thr_id   = reference_info->stack_local.thread_id;
+            depth    = reference_info->stack_local.depth;
+            method   = reference_info->stack_local.method;
+            location = reference_info->stack_local.location;
+            slot     = reference_info->stack_local.slot;
+            index    = slot | depth << 16;
+            break;
+        case JVMTI_HEAP_REFERENCE_JNI_LOCAL:
+            thr_tag  = reference_info->jni_local.thread_tag;
+            thr_id   = reference_info->jni_local.thread_id;
+            depth    = reference_info->jni_local.depth;
+            method   = reference_info->jni_local.method;
+            index    = depth;
+            break;
+        default:
+            // TODO: check that realy should be done w/ other jvmtiHeapReferenceKind
+            break;
+    }
+
+    if (ref_kind == JVMTI_HEAP_REFERENCE_OTHER      ||
+        ref_kind == JVMTI_HEAP_REFERENCE_JNI_GLOBAL ||
+        ref_kind == JVMTI_HEAP_REFERENCE_SYSTEM_CLASS) {
+        return 0; /* Skip it as there is a plan to test it differently */
+    }
+
+    if (ref_kind == JVMTI_HEAP_REFERENCE_THREAD) {
+        /* Target thread has been tagged already */
+        if (tag == 0) {
+            tag = *tag_ptr = thrObjectTag++;
+            /* Just want to report new tag for thread object */
+            printf("     heapReferenceCallback: ref=%s, tag=%-3ld, size=%-3ld\n",
+                   ref_kind_str[ref_kind],
+                   (long) *tag_ptr,
+                   (long) size);
+        }
+
+        fflush(0);
+        return 0;
+    }
+
+    printf("     heapReferenceCallback: ref=%s, class_tag=%-3ld, tag=%-3ld,"
+           " size=%-3ld, len=%-2d\n"
+           "\t\t ref_tag=%-"LL"d, thr_tag=%-3ld, thr_id=%"LL"d, "
+           "meth=0x%p, loc=%ld, idx=%#x\n",
+           ref_kind_str[ref_kind],
+           (long) class_tag,
+           (long) tag,
+           (long) size,
+           (int ) length,
+           ref_tag,
+           (long) thr_tag,
+           thr_id,
+           method,
+           (long) location,
+           (int ) index);
+    fflush(0);
+
+    if (tag_ptr == NULL) {
+        NSK_COMPLAIN1("NULL tag_ptr is passed to heapReferenceCallback:"
+                      " tag_ptr=0x%p\n", (void*)tag_ptr);
+        nsk_jvmti_setFailStatus();
+    }
+
+    if (tag_ptr != NULL && tag != 0) {
+        int found = 0;
+        int i;
+
+        for (i = 0; i < objectsCount; i++) {
+            if (*tag_ptr == objectDescList[i].tag) {
+                found++;
+                objectDescList[i].found++;
+
+                if (*tag_ptr < 0 && *tag_ptr != -chainObjectTag &&
+                    ref_kind != JVMTI_HEAP_REFERENCE_STACK_LOCAL)
+                {
+                    NSK_COMPLAIN0("Unreachable tagged object is "
+                                  "passed to heapReferenceCallback\n");
+                    nsk_jvmti_setFailStatus();
+                    break;
+                }
+                break;
+            }
+        }
+
+        if (ref_kind != JVMTI_HEAP_REFERENCE_CLASS &&
+            ref_kind != JVMTI_HEAP_REFERENCE_JNI_LOCAL && found <= 0 &&
+            tag < FIRST_THREAD_TAG && tag > (thrObjectTag - 1))
+        {
+            NSK_COMPLAIN0("Unknown tagged object is passed "
+                          "to heapReferenceCallback\n");
+            nsk_jvmti_setFailStatus();
+        }
+    }
+
+    if (user_data != &fakeUserData && !userDataError) {
+       NSK_COMPLAIN2("Unexpected user_data is passed "
+                     "to heapReferenceCallback:\n"
+                      "   expected:       0x%p\n"
+                      "   actual:         0x%p\n",
+                      user_data,
+                      &fakeUserData);
+        nsk_jvmti_setFailStatus();
+        userDataError++;
+    }
+
+    switch (ref_kind) {
+        case JVMTI_HEAP_REFERENCE_CLASS: {
+            int i;
+            if (tag == 0) {
+                return 0;
+            }
+            if (tag != rootClassTag && tag != chainClassTag) {
+                NSK_COMPLAIN0("Unknown tagged class is passed "
+                              "to heapReferenceCallback\n");
+                nsk_jvmti_setFailStatus();
+                break;
+            }
+            for (i = 0; i < objectsCount; i++) {
+               if (ref_tag == objectDescList[i].tag) {
+                   if (objectDescList[i].exp_class_tag != tag) {
+                       NSK_COMPLAIN2("Wrong tag in heapReferenceCallback"
+                                     "/JVMTI_HEAP_REFERENCE_CLASS:\n"
+                                     "Expected: %-3ld\n"
+                                     "Passed:   %-3ld\n",
+                                      objectDescList[i].exp_class_tag,
+                                      tag);
+                       nsk_jvmti_setFailStatus();
+                   }
+                   break;
+               }
+            }
+            break;
+        }
+
+        case JVMTI_HEAP_REFERENCE_STATIC_FIELD:
+            if (tag != rootObjectTag || class_tag != rootClassTag) {
+                 NSK_COMPLAIN1("This reference kind was not expected: %s\n",
+                               ref_kind_str[ref_kind]);
+                 fflush(0);
+                 nsk_jvmti_setFailStatus();
+            }
+            break;
+
+        case JVMTI_HEAP_REFERENCE_STACK_LOCAL:
+            thr_idx  = registerThread(thr_id, thr_tag);
+            meth_idx = registerFrame(thr_id, depth, method, ref_kind);
+            if (meth_idx > 0) {
+                jint loc_idx  = registerLocal(meth_idx, location, slot, tag);
+            }
+            /* This part is kind of hack. It has some expectations about stack layout */
+            if (thr_tag == TARG_THREAD_TAG &&
+                reference_info->stack_local.depth == TARG_FRAME_DEPTH) {
+               if (length != -1) {
+                   jint exp_len = length;
+
+                   if (reference_info->stack_local.slot == ARGV_STRING_ARR_SLOT) {
+                       exp_len = 0;
+                   }
+                   else if (reference_info->stack_local.slot >= FIRST_PRIM_ARR_SLOT &&
+                            reference_info->stack_local.slot <= LAST_PRIM_ARR_SLOT) {
+                       exp_len = 2;
+                   }
+                   else if (reference_info->stack_local.slot == DUMMY_STRING_ARR_SLOT) {
+                       exp_len = 3;
+                   }
+                   if (length != exp_len) {
+                       NSK_COMPLAIN2("Wrong length of the local array:"
+                                     " expected: %-d, found: %-d\n\n", exp_len, length);
+                   }
+                } else { /* length == -1 */
+                    if ((reference_info->stack_local.slot >= FIRST_PRIM_ARR_SLOT &&
+                         reference_info->stack_local.slot <= DUMMY_STRING_ARR_SLOT) ||
+                         reference_info->stack_local.slot == ARGV_STRING_ARR_SLOT) {
+                       NSK_COMPLAIN0("Length of array must not be -1\n");
+                    }
+                }
+               if (length == 0
+                    && reference_info->stack_local.slot != ARGV_STRING_ARR_SLOT
+                    && reference_info->stack_local.slot < FIRST_PRIM_ARR_SLOT
+                    && reference_info->stack_local.slot > DUMMY_STRING_ARR_SLOT) {
+                   NSK_COMPLAIN1("Wrong length of the local variable:"
+                                 " expected: -1, found: %-d\n\n", length);
+                   nsk_jvmti_setFailStatus();
+               }
+            }
+            /* Fall through */
+        case JVMTI_HEAP_REFERENCE_JNI_LOCAL:
+            thr_idx  = registerThread(thr_id, thr_tag);
+            meth_idx = registerFrame(thr_id, depth, method, ref_kind);
+            break;
+
+        case JVMTI_REFERENCE_ARRAY_ELEMENT:
+        case JVMTI_HEAP_REFERENCE_JNI_GLOBAL:
+        case JVMTI_HEAP_REFERENCE_SYSTEM_CLASS:
+        case JVMTI_HEAP_REFERENCE_MONITOR:
+        case JVMTI_HEAP_REFERENCE_OTHER:
+            /* These reference kinds are expected */
+            break;
+
+        default: {
+            NSK_COMPLAIN1("This reference kind was not expected: %s\n\n",
+                           ref_kind_str[ref_kind]);
+            fflush(0);
+            nsk_jvmti_setFailStatus();
+            break;
+        }
+    }
+    return 0;
+}
+
+jint JNICALL primitiveFieldCallback
+    (jvmtiHeapReferenceKind        ref_kind,
+     const jvmtiHeapReferenceInfo* reference_info,
+     jlong                         class_tag,
+     jlong*                        tag_ptr,
+     jvalue                        value,
+     jvmtiPrimitiveType            value_type,
+     void*                         user_data)
+{
+    printf(" primitiveFieldCallback: ref=%s, class_tag=%-3ld, tag=%-3ld, type=%c\n",
+           ref_kind_str[ref_kind],
+           (long) class_tag,
+           (long) DEREF(tag_ptr),
+           (int ) value_type);
+    fflush(0);
+    return 0;
+}
+
+jint JNICALL arrayPrimitiveValueCallback
+    (jlong class_tag, jlong size, jlong* tag_ptr, jint element_count,
+     jvmtiPrimitiveType element_type, const void* elements, void* user_data)
+{
+    printf(" arrayPrimitiveValueCallback: class_tag=%-3ld, tag=%-3ld, len=%d, type=%c\n",
+           (long) class_tag,
+           (long) DEREF(tag_ptr),
+           (int ) element_count,
+           (int ) element_type);
+    fflush(0);
+    return 0;
+}
+
+jint JNICALL stringPrimitiveValueCallback
+    (jlong class_tag, jlong size, jlong* tag_ptr, const jchar* value,
+     jint value_length, void* user_data)
+{
+    printf("stringPrimitiveValueCallback: class_tag=%-3ld, tag=%-3ld, len=%d\n",
+           (long) class_tag,
+           (long) DEREF(tag_ptr),
+           (int ) value_length);
+    fflush(0);
+    return 0;
+}
+
+
+/* ============================================================================= */
+static jthread getTargetThread(jvmtiEnv *jvmti) {
+    static const char *target_thread_name = "main";
+    jint i;
+    jint thread_count = -1;
+    jthread *threads = NULL;
+
+    (*jvmti)->GetAllThreads(jvmti, &thread_count, &threads);
+
+    for (i = 0; i < thread_count; i++) {
+        jvmtiThreadInfo thread_info;
+        (*jvmti)->GetThreadInfo(jvmti, threads[i], &thread_info);
+
+        if (strcmp(thread_info.name, target_thread_name) == 0) {
+            return threads[i];
+        }
+    }
+
+    return NULL;
+}
+
+static jvmtiError setTagForTargetThread(jvmtiEnv *jvmti, jlong tag) {
+    jthread target_thread = getTargetThread(jvmti);
+    return (*jvmti)->SetTag(jvmti, target_thread, tag);
+}
+
+/** Agent algorithm. */
+static void JNICALL
+agentProc(jvmtiEnv* jvmti, JNIEnv* jni, void* arg) {
+    jobject rootObject = NULL;
+
+    printf("Wait for tested objects created\n");
+    fflush(0);
+    if (!NSK_VERIFY(nsk_jvmti_waitForSync(timeout))) {
+        return;
+    }
+
+    printf(">>> Obtain and tag tested objects from debugee class\n");
+    fflush(0);
+    {
+        if (!NSK_VERIFY(getTestedObjects(jvmti, jni, chainLength, &objectsCount,
+                                         &objectDescList, &rootObject))) {
+            return;
+        }
+    }
+
+    printf(">>> Let debugee to clean links to unreachable objects\n");
+    fflush(0);
+    {
+        if (!NSK_VERIFY(nsk_jvmti_resumeSync())) {
+            return;
+        }
+        if (!NSK_VERIFY(nsk_jvmti_waitForSync(timeout))) {
+            return;
+        }
+    }
+
+    if (!NSK_JVMTI_VERIFY(setTagForTargetThread(jvmti, TARG_THREAD_TAG))) {
+        nsk_jvmti_setFailStatus();
+        return;
+    }
+
+    printf("\n\n>>> Start 1-st iteration starting from the heap root\n");
+    fflush(0);
+    {
+        if (!NSK_JVMTI_VERIFY(
+                NSK_CPP_STUB6(FollowReferences, jvmti,
+                    (jint)   0,     /* heap_filter    */
+                    (jclass)  NULL, /* class          */
+                    (jobject) NULL, /* initial_object */
+                    &heapCallbacks,
+                    (const void *) &fakeUserData)))
+        {
+             nsk_jvmti_setFailStatus();
+             return;
+        }
+    }
+
+    printf(">>> Check if reachable objects were iterated\n");
+    fflush(0);
+    {
+        if (!checkTestedObjects(jvmti, jni, chainLength, objectDescList)) {
+            nsk_jvmti_setFailStatus();
+        }
+    }
+
+    {            /* Reinstall the expectations */
+        int k;
+        for (k = 0; k < objectsCount; k++) {
+            (objectDescList)[k].exp_found = 0;
+            (objectDescList)[k].found = 0;
+        }
+        /* Heap root object must be reported 2 times */
+        objectDescList[0].exp_found = 2;
+
+        /* First unreachable object must be reported once
+         * as JVMTI_HEAP_REFERENCE_STACK_LOCAL */
+        objectDescList[2 * chainLength].exp_found = 1;
+    }
+
+    printf("\n\n>>> Start 2-nd iteration starting from the heap root\n");
+    fflush(0);
+    first_followref = 0;
+    {
+        jint heap_filter = JVMTI_HEAP_FILTER_UNTAGGED
+                         | JVMTI_HEAP_FILTER_CLASS_UNTAGGED;
+        if (!NSK_JVMTI_VERIFY(
+                NSK_CPP_STUB6(FollowReferences, jvmti,
+                    heap_filter,
+                    (jclass)  NULL, /* class          */
+                    (jobject) NULL, /* initial_object */
+                    &heapCallbacks,
+                    (const void *) &fakeUserData)))
+        {
+             nsk_jvmti_setFailStatus();
+             return;
+        }
+    }
+
+    printf(">>> Check that both reachable and unreachable "
+           "objects were not iterated\n");
+    fflush(0);
+    {
+        if (!checkTestedObjects(jvmti, jni, chainLength, objectDescList)) {
+            nsk_jvmti_setFailStatus();
+        }
+    }
+
+
+    printf(">>> Clean used data\n");
+    fflush(0);
+    {
+        if (!NSK_VERIFY(releaseTestedObjects(jvmti, jni, chainLength,
+                        objectDescList, rootObject))) {
+            return;
+        }
+    }
+
+    printf("Let debugee to finish\n");
+    fflush(0);
+    if (!NSK_VERIFY(nsk_jvmti_resumeSync()))
+        return;
+}
+
+/* ============================================================================= */
+
+/** Agent library initialization. */
+#ifdef STATIC_BUILD
+JNIEXPORT jint JNICALL Agent_OnLoad_followref003(JavaVM *jvm, char *options, void *reserved) {
+    return Agent_Initialize(jvm, options, reserved);
+}
+JNIEXPORT jint JNICALL Agent_OnAttach_followref003(JavaVM *jvm, char *options, void *reserved) {
+    return Agent_Initialize(jvm, options, reserved);
+}
+JNIEXPORT jint JNI_OnLoad_followref003(JavaVM *jvm, char *options, void *reserved) {
+    return JNI_VERSION_1_8;
+}
+#endif
+jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
+    jvmtiEnv* jvmti = NULL;
+
+    if (!NSK_VERIFY(nsk_jvmti_parseOptions(options)))
+        return JNI_ERR;
+
+    timeout = nsk_jvmti_getWaitTime() * 60 * 1000;
+
+    {
+        const char* infoOpt = nsk_jvmti_findOptionValue("info");
+        if (infoOpt != NULL) {
+            if (strcmp(infoOpt, "none") == 0)
+                info = INFO_NONE;
+            else if (strcmp(infoOpt, "all") == 0)
+                info = INFO_ALL;
+            else if (strcmp(infoOpt, "objref") == 0)
+                info = INFO_OBJREF;
+            else if (strcmp(infoOpt, "stackref") == 0)
+                info = INFO_STACKREF;
+            else if (strcmp(infoOpt, "heaproot") == 0)
+                info = INFO_HEAPROOT;
+            else if (strcmp(infoOpt, "heapobj") == 0)
+                info = INFO_HEAPOBJ;
+            else {
+                printf("Unknown option value: info=%s\n", infoOpt);
+                fflush(0);
+                return JNI_ERR;
+            }
+        }
+    }
+
+    chainLength = nsk_jvmti_findOptionIntValue("objects", DEFAULT_CHAIN_LENGTH);
+    if (!NSK_VERIFY(chainLength > 0))
+        return JNI_ERR;
+
+    if (!NSK_VERIFY((jvmti =
+            nsk_jvmti_createJVMTIEnv(jvm, reserved)) != NULL))
+        return JNI_ERR;
+
+    {
+        jvmtiCapabilities caps;
+
+        memset(&caps, 0, sizeof(caps));
+        caps.can_tag_objects = 1;
+        if (!NSK_JVMTI_VERIFY(NSK_CPP_STUB2(AddCapabilities, jvmti, &caps))) {
+            return JNI_ERR;
+        }
+    }
+
+    /* Setting Heap Callbacks */
+    heapCallbacks.heap_iteration_callback         = NULL;
+    heapCallbacks.heap_reference_callback         = heapReferenceCallback;
+    heapCallbacks.primitive_field_callback        = primitiveFieldCallback;
+    heapCallbacks.array_primitive_value_callback  = arrayPrimitiveValueCallback;
+    heapCallbacks.string_primitive_value_callback = stringPrimitiveValueCallback;
+
+    if (!NSK_VERIFY(nsk_jvmti_setAgentProc(agentProc, NULL)))
+        return JNI_ERR;
+
+    return JNI_OK;
+}
+
+/* ============================================================================= */
+
+#ifdef __cplusplus
+}
+#endif