--- /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