test/hotspot/jtreg/vmTestbase/nsk/jvmti/unit/FollowReferences/followref001/followref001.cpp
author jcbeyler
Thu, 25 Oct 2018 08:18:42 -0700
changeset 52284 1f402d1f630f
parent 52215 0b0ba3a2fec9
child 57629 7aba63ce3b3a
permissions -rw-r--r--
8212770: Remove spaces before/after () for vmTestbase/jvmti/[s-u] Summary: Remove spaces before/after () Reviewed-by: amenkov, cjplummer

/*
 * 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 <stdint.h>
#include "jvmti.h"
#include "agent_common.h"
#include "jni_tools.h"
#include "jvmti_tools.h"

extern "C" {

/* ============================================================================= */

static jlong timeout = 0;

#define DEBUGEE_CLASS_NAME      "nsk/jvmti/unit/FollowReferences/followref001"
#define ROOT_OBJECT_CLASS_NAME  "nsk/jvmti/unit/FollowReferences/followref001RootTestedClass"
#define ROOT_OBJECT_CLASS_SIG   "L" ROOT_OBJECT_CLASS_NAME ";"
#define CHAIN_OBJECT_CLASS_NAME "nsk/jvmti/unit/FollowReferences/followref001TestedClass"
#define CHAIN_OBJECT_CLASS_SIG  "L" CHAIN_OBJECT_CLASS_NAME ";"

#define OBJECT_FIELD_NAME               "rootObject"
#define REACHABLE_CHAIN_FIELD_NAME      "reachableChain"
#define UNREACHABLE_CHAIN_FIELD_NAME    "unreachableChain"
#define NEXT_FIELD_NAME                 "next"


#define DEFAULT_CHAIN_LENGTH 3
#define FULL_32_BIT_MASK     0xFFFFFFFF

typedef struct ObjectDescStruct {
    jlong tag;           /* Tag of the object */
    jlong exp_class_tag; /* Expected tag of the object class */
    jlong class_tag;     /* Reported tag of the object class */
    jint  exp_found;     /* Expected number of iterations through the object */
    jint  found;         /* Reported number of iterations through the object */
} ObjectDesc;

static int chainLength   = 0;
static int objectsCount  = 0;
static int fakeUserData  = 0;
static int userDataError = 0;

static ObjectDesc* objectDescList = NULL;

static const jlong ROOT_CLASS_TAG   = 9;
static const jlong CHAIN_CLASS_TAG  = 99;
static const jlong ROOT_OBJECT_TAG  = 10;
static const jlong CHAIN_OBJECT_TAG = 100;

static jvmtiHeapCallbacks heapCallbacks;

/* This array has to be up-to-date with the jvmtiHeapReferenceKind enum */
static const char* ref_kind_str[28] = {
   "unknown_0",
   "JVMTI_HEAP_REFERENCE_CLASS",
   "JVMTI_HEAP_REFERENCE_FIELD",
   "JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT",
   "JVMTI_HEAP_REFERENCE_CLASS_LOADER",
   "JVMTI_HEAP_REFERENCE_SIGNERS",
   "JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN",
   "JVMTI_HEAP_REFERENCE_INTERFACE",
   "JVMTI_HEAP_REFERENCE_STATIC_FIELD",
   "JVMTI_HEAP_REFERENCE_CONSTANT_POOL",
   "JVMTI_HEAP_REFERENCE_SUPERCLASS",
   "unknown_11", "unknown_12", "unknown_13", "unknown_14", "unknown_15",
   "unknown_16", "unknown_17", "unknown_18", "unknown_19", "unknown_20",
   "JVMTI_HEAP_REFERENCE_JNI_GLOBAL",
   "JVMTI_HEAP_REFERENCE_SYSTEM_CLASS",
   "JVMTI_HEAP_REFERENCE_MONITOR",
   "JVMTI_HEAP_REFERENCE_STACK_LOCAL",
   "JVMTI_HEAP_REFERENCE_JNI_LOCAL",
   "JVMTI_HEAP_REFERENCE_THREAD",
   "JVMTI_HEAP_REFERENCE_OTHER"
};

#define DEREF(ptr) (((ptr) == NULL ? 0 : *(ptr)))


/* ============================================================================= */

static int get_reference_index(jvmtiHeapReferenceKind   reference_kind,
                               const jvmtiHeapReferenceInfo* reference_info)
{
    int referrer_index = 0;

    switch (reference_kind) {
        case JVMTI_HEAP_REFERENCE_CONSTANT_POOL:
            referrer_index = reference_info->constant_pool.index;
            break;
        case JVMTI_HEAP_REFERENCE_FIELD:
        case JVMTI_HEAP_REFERENCE_STATIC_FIELD:
            referrer_index = reference_info->field.index;
            break;
        case JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT:
            referrer_index = reference_info->array.index;
            break;
        case JVMTI_HEAP_REFERENCE_STACK_LOCAL:
            referrer_index = reference_info->stack_local.slot;
            /* Fall through */
        case JVMTI_HEAP_REFERENCE_JNI_LOCAL:
            referrer_index |= reference_info->stack_local.depth << 16;
            break;
        default:
            // TODO: check that realy should be done w/ other jvmtiHeapReferenceKind
            break;
    }

    return referrer_index;
} /* get_reference_index */


/** Initialize objectDescList. */
static int initObjectDescList(jvmtiEnv*    jvmti,
                              int          chainLength,
                              int*         objectsCount,
                              ObjectDesc** objectDescList)
{
    /* root object + reachable and unreachable object chains */
    *objectsCount = 1 + 2 * chainLength;

    printf("Allocate memory for objects list: %d objects\n", *objectsCount);
    fflush(0);
    if (!NSK_JVMTI_VERIFY(jvmti->Allocate((*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 = CHAIN_CLASS_TAG;
            (*objectDescList)[k].exp_found = 0;
            (*objectDescList)[k].found = 0;
        }
    }
    (*objectDescList)[0].exp_class_tag = ROOT_CLASS_TAG;
    (*objectDescList)[0].tag           = ROOT_OBJECT_TAG;

    /* Object with tag=100 must be referenced 2 times */
    (*objectDescList)[chainLength].exp_found = 1;


     return NSK_TRUE;
} /* initObjectDescList */


/** Find and tag classes. */
static int getAndTagClasses(jvmtiEnv*    jvmti,
                            JNIEnv*      jni,
                            jclass*      debugeeClass,
                            jclass*      rootObjectClass,
                            jclass*      chainObjectClass)
{

    if (!NSK_JNI_VERIFY(jni, (*debugeeClass = jni->FindClass(DEBUGEE_CLASS_NAME)) != NULL)) {
        nsk_jvmti_setFailStatus();
        return NSK_FALSE;
    }
    printf("\nFound debugee class: 0x%p\n  %s\n",
           (void*) *debugeeClass, DEBUGEE_CLASS_NAME);
    fflush(0);

    if (!NSK_JNI_VERIFY(jni, (*rootObjectClass =
            jni->FindClass(ROOT_OBJECT_CLASS_NAME)) != NULL)) {
        nsk_jvmti_setFailStatus();
        return NSK_FALSE;
    }

    if (!NSK_JVMTI_VERIFY(jvmti->SetTag(*rootObjectClass, ROOT_CLASS_TAG))) {
        nsk_jvmti_setFailStatus();
    }

    printf("\nFound root object class: 0x%p, tag=%ld\n  %s\n",
           (void*) *rootObjectClass,(long) ROOT_CLASS_TAG,
           ROOT_OBJECT_CLASS_NAME);
    fflush(0);


    if (!NSK_JNI_VERIFY(jni, (*chainObjectClass =
            jni->FindClass(CHAIN_OBJECT_CLASS_NAME)) != NULL)) {
        nsk_jvmti_setFailStatus();
        return NSK_FALSE;
    }

    if (!NSK_JVMTI_VERIFY(jvmti->SetTag(*chainObjectClass, CHAIN_CLASS_TAG))) {
        nsk_jvmti_setFailStatus();
    }
    printf("\nFound chain object class: 0x%p, tag=%ld\n  %s\n",
           (void*) *chainObjectClass, (long) CHAIN_CLASS_TAG,
           CHAIN_OBJECT_CLASS_NAME);
    fflush(0);

     return NSK_TRUE;
} /* getAndTagClasses */


/** Obtain chain of tested objects and tag them recursively. */
static int getFieldsAndObjects(jvmtiEnv*  jvmti,
                             JNIEnv*     jni,
                             jclass      debugeeClass,
                             jclass      rootObjectClass,
                             jclass      chainObjectClass,
                             jobject*    rootObjectPtr,
                             jfieldID*   reachableChainField,
                             jfieldID*   unreachableChainField,
                             jfieldID*   nextField)
{
    jfieldID rootObjectField = NULL;

    if (!NSK_JNI_VERIFY(jni, (rootObjectField =
            jni->GetStaticFieldID(debugeeClass, OBJECT_FIELD_NAME, ROOT_OBJECT_CLASS_SIG)) != NULL)) {
        nsk_jvmti_setFailStatus();
        return NSK_FALSE;
    }
    printf("\nFound fieldID: 0x%p - \'%s\' static field in debugee class\n",
           (void*) rootObjectField, OBJECT_FIELD_NAME);
    fflush(0);

    if (!NSK_JNI_VERIFY(jni, (*reachableChainField =
            jni->GetFieldID(rootObjectClass, REACHABLE_CHAIN_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
        nsk_jvmti_setFailStatus();
        return NSK_FALSE;
    }
    printf("\nFound fieldID: 0x%p - \'%s\' field in root object class\n",
           (void*) reachableChainField, REACHABLE_CHAIN_FIELD_NAME);
    fflush(0);

    if (!NSK_JNI_VERIFY(jni, (*unreachableChainField =
            jni->GetFieldID(rootObjectClass, UNREACHABLE_CHAIN_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
        nsk_jvmti_setFailStatus();
        return NSK_FALSE;
    }

    printf("\nFound fieldID: 0x%p - \'%s\' field in root object class\n",
           (void*) unreachableChainField, UNREACHABLE_CHAIN_FIELD_NAME);
    fflush(0);

    if (!NSK_JNI_VERIFY(jni, (*nextField =
            jni->GetFieldID(chainObjectClass, NEXT_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
        nsk_jvmti_setFailStatus();
        return NSK_FALSE;
    }
    printf("\nFound fieldID: 0x%p - \'%s\' field in chain object class\n",
           (void*) nextField, NEXT_FIELD_NAME);
    fflush(0);

    if (!NSK_JNI_VERIFY(jni, (*rootObjectPtr =
            jni->GetStaticObjectField(debugeeClass, rootObjectField)) != NULL)) {
        nsk_jvmti_setFailStatus();
        return NSK_FALSE;
    }
    printf("\nFound root object: 0x%p\n", (void*) *rootObjectPtr);
    fflush(0);

    if (!NSK_JNI_VERIFY(jni, (*rootObjectPtr = jni->NewGlobalRef(*rootObjectPtr)) != NULL)) {
        nsk_jvmti_setFailStatus();
        return NSK_FALSE;
    }
    printf("Created root object global ref: 0x%p\n", (void*)*rootObjectPtr);
    fflush(0);

     return NSK_TRUE;
} /* getFieldsAndObjects */


/** Obtain chain of tested objects and tag them recursively. */
static int getAndTagChainObjects(
    jvmtiEnv*  jvmti,
    JNIEnv*    jni,
    jobject    currObj,
    jfieldID   refField,
    jfieldID   nextField,
    int        count,
    ObjectDesc objectDescList[],
    jlong      tag,
    int        reachable)
{
    jobject nextObj = NULL;
    jlong objTag = (reachable ? tag : -tag);

    if (count <= 0) {
        return NSK_TRUE;
    }

    count--;
    tag++;

    if (!NSK_JNI_VERIFY(jni, (nextObj = jni->GetObjectField(currObj, refField)) != NULL)) {
        nsk_jvmti_setFailStatus();
        return NSK_FALSE;
    }

    objectDescList[count].tag = objTag;
    if (reachable) {
        objectDescList[count].exp_found++;
    }

    if (!NSK_JVMTI_VERIFY(jvmti->SetTag(nextObj, objTag))) {
        nsk_jvmti_setFailStatus();
    }
    printf("        tag=%-5ld object=0x%p\n", (long)objTag, (void*)nextObj);
    fflush(0);

    /* To continue traversing objects in the chain */
    if (!getAndTagChainObjects(jvmti,
                               jni,
                               nextObj,
                               nextField,
                               nextField,
                               count,
                               objectDescList,
                               tag,
                               reachable)
    ) {
        return NSK_FALSE;
    }

    NSK_TRACE(jni->DeleteLocalRef(nextObj));

    return NSK_TRUE;
} /* getAndTagChainObjects */

/** Obtain all tested objects from debugee class and tag them recursively. */
static int getAndTagTestedObjects(
    jvmtiEnv*    jvmti,
    JNIEnv*      jni,
    int          chainLength,
    int*         objectsCount,
    ObjectDesc** objectDescList,
    jobject*     rootObjectPtr)
{
    jclass   debugeeClass          = NULL;
    jclass   rootObjectClass       = NULL;
    jclass   chainObjectClass      = NULL;

    jfieldID reachableChainField   = NULL;
    jfieldID unreachableChainField = NULL;
    jfieldID nextField             = NULL;

    if (initObjectDescList(jvmti,
                           chainLength,
                           objectsCount,
                           objectDescList) == NSK_FALSE) {
        return NSK_FALSE;
    }

    if (getAndTagClasses(jvmti,
                         jni,
                         &debugeeClass,
                         &rootObjectClass,
                         &chainObjectClass) == NSK_FALSE) {
        return NSK_FALSE;
    }

    if (getFieldsAndObjects(jvmti,
                            jni,
                            debugeeClass,
                            rootObjectClass,
                            chainObjectClass,
                            rootObjectPtr,
                            &reachableChainField,
                            &unreachableChainField,
                            &nextField) == NSK_FALSE) {
        return NSK_FALSE;
    }

    printf("\nObtain and tag chain objects:\n");
    printf("    root tested object:\n");

    if (!NSK_JVMTI_VERIFY(jvmti->SetTag(*rootObjectPtr, ROOT_OBJECT_TAG))
    ) {
        nsk_jvmti_setFailStatus();
    }
    printf("        tag=%-5ld  object = 0x%p\n",
           (long) ROOT_OBJECT_TAG, (void*) *rootObjectPtr);

    printf("    reachable objects chain: %d objects\n", chainLength);
    fflush(0);

    if (!getAndTagChainObjects(jvmti,
                               jni,
                               *rootObjectPtr,
                               reachableChainField,
                               nextField,
                               chainLength,
                               (*objectDescList) + 1,
                               CHAIN_OBJECT_TAG,
                               NSK_TRUE)  /* reachable objects */
    ) {
        nsk_jvmti_setFailStatus();
        return NSK_FALSE;
    }

    printf("    unreachable objects chain: %d objects\n", chainLength);
    if (!getAndTagChainObjects(jvmti,
                               jni,
                               *rootObjectPtr,
                               unreachableChainField,
                               nextField,
                               chainLength,
                               (*objectDescList) + 1 + chainLength,
                               CHAIN_OBJECT_TAG,
                               NSK_FALSE) /* unreachable objects */
    ) {
        nsk_jvmti_setFailStatus();
        return NSK_FALSE;
    }

    return NSK_TRUE;
} /* getAndTagTestedObjects */

/** 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) {
            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) {
            NSK_COMPLAIN0("Unreachable object was iterated\n");
            nsk_jvmti_setFailStatus();
        }
        fflush(0);
    }

    return NSK_TRUE;
} /* checkTestedObjects */


/** 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(jni->DeleteGlobalRef(rootObject));
    }

    if (objectDescList != NULL) {
        printf("Deallocate objects list: 0x%p\n", (void*)objectDescList);
        if (!NSK_JVMTI_VERIFY(jvmti->Deallocate((unsigned char*)objectDescList))) {
            nsk_jvmti_setFailStatus();
        }
    }

    fflush(0);
    return NSK_TRUE;
} /* releaseTestedObjects */


/* ============================================================================= */

/** heapReferenceCallback for heap iterator. */
jint JNICALL heapReferenceCallback(
     jvmtiHeapReferenceKind        reference_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 referrer_index = 0;
    jlong tag;
    jlong ref_tag;

    printf(" heapReferenceCallback: %s\n", ref_kind_str[reference_kind]);
    printf("   reference_info: 0x%p, class_tag: 0x%" LL "d, referrer_class_tag: 0x%" LL "d\n",
               reference_info,       class_tag,           referrer_class_tag);
                          /* ss45998: class_tag=>referrence_class_tag */
    printf("   size: %" LL "d, tag_ptr: 0x%p, referrer_tag_ptr: 0x%p, length: %-d\n",
               size,         tag_ptr,       referrer_tag_ptr,       length);
    fflush(0);

    if (((uintptr_t) tag_ptr & FULL_32_BIT_MASK) == FULL_32_BIT_MASK) {
        NSK_COMPLAIN1("wrong tag_ptr passed to "
                      "heapReferenceCallback: %#lx\n", tag_ptr);
        nsk_jvmti_setFailStatus();
        tag = 0;
    } else {
        tag = DEREF(tag_ptr);
    }

    if (((uintptr_t) referrer_tag_ptr & FULL_32_BIT_MASK) == FULL_32_BIT_MASK) {
        NSK_COMPLAIN1("wrong referrer_tag_ptr passed to "
                      "heapReferenceCallback: %#lx\n", referrer_tag_ptr);
        nsk_jvmti_setFailStatus();
        ref_tag = 0;
    } else {
        ref_tag = DEREF(referrer_tag_ptr);
    }

    referrer_index = get_reference_index(reference_kind, reference_info);

    printf("   class_tag=%" LL "d, tag=%" LL "d, size=%" LL "d,"
           " ref_tag=%" LL "d, referrer_index=%d\n\n",
               class_tag, tag, size, ref_tag, referrer_index);
    fflush(0);

    if (length != -1) {
        NSK_COMPLAIN1("wrong length passed to heapReferenceCallback: "
                      "%d; must be: -1\n", length);
        nsk_jvmti_setFailStatus();
    }

    if (tag_ptr != NULL && *tag_ptr != 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) {
                    NSK_COMPLAIN0("Unreachable tagged object is passed"
                                  " to heapReferenceCallback\n");
                    nsk_jvmti_setFailStatus();
                }
                break;
            }
        }

        if (reference_kind != JVMTI_HEAP_REFERENCE_CLASS && found <= 0) {
            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 (reference_kind) {
        int i;
        case JVMTI_HEAP_REFERENCE_CLASS: {
            if (tag == 0) {
                return 0;
            }
            if (tag != ROOT_CLASS_TAG && tag != CHAIN_CLASS_TAG) {
                NSK_COMPLAIN0("Unknown tagged class is passed"
                              " to heapReferenceCallback\n");
                nsk_jvmti_setFailStatus();
            }
            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;
               }
            }
            return 0;
        }
        case JVMTI_HEAP_REFERENCE_JNI_GLOBAL:
        case JVMTI_HEAP_REFERENCE_SYSTEM_CLASS:
        case JVMTI_HEAP_REFERENCE_MONITOR:
        case JVMTI_HEAP_REFERENCE_STACK_LOCAL:
        case JVMTI_HEAP_REFERENCE_JNI_LOCAL:
        case JVMTI_HEAP_REFERENCE_THREAD:
        case JVMTI_HEAP_REFERENCE_OTHER: {
            NSK_COMPLAIN1("This reference kind was not expected: %s\n",
                           ref_kind_str[reference_kind]);
            fflush(0);
            nsk_jvmti_setFailStatus();
            return 0;
        }
        default:
            // TODO: check that realy should be done w/ other jvmtiHeapReferenceKind
            break;
    }
    return JVMTI_VISIT_OBJECTS;
} /* heapReferenceCallback */


jint JNICALL primitiveFieldCallback(
     jvmtiHeapReferenceKind        reference_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[reference_kind],
               (long) class_tag,
               (long) DEREF(tag_ptr),
               (int) value_type);
    fflush(0);
    return 0;
} /* primitiveFieldCallback */


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;
} /* arrayPrimitiveValueCallback */


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;
} /* stringPrimitiveValueCallback */



/* ============================================================================= */

/** 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(getAndTagTestedObjects(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;
    }

    printf(">>> Start iteration from root tested object: 0x%p\n\n", rootObject);
    fflush(0);

    if (!NSK_JVMTI_VERIFY(jvmti->FollowReferences((jint)   0,    /* heap_filter    */
                                                  (jclass) NULL, /* class          */
                                                  rootObject,    /* 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();
    }

    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;
    }
} /* agentProc */


/* ============================================================================= */

/** Agent library initialization. */
#ifdef STATIC_BUILD
JNIEXPORT jint JNICALL Agent_OnLoad_followref001(JavaVM *jvm, char *options, void *reserved) {
    return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT jint JNICALL Agent_OnAttach_followref001(JavaVM *jvm, char *options, void *reserved) {
    return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT jint JNI_OnLoad_followref001(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;

    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(jvmti->AddCapabilities(&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;
} /* Agent_OnLoad */


/* ============================================================================= */

}