1 /*
2  * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 #include <string.h>
25 #include "jvmti.h"
26 #include "agent_common.h"
27 #include "jni_tools.h"
28 #include "jvmti_tools.h"
29 
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33 
34 /* ============================================================================= */
35 
36 static jlong timeout = 0;
37 
38 #define INFO_NONE       0x00
39 #define INFO_ALL        0xFF
40 #define INFO_OBJREF     0x01
41 #define INFO_STACKREF   0x02
42 #define INFO_HEAPROOT   0x04
43 #define INFO_HEAPOBJ    0x08
44 #define INFO_HEAPOBJ    0x08
45 
46 static unsigned int info = INFO_NONE;
47 
48 #define DEBUGEE_CLASS_NAME      "nsk/jvmti/IterateOverInstancesOfClass/iterinstcls003"
49 #define ROOT_OBJECT_CLASS_NAME  "nsk/jvmti/IterateOverInstancesOfClass/iterinstcls003RootTestedClass"
50 #define ROOT_OBJECT_CLASS_SIG   "L"ROOT_OBJECT_CLASS_NAME";"
51 #define CHAIN_OBJECT_CLASS_NAME "nsk/jvmti/IterateOverInstancesOfClass/iterinstcls003TestedClass"
52 #define CHAIN_OBJECT_CLASS_SIG  "L"CHAIN_OBJECT_CLASS_NAME";"
53 
54 #define OBJECT_FIELD_NAME               "object"
55 #define REACHABLE_CHAIN_FIELD_NAME      "reachableChain"
56 #define UNREACHABLE_CHAIN_FIELD_NAME    "unreachableChain"
57 #define TAIL_FIELD_NAME                 "tail"
58 
59 #define DEFAULT_CHAIN_LENGTH 4
60 
61 typedef struct ObjectDescStruct {
62     jlong tag;
63     jint found;
64     int collected;
65 } ObjectDesc;
66 
67 static int chainLength = 0;
68 static int objectsCount = 0;
69 
70 static ObjectDesc* objectDescList = NULL;
71 
72 static volatile int foundUntagged = 0;
73 
74 static int fakeUserData = 0;
75 static int userDataError = 0;
76 
77 /* ============================================================================= */
78 
79 /** Obtain chain of tested objects and tag them recursively. */
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)80 static int getChainObjects(jvmtiEnv* jvmti, JNIEnv* jni, jobject firstObject,
81                                     jfieldID firstField, const char firstFieldName[],
82                                     jfieldID nextField, const char nextFieldName[],
83                                     int count, ObjectDesc objectDescList[],
84                                     jlong tag, int reachable) {
85 
86     int success = NSK_TRUE;
87     jobject obj = NULL;
88     jlong objTag = (!reachable ? -tag : (tag % 2 != 0) ? tag : (jlong)0);
89 
90     if (count <= 0)
91         return NSK_TRUE;
92 
93     count--;
94     tag++;
95 
96     if (!NSK_JNI_VERIFY(jni, (obj =
97             NSK_CPP_STUB3(GetObjectField, jni, firstObject, firstField)) != NULL)) {
98         nsk_jvmti_setFailStatus();
99         return NSK_FALSE;
100     }
101 
102     objectDescList[count].tag = objTag;
103     if (!NSK_JVMTI_VERIFY(
104             NSK_CPP_STUB3(SetTag, jvmti, obj, objTag))) {
105         nsk_jvmti_setFailStatus();
106         return NSK_FALSE;
107     }
108     NSK_DISPLAY2("        tag=%-5ld object=0x%p\n", (long)objTag, (void*)obj);
109 
110     if (!getChainObjects(jvmti, jni, obj, nextField, nextFieldName,
111                                 nextField, nextFieldName,
112                                 count, objectDescList, tag, reachable)) {
113         return NSK_FALSE;
114     }
115 
116     NSK_TRACE(NSK_CPP_STUB2(DeleteLocalRef, jni, obj));
117     return success;
118 }
119 
120 /** Obtain all tested objects from debugee class and tag them recursively. */
getTestedObjects(jvmtiEnv * jvmti,JNIEnv * jni,int chainLength,int * objectsCount,ObjectDesc ** objectDescList,jclass * testedClass)121 static int getTestedObjects(jvmtiEnv* jvmti, JNIEnv* jni, int chainLength,
122                                     int *objectsCount, ObjectDesc* *objectDescList,
123                                     jclass *testedClass) {
124     jclass debugeeClass = NULL;
125     jclass rootObjectClass = NULL;
126     jclass chainObjectClass = NULL;
127 
128     jfieldID objectField = NULL;
129     jfieldID reachableChainField = NULL;
130     jfieldID unreachableChainField = NULL;
131     jfieldID tailField = NULL;
132 
133     jobject rootObject = NULL;
134 
135     jlong testedClassTag = 1;
136     jlong chainObjectTag = 100;
137 
138     *objectsCount = 2 * chainLength;
139 
140     NSK_DISPLAY1("Allocate memory for objects list: %d objects\n", *objectsCount);
141     if (!NSK_JVMTI_VERIFY(
142             NSK_CPP_STUB3(Allocate, jvmti, (*objectsCount * sizeof(ObjectDesc)),
143                                                     (unsigned char**)objectDescList))) {
144         nsk_jvmti_setFailStatus();
145         return NSK_FALSE;
146     }
147     NSK_DISPLAY1("  ... allocated array: 0x%p\n", (void*)objectDescList);
148 
149     {
150         int k;
151         for (k = 0; k < *objectsCount; k++) {
152             (*objectDescList)[k].tag = 0;
153             (*objectDescList)[k].found = 0;
154             (*objectDescList)[k].collected = 0;
155         }
156     }
157 
158     NSK_DISPLAY1("Find debugee class: %s\n", DEBUGEE_CLASS_NAME);
159     if (!NSK_JNI_VERIFY(jni, (debugeeClass =
160             NSK_CPP_STUB2(FindClass, jni, DEBUGEE_CLASS_NAME)) != NULL)) {
161         nsk_jvmti_setFailStatus();
162         return NSK_FALSE;
163     }
164     NSK_DISPLAY1("  ... found class: 0x%p\n", (void*)debugeeClass);
165 
166     NSK_DISPLAY1("Find root object class: %s\n", ROOT_OBJECT_CLASS_NAME);
167     if (!NSK_JNI_VERIFY(jni, (rootObjectClass =
168             NSK_CPP_STUB2(FindClass, jni, ROOT_OBJECT_CLASS_NAME)) != NULL)) {
169         nsk_jvmti_setFailStatus();
170         return NSK_FALSE;
171     }
172     NSK_DISPLAY1("  ... found class: 0x%p\n", (void*)rootObjectClass);
173 
174     NSK_DISPLAY1("Find chain object class: %s\n", CHAIN_OBJECT_CLASS_NAME);
175     if (!NSK_JNI_VERIFY(jni, (chainObjectClass =
176             NSK_CPP_STUB2(FindClass, jni, CHAIN_OBJECT_CLASS_NAME)) != NULL)) {
177         nsk_jvmti_setFailStatus();
178         return NSK_FALSE;
179     }
180     NSK_DISPLAY1("  ... found class: 0x%p\n", (void*)chainObjectClass);
181 
182     NSK_DISPLAY1("Create global ref to tested class: 0x%p\n", chainObjectClass);
183     if (!NSK_JNI_VERIFY(jni, (*testedClass = (jclass)
184             NSK_CPP_STUB2(NewGlobalRef, jni, chainObjectClass)) != NULL)) {
185         nsk_jvmti_setFailStatus();
186         return NSK_FALSE;
187     }
188     NSK_DISPLAY1("  ... global ref: 0x%p\n", (void*)*testedClass);
189 
190     NSK_DISPLAY1("Find static field in debugee class: %s\n", OBJECT_FIELD_NAME);
191     if (!NSK_JNI_VERIFY(jni, (objectField =
192             NSK_CPP_STUB4(GetStaticFieldID, jni, debugeeClass,
193                             OBJECT_FIELD_NAME, ROOT_OBJECT_CLASS_SIG)) != NULL)) {
194         nsk_jvmti_setFailStatus();
195         return NSK_FALSE;
196     }
197     NSK_DISPLAY1("  ... got fieldID: 0x%p\n", (void*)objectField);
198 
199     NSK_DISPLAY1("Find instance field in root object class: %s\n", REACHABLE_CHAIN_FIELD_NAME);
200     if (!NSK_JNI_VERIFY(jni, (reachableChainField =
201             NSK_CPP_STUB4(GetFieldID, jni, rootObjectClass,
202                         REACHABLE_CHAIN_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
203         nsk_jvmti_setFailStatus();
204         return NSK_FALSE;
205     }
206     NSK_DISPLAY1("  ... got fieldID: 0x%p\n", (void*)reachableChainField);
207 
208     NSK_DISPLAY1("Find instance field in root object class: %s\n", UNREACHABLE_CHAIN_FIELD_NAME);
209     if (!NSK_JNI_VERIFY(jni, (unreachableChainField =
210             NSK_CPP_STUB4(GetFieldID, jni, rootObjectClass,
211                         UNREACHABLE_CHAIN_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
212         nsk_jvmti_setFailStatus();
213         return NSK_FALSE;
214     }
215     NSK_DISPLAY1("  ... got fieldID: 0x%p\n", (void*)unreachableChainField);
216 
217     NSK_DISPLAY1("Find instance field in chain object class: %s\n", TAIL_FIELD_NAME);
218     if (!NSK_JNI_VERIFY(jni, (tailField =
219             NSK_CPP_STUB4(GetFieldID, jni, chainObjectClass,
220                             TAIL_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
221         nsk_jvmti_setFailStatus();
222         return NSK_FALSE;
223     }
224     NSK_DISPLAY1("  ... got fieldID: 0x%p\n", (void*)tailField);
225 
226     NSK_DISPLAY1("Get root object from static field: %s\n", OBJECT_FIELD_NAME);
227     if (!NSK_JNI_VERIFY(jni, (rootObject =
228             NSK_CPP_STUB3(GetStaticObjectField, jni, debugeeClass,
229                                                     objectField)) != NULL)) {
230         nsk_jvmti_setFailStatus();
231         return NSK_FALSE;
232     }
233     NSK_DISPLAY1("  ... got object: 0x%p\n", (void*)rootObject);
234 
235     NSK_DISPLAY0("Obtain and tag chain objects:\n");
236 
237     NSK_DISPLAY0("    tested class object:\n");
238     if (!NSK_JVMTI_VERIFY(
239             NSK_CPP_STUB3(SetTag, jvmti, *testedClass, testedClassTag))) {
240         nsk_jvmti_setFailStatus();
241         return NSK_FALSE;
242     }
243     NSK_DISPLAY2("        tag=%-5ld object=0x%p\n", (long)testedClassTag, (void*)*testedClass);
244 
245     NSK_DISPLAY1("    reachable objects chain: %d objects\n", chainLength);
246     if (!getChainObjects(jvmti, jni, rootObject,
247                                 reachableChainField, REACHABLE_CHAIN_FIELD_NAME,
248                                 tailField, TAIL_FIELD_NAME,
249                                 chainLength, *objectDescList,
250                                 chainObjectTag, NSK_TRUE)) {
251         nsk_jvmti_setFailStatus();
252         return NSK_FALSE;
253     }
254 
255     NSK_DISPLAY1("    unreachable objects chain: %d objects\n", chainLength);
256     if (!getChainObjects(jvmti, jni, rootObject,
257                                 unreachableChainField, UNREACHABLE_CHAIN_FIELD_NAME,
258                                 tailField, TAIL_FIELD_NAME,
259                                 chainLength, *objectDescList + chainLength,
260                                 chainObjectTag, NSK_FALSE)) {
261         nsk_jvmti_setFailStatus();
262         return NSK_FALSE;
263     }
264 
265     return NSK_TRUE;
266 }
267 
268 /** Check if tagged objects were iterated. */
checkTestedObjects(jvmtiEnv * jvmti,JNIEnv * jni,int chainLength,ObjectDesc objectDescList[])269 static int checkTestedObjects(jvmtiEnv* jvmti, JNIEnv* jni,
270                                 int chainLength, ObjectDesc objectDescList[]) {
271     int success = NSK_TRUE;
272     int expectedUntagged = 0;
273     int i;
274 
275     NSK_DISPLAY0("Following tagged/untagged objects were iterated:\n");
276 
277     NSK_DISPLAY0("    reachable objects:\n");
278     for (i = 0; i < chainLength; i++) {
279         NSK_DISPLAY2("        tag=%-5ld iterated=%d times\n",
280                         (long)objectDescList[i].tag, objectDescList[i].found);
281 
282         if (objectDescList[i].found > 0
283                     && objectDescList[i].tag != 0) {
284             NSK_COMPLAIN2("Reachable tagged object was iterated:\n"
285                           "#   tag:      %ld\n"
286                           "#   iterated: %d times\n",
287                             (long)objectDescList[i].tag,
288                             objectDescList[i].found);
289             nsk_jvmti_setFailStatus();
290         }
291 
292         if (objectDescList[i].tag == 0) {
293             expectedUntagged++;
294         }
295     }
296 
297     NSK_DISPLAY0("    unreachable objects:\n");
298     for (i = 0; i < chainLength; i++) {
299         NSK_DISPLAY3("        tag=%-5ld iterated=%-3d collected=%d times\n",
300                         (long)objectDescList[i + chainLength].tag,
301                         objectDescList[i + chainLength].found,
302                         objectDescList[i + chainLength].collected);
303 
304         if (objectDescList[i + chainLength].found <= 0
305                     && objectDescList[i + chainLength].collected > 0) {
306             NSK_COMPLAIN2("Not collected unreachable tagged object was iterated:\n"
307                           "#   tag:      %ld\n"
308                           "#   iterated: %d times\n",
309                             (long)objectDescList[i + chainLength].tag,
310                             objectDescList[i + chainLength].found);
311             nsk_jvmti_setFailStatus();
312         }
313     }
314 
315     NSK_DISPLAY0("    untagged objects:\n");
316     NSK_DISPLAY2("        total=%-3d iterated=%d objects\n",
317                                     expectedUntagged, foundUntagged);
318     if (foundUntagged > expectedUntagged) {
319         NSK_COMPLAIN2("Unexpected number of untagged objects were iterated:\n"
320                       "#   iterated untagged objects: %d\n"
321                       "#   expected:                  %d\n",
322                         foundUntagged, expectedUntagged);
323         nsk_jvmti_setFailStatus();
324     }
325 
326     return NSK_TRUE;
327 }
328 
329 /** Release references to the tested objects and free allocated memory. */
releaseTestedObjects(jvmtiEnv * jvmti,JNIEnv * jni,int chainLength,ObjectDesc * objectDescList,jclass testedClass)330 static int releaseTestedObjects(jvmtiEnv* jvmti, JNIEnv* jni, int chainLength,
331                                         ObjectDesc* objectDescList, jclass testedClass) {
332     if (testedClass != NULL) {
333         NSK_DISPLAY1("Release object reference to tested class: 0x%p\n", testedClass);
334         NSK_TRACE(NSK_CPP_STUB2(DeleteGlobalRef, jni, testedClass));
335     }
336 
337     if (objectDescList != NULL) {
338         NSK_DISPLAY1("Deallocate objects list: 0x%p\n", (void*)objectDescList);
339         if (!NSK_JVMTI_VERIFY(
340             NSK_CPP_STUB2(Deallocate, jvmti, (unsigned char*)objectDescList))) {
341             nsk_jvmti_setFailStatus();
342         }
343     }
344 
345     return NSK_TRUE;
346 }
347 
348 /* ============================================================================= */
349 
350 /** heapRootCallback for heap iterator. */
351 jvmtiIterationControl JNICALL
heapObjectCallback(jlong class_tag,jlong size,jlong * tag_ptr,void * user_data)352 heapObjectCallback(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data) {
353 
354     if (info & INFO_HEAPOBJ) {
355         NSK_DISPLAY3("  heapObjectCallback: class_tag=%-3ld size=%-3ld *tag_ptr=%-5ld\n",
356                         (long)class_tag, (long)size,
357                         (long)(tag_ptr == NULL ? (jlong)0 : *tag_ptr));
358     }
359 
360     if (class_tag != 1) {
361         NSK_COMPLAIN3("Unexpected class_tag passed to heapObjectCallback:\n"
362                       "#   object tag:     %ld\n"
363                       "#   class_tag:      %ld\n"
364                       "#   size:           %ld\n",
365                         (long)*tag_ptr,
366                         (long)class_tag,
367                         (long)size);
368         nsk_jvmti_setFailStatus();
369     }
370 
371     if (tag_ptr == NULL) {
372         NSK_COMPLAIN3("NULL tag_ptr is passed to heapObjectCallback:\n"
373                       "#   tag_ptr:        0x%p\n"
374                       "#   class_tag:      %ld\n"
375                       "#   size:           %ld\n",
376                         (void*)tag_ptr,
377                         (long)class_tag,
378                         (long)size);
379         nsk_jvmti_setFailStatus();
380     } else {
381         if (*tag_ptr == 0) {
382             foundUntagged++;
383         } else {
384             int found = 0;
385             int i;
386 
387             for (i = 0; i < objectsCount; i++) {
388                 if (*tag_ptr == objectDescList[i].tag) {
389                     found++;
390                     objectDescList[i].found++;
391                     break;
392                 }
393             }
394 
395             if (found <= 0) {
396                 NSK_COMPLAIN3("Unknown tagged object passed to heapObjectCallback:\n"
397                               "#   tag:            %ld\n"
398                               "#   class_tag:      %ld\n"
399                               "#   size:           %ld\n",
400                                 (long)*tag_ptr,
401                                 (long)class_tag,
402                                 (long)size);
403                 nsk_jvmti_setFailStatus();
404             } else {
405                 NSK_COMPLAIN3("Known tagged object passed to heapObjectCallback:\n"
406                               "#   tag:            %ld\n"
407                               "#   class_tag:      %ld\n"
408                               "#   size:           %ld\n",
409                                 (long)*tag_ptr,
410                                 (long)class_tag,
411                                 (long)size);
412                 nsk_jvmti_setFailStatus();
413             }
414         }
415     }
416 
417     if (user_data != &fakeUserData && !userDataError) {
418        NSK_COMPLAIN2("Unexpected user_data is passed to heapObjectCallback:\n"
419                       "#   expected:       0x%p\n"
420                       "#   actual:         0x%p\n",
421                       user_data,
422                       &fakeUserData);
423         nsk_jvmti_setFailStatus();
424         userDataError++;
425     }
426 
427     return JVMTI_ITERATION_CONTINUE;
428 }
429 
430 /* ============================================================================= */
431 
432 /** Agent algorithm. */
433 static void JNICALL
agentProc(jvmtiEnv * jvmti,JNIEnv * jni,void * arg)434 agentProc(jvmtiEnv* jvmti, JNIEnv* jni, void* arg) {
435     NSK_DISPLAY0("Wait for tested objects created\n");
436     if (!NSK_VERIFY(nsk_jvmti_waitForSync(timeout)))
437         return;
438 
439     {
440         jclass testedClass = NULL;
441 
442         NSK_DISPLAY0(">>> Obtain and tag tested objects from debugee class\n");
443         {
444             if (!NSK_VERIFY(getTestedObjects(jvmti, jni, chainLength,
445                                         &objectsCount, &objectDescList, &testedClass)))
446                 return;
447         }
448 
449         NSK_DISPLAY0(">>> Enable OBJECT_FREE event and let debugee to clean links to unreachable objects\n");
450         {
451             jvmtiEvent event = JVMTI_EVENT_OBJECT_FREE;
452             if (!NSK_VERIFY(nsk_jvmti_enableEvents(JVMTI_ENABLE, 1, &event, NULL)))
453                 return;
454 
455             if (!NSK_VERIFY(nsk_jvmti_resumeSync()))
456                 return;
457             if (!NSK_VERIFY(nsk_jvmti_waitForSync(timeout)))
458                 return;
459         }
460 
461         NSK_DISPLAY0(">>> Iterate over instances of tested class with filter JVMTI_HEAP_OBJECT_UNTAGGED\n");
462         {
463             if (!NSK_JVMTI_VERIFY(
464                     NSK_CPP_STUB5(IterateOverInstancesOfClass, jvmti,
465                         testedClass, JVMTI_HEAP_OBJECT_UNTAGGED,
466                         heapObjectCallback, &fakeUserData))) {
467                 nsk_jvmti_setFailStatus();
468                 return;
469             }
470         }
471 
472         NSK_DISPLAY0(">>> Disable OBJECT_FREE event and check if untagged objects were iterated:\n");
473         {
474             jvmtiEvent event = JVMTI_EVENT_OBJECT_FREE;
475             if (!NSK_VERIFY(nsk_jvmti_enableEvents(JVMTI_DISABLE, 1, &event, NULL)))
476                 return;
477 
478             if (!checkTestedObjects(jvmti, jni, chainLength, objectDescList)) {
479                 nsk_jvmti_setFailStatus();
480             }
481         }
482 
483         NSK_DISPLAY0(">>> Clean used data\n");
484         {
485             if (!NSK_VERIFY(releaseTestedObjects(jvmti, jni, chainLength,
486                                                             objectDescList, testedClass)))
487                 return;
488         }
489     }
490 
491     NSK_DISPLAY0("Let debugee to finish\n");
492     if (!NSK_VERIFY(nsk_jvmti_resumeSync()))
493         return;
494 }
495 
496 /* ============================================================================= */
497 
498 JNIEXPORT void JNICALL
callbackObjectFree(jvmtiEnv * jvmti,jlong tag)499 callbackObjectFree(jvmtiEnv* jvmti, jlong tag) {
500     int i;
501 
502     if (info & INFO_HEAPOBJ) {
503         NSK_DISPLAY1("  <ObjectFree>: tag=%-5ld\n", tag);
504     }
505 
506     if (tag > 0) {
507         for (i = 0; i < objectsCount; i++) {
508             if (tag == objectDescList[i].tag) {
509                 objectDescList[i].collected++;
510                 break;
511             }
512         }
513     }
514 }
515 
516 /* ============================================================================= */
517 
518 /** Agent library initialization. */
519 #ifdef STATIC_BUILD
Agent_OnLoad_iterinstcls003(JavaVM * jvm,char * options,void * reserved)520 JNIEXPORT jint JNICALL Agent_OnLoad_iterinstcls003(JavaVM *jvm, char *options, void *reserved) {
521     return Agent_Initialize(jvm, options, reserved);
522 }
Agent_OnAttach_iterinstcls003(JavaVM * jvm,char * options,void * reserved)523 JNIEXPORT jint JNICALL Agent_OnAttach_iterinstcls003(JavaVM *jvm, char *options, void *reserved) {
524     return Agent_Initialize(jvm, options, reserved);
525 }
JNI_OnLoad_iterinstcls003(JavaVM * jvm,char * options,void * reserved)526 JNIEXPORT jint JNI_OnLoad_iterinstcls003(JavaVM *jvm, char *options, void *reserved) {
527     return JNI_VERSION_1_8;
528 }
529 #endif
Agent_Initialize(JavaVM * jvm,char * options,void * reserved)530 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
531     jvmtiEnv* jvmti = NULL;
532 
533     if (!NSK_VERIFY(nsk_jvmti_parseOptions(options)))
534         return JNI_ERR;
535 
536     timeout = nsk_jvmti_getWaitTime() * 60 * 1000;
537 
538     {
539         const char* infoOpt = nsk_jvmti_findOptionValue("info");
540         if (infoOpt != NULL) {
541             if (strcmp(infoOpt, "none") == 0)
542                 info = INFO_NONE;
543             else if (strcmp(infoOpt, "all") == 0)
544                 info = INFO_ALL;
545             else if (strcmp(infoOpt, "objref") == 0)
546                 info = INFO_OBJREF;
547             else if (strcmp(infoOpt, "stackref") == 0)
548                 info = INFO_STACKREF;
549             else if (strcmp(infoOpt, "heaproot") == 0)
550                 info = INFO_HEAPROOT;
551             else if (strcmp(infoOpt, "heapobj") == 0)
552                 info = INFO_HEAPOBJ;
553             else {
554                 NSK_COMPLAIN1("Unknown option value: info=%s\n", infoOpt);
555                 return JNI_ERR;
556             }
557         }
558     }
559 
560     chainLength = nsk_jvmti_findOptionIntValue("objects", DEFAULT_CHAIN_LENGTH);
561     if (!NSK_VERIFY(chainLength > 0))
562         return JNI_ERR;
563 
564     if (!NSK_VERIFY((jvmti =
565             nsk_jvmti_createJVMTIEnv(jvm, reserved)) != NULL))
566         return JNI_ERR;
567 
568     {
569         jvmtiCapabilities caps;
570 
571         memset(&caps, 0, sizeof(caps));
572         caps.can_tag_objects = 1;
573         caps.can_generate_object_free_events = 1;
574         if (!NSK_JVMTI_VERIFY(
575                 NSK_CPP_STUB2(AddCapabilities, jvmti, &caps))) {
576             return JNI_ERR;
577         }
578     }
579 
580     {
581         jvmtiEventCallbacks eventCallbacks;
582         memset(&eventCallbacks, 0, sizeof(eventCallbacks));
583         eventCallbacks.ObjectFree = callbackObjectFree;
584         if (!NSK_JVMTI_VERIFY(
585                 NSK_CPP_STUB3(SetEventCallbacks, jvmti,
586                                     &eventCallbacks, sizeof(eventCallbacks))))
587             return JNI_ERR;
588     }
589 
590     if (!NSK_VERIFY(nsk_jvmti_setAgentProc(agentProc, NULL)))
591         return JNI_ERR;
592 
593     return JNI_OK;
594 }
595 
596 /* ============================================================================= */
597 
598 #ifdef __cplusplus
599 }
600 #endif
601