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 extern "C" {
31 
32 /* ============================================================================= */
33 
34 static jlong timeout = 0;
35 
36 #define INFO_NONE       0x00
37 #define INFO_ALL        0xFF
38 #define INFO_OBJREF     0x01
39 #define INFO_STACKREF   0x02
40 #define INFO_HEAPROOT   0x04
41 #define INFO_HEAPOBJ    0x08
42 #define INFO_TAGGED     0x10
43 
44 static unsigned int info = INFO_NONE;
45 
46 #define DEBUGEE_CLASS_NAME      "nsk/jvmti/IterateOverHeap/iterheap002"
47 #define ROOT_OBJECT_CLASS_NAME  "nsk/jvmti/IterateOverHeap/iterheap002RootTestedClass"
48 #define ROOT_OBJECT_CLASS_SIG   "L" ROOT_OBJECT_CLASS_NAME ";"
49 #define CHAIN_OBJECT_CLASS_NAME "nsk/jvmti/IterateOverHeap/iterheap002TestedClass"
50 #define CHAIN_OBJECT_CLASS_SIG  "L" CHAIN_OBJECT_CLASS_NAME ";"
51 
52 #define OBJECT_FIELD_NAME               "object"
53 #define REACHABLE_CHAIN_FIELD_NAME      "reachableChain"
54 #define UNREACHABLE_CHAIN_FIELD_NAME    "unreachableChain"
55 #define TAIL_FIELD_NAME                 "tail"
56 
57 #define DEFAULT_CHAIN_LENGTH 4
58 
59 typedef struct ObjectDescStruct {
60     jlong tag;
61     jint found;
62     int collected;
63 } ObjectDesc;
64 
65 static int chainLength = 0;
66 static int objectsCount = 0;
67 
68 static ObjectDesc* objectDescList = NULL;
69 
70 static volatile int foundUntagged = 0;
71 
72 static int fakeUserData = 0;
73 static int userDataError = 0;
74 
75 /* ============================================================================= */
76 
77 /** 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)78 static int getChainObjects(jvmtiEnv* jvmti, JNIEnv* jni, jobject firstObject,
79                                     jfieldID firstField, const char firstFieldName[],
80                                     jfieldID nextField, const char nextFieldName[],
81                                     int count, ObjectDesc objectDescList[],
82                                     jlong tag, int reachable) {
83 
84     int success = NSK_TRUE;
85     jobject obj = NULL;
86     jlong objTag = (!reachable ? -tag : (tag % 2 != 0) ? tag : (jlong)0);
87 
88     if (count <= 0)
89         return NSK_TRUE;
90 
91     count--;
92     tag++;
93 
94     if (!NSK_JNI_VERIFY(jni, (obj =
95             jni->GetObjectField(firstObject, firstField)) != NULL)) {
96         nsk_jvmti_setFailStatus();
97         return NSK_FALSE;
98     }
99 
100     objectDescList[count].tag = objTag;
101     if (!NSK_JVMTI_VERIFY(
102             jvmti->SetTag(obj, objTag))) {
103         nsk_jvmti_setFailStatus();
104         return NSK_FALSE;
105     }
106     NSK_DISPLAY2("        tag=%-5ld object=0x%p\n", (long)objTag, (void*)obj);
107 
108     if (!getChainObjects(jvmti, jni, obj, nextField, nextFieldName,
109                                 nextField, nextFieldName,
110                                 count, objectDescList, tag, reachable)) {
111         return NSK_FALSE;
112     }
113 
114     NSK_TRACE(jni->DeleteLocalRef(obj));
115     return success;
116 }
117 
118 /** Obtain all tested objects from debugee class and tag them recursively. */
getTestedObjects(jvmtiEnv * jvmti,JNIEnv * jni,int chainLength,int * objectsCount,ObjectDesc ** objectDescList)119 static int getTestedObjects(jvmtiEnv* jvmti, JNIEnv* jni, int chainLength,
120                                     int *objectsCount, ObjectDesc* *objectDescList) {
121     jclass debugeeClass = NULL;
122     jclass rootObjectClass = NULL;
123     jclass chainObjectClass = NULL;
124 
125     jfieldID objectField = NULL;
126     jfieldID reachableChainField = NULL;
127     jfieldID unreachableChainField = NULL;
128     jfieldID tailField = NULL;
129 
130     jobject rootObject = NULL;
131 
132     jlong chainObjectTag = 100;
133 
134     *objectsCount = 2 * chainLength;
135 
136     NSK_DISPLAY1("Allocate memory for objects list: %d objects\n", *objectsCount);
137     if (!NSK_JVMTI_VERIFY(jvmti->Allocate((*objectsCount * sizeof(ObjectDesc)), (unsigned char**)objectDescList))) {
138         nsk_jvmti_setFailStatus();
139         return NSK_FALSE;
140     }
141     NSK_DISPLAY1("  ... allocated array: 0x%p\n", (void*)objectDescList);
142 
143     {
144         int k;
145         for (k = 0; k < *objectsCount; k++) {
146             (*objectDescList)[k].tag = 0;
147             (*objectDescList)[k].found = 0;
148             (*objectDescList)[k].collected = 0;
149         }
150     }
151 
152     NSK_DISPLAY1("Find debugee class: %s\n", DEBUGEE_CLASS_NAME);
153     if (!NSK_JNI_VERIFY(jni, (debugeeClass =
154             jni->FindClass(DEBUGEE_CLASS_NAME)) != NULL)) {
155         nsk_jvmti_setFailStatus();
156         return NSK_FALSE;
157     }
158     NSK_DISPLAY1("  ... found class: 0x%p\n", (void*)debugeeClass);
159 
160     NSK_DISPLAY1("Find root object class: %s\n", ROOT_OBJECT_CLASS_NAME);
161     if (!NSK_JNI_VERIFY(jni, (rootObjectClass =
162             jni->FindClass(ROOT_OBJECT_CLASS_NAME)) != NULL)) {
163         nsk_jvmti_setFailStatus();
164         return NSK_FALSE;
165     }
166     NSK_DISPLAY1("  ... found class: 0x%p\n", (void*)rootObjectClass);
167 
168     NSK_DISPLAY1("Find chain object class: %s\n", CHAIN_OBJECT_CLASS_NAME);
169     if (!NSK_JNI_VERIFY(jni, (chainObjectClass =
170             jni->FindClass(CHAIN_OBJECT_CLASS_NAME)) != NULL)) {
171         nsk_jvmti_setFailStatus();
172         return NSK_FALSE;
173     }
174     NSK_DISPLAY1("  ... found class: 0x%p\n", (void*)chainObjectClass);
175 
176     NSK_DISPLAY1("Find static field in debugee class: %s\n", OBJECT_FIELD_NAME);
177     if (!NSK_JNI_VERIFY(jni, (objectField =
178             jni->GetStaticFieldID(debugeeClass, OBJECT_FIELD_NAME, ROOT_OBJECT_CLASS_SIG)) != NULL)) {
179         nsk_jvmti_setFailStatus();
180         return NSK_FALSE;
181     }
182     NSK_DISPLAY1("  ... got fieldID: 0x%p\n", (void*)objectField);
183 
184     NSK_DISPLAY1("Find instance field in root object class: %s\n", REACHABLE_CHAIN_FIELD_NAME);
185     if (!NSK_JNI_VERIFY(jni, (reachableChainField =
186             jni->GetFieldID(rootObjectClass, REACHABLE_CHAIN_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
187         nsk_jvmti_setFailStatus();
188         return NSK_FALSE;
189     }
190     NSK_DISPLAY1("  ... got fieldID: 0x%p\n", (void*)reachableChainField);
191 
192     NSK_DISPLAY1("Find instance field in root object class: %s\n", UNREACHABLE_CHAIN_FIELD_NAME);
193     if (!NSK_JNI_VERIFY(jni, (unreachableChainField =
194             jni->GetFieldID(rootObjectClass, UNREACHABLE_CHAIN_FIELD_NAME, CHAIN_OBJECT_CLASS_SIG)) != NULL)) {
195         nsk_jvmti_setFailStatus();
196         return NSK_FALSE;
197     }
198     NSK_DISPLAY1("  ... got fieldID: 0x%p\n", (void*)unreachableChainField);
199 
200     NSK_DISPLAY1("Find instance field in chain object class: %s\n", TAIL_FIELD_NAME);
201     if (!NSK_JNI_VERIFY(jni, (tailField =
202             jni->GetFieldID(chainObjectClass, TAIL_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*)tailField);
207 
208     NSK_DISPLAY1("Get root object from static field: %s\n", OBJECT_FIELD_NAME);
209     if (!NSK_JNI_VERIFY(jni, (rootObject =
210             jni->GetStaticObjectField(debugeeClass, objectField)) != NULL)) {
211         nsk_jvmti_setFailStatus();
212         return NSK_FALSE;
213     }
214     NSK_DISPLAY1("  ... got object: 0x%p\n", (void*)rootObject);
215 
216     NSK_DISPLAY0("Obtain and tag chain objects:\n");
217 
218     NSK_DISPLAY1("    reachable objects chain: %d objects\n", chainLength);
219     if (!getChainObjects(jvmti, jni, rootObject,
220                                 reachableChainField, REACHABLE_CHAIN_FIELD_NAME,
221                                 tailField, TAIL_FIELD_NAME,
222                                 chainLength, *objectDescList,
223                                 chainObjectTag, NSK_TRUE)) {
224         nsk_jvmti_setFailStatus();
225         return NSK_FALSE;
226     }
227 
228     NSK_DISPLAY1("    unreachable objects chain: %d objects\n", chainLength);
229     if (!getChainObjects(jvmti, jni, rootObject,
230                                 unreachableChainField, UNREACHABLE_CHAIN_FIELD_NAME,
231                                 tailField, TAIL_FIELD_NAME,
232                                 chainLength, *objectDescList + chainLength,
233                                 chainObjectTag, NSK_FALSE)) {
234         nsk_jvmti_setFailStatus();
235         return NSK_FALSE;
236     }
237 
238     return NSK_TRUE;
239 }
240 
241 /** Check if tagged objects were iterated. */
checkTestedObjects(jvmtiEnv * jvmti,JNIEnv * jni,int chainLength,ObjectDesc objectDescList[])242 static int checkTestedObjects(jvmtiEnv* jvmti, JNIEnv* jni,
243                                 int chainLength, ObjectDesc objectDescList[]) {
244     int success = NSK_TRUE;
245     int expectedUntagged = 0;
246     int i;
247 
248     NSK_DISPLAY0("Following tagged/untagged objects were iterated:\n");
249 
250     NSK_DISPLAY0("    reachable objects:\n");
251     for (i = 0; i < chainLength; i++) {
252         NSK_DISPLAY2("        tag=%-5ld iterated=%d times\n",
253                         (long)objectDescList[i].tag, objectDescList[i].found);
254 
255         if (objectDescList[i].found <= 0
256                     && objectDescList[i].tag != 0) {
257             NSK_COMPLAIN2("Reachable tagged object was not iterated:\n"
258                           "#   tag:      %ld\n"
259                           "#   iterated: %d times\n",
260                             (long)objectDescList[i].tag,
261                             objectDescList[i].found);
262             nsk_jvmti_setFailStatus();
263         }
264 
265         if (objectDescList[i].tag == 0) {
266             expectedUntagged++;
267         }
268     }
269 
270     NSK_DISPLAY0("    unreachable objects:\n");
271     for (i = 0; i < chainLength; i++) {
272         NSK_DISPLAY3("        tag=%-5ld iterated=%-3d collected=%d times\n",
273                         (long)objectDescList[i + chainLength].tag,
274                         objectDescList[i + chainLength].found,
275                         objectDescList[i + chainLength].collected);
276 
277         if (objectDescList[i + chainLength].found <= 0
278                     && objectDescList[i + chainLength].collected <= 0) {
279             NSK_COMPLAIN2("Not collected unreachable tagged object was not iterated:\n"
280                           "#   tag:      %ld\n"
281                           "#   iterated: %d times\n",
282                             (long)objectDescList[i + chainLength].tag,
283                             objectDescList[i + chainLength].found);
284             nsk_jvmti_setFailStatus();
285         }
286     }
287 
288     NSK_DISPLAY0("    untagged objects:\n");
289     NSK_DISPLAY2("        minimum=%-3d iterated=%d objects\n",
290                                     expectedUntagged, foundUntagged);
291     if (foundUntagged > 0) {
292         NSK_COMPLAIN2("A number of untagged objects were iterated:\n"
293                       "#   iterated untagged objects: %d\n"
294                       "#   untagged objects at least: %d\n",
295                         foundUntagged, expectedUntagged);
296         nsk_jvmti_setFailStatus();
297     }
298 
299     return NSK_TRUE;
300 }
301 
302 /** Release references to the tested objects and free allocated memory. */
releaseTestedObjects(jvmtiEnv * jvmti,JNIEnv * jni,int chainLength,ObjectDesc * objectDescList)303 static int releaseTestedObjects(jvmtiEnv* jvmti, JNIEnv* jni, int chainLength,
304                                                     ObjectDesc* objectDescList) {
305     if (objectDescList != NULL) {
306         NSK_DISPLAY1("Deallocate objects list: 0x%p\n", (void*)objectDescList);
307         if (!NSK_JVMTI_VERIFY(
308             jvmti->Deallocate((unsigned char*)objectDescList))) {
309             nsk_jvmti_setFailStatus();
310         }
311     }
312 
313     return NSK_TRUE;
314 }
315 
316 /* ============================================================================= */
317 
318 /** heapRootCallback for heap iterator. */
319 jvmtiIterationControl JNICALL
heapObjectCallback(jlong class_tag,jlong size,jlong * tag_ptr,void * user_data)320 heapObjectCallback(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data) {
321 
322     if (info & INFO_HEAPOBJ) {
323         NSK_DISPLAY3("  heapObjectCallback: class_tag=%-3ld size=%-3ld *tag_ptr=%-5ld\n",
324                         (long)class_tag, (long)size,
325                         (long)(tag_ptr == NULL ? (jlong)0 : *tag_ptr));
326     } else if ((info & INFO_TAGGED) != 0 &&
327                     tag_ptr != NULL && *tag_ptr != 0) {
328         NSK_DISPLAY3("  heapObjectCallback: class_tag=%-3ld size=%-3ld *tag_ptr=%-5ld\n",
329                         (long)class_tag, (long)size,
330                         (long)*tag_ptr);
331     }
332 
333     if (class_tag != 0) {
334         NSK_COMPLAIN3("Unexpected class_tag passed to heapObjectCallback:\n"
335                       "#   object tag:     %ld\n"
336                       "#   class_tag:      %ld\n"
337                       "#   size:           %ld\n",
338                         (long)*tag_ptr,
339                         (long)class_tag,
340                         (long)size);
341         nsk_jvmti_setFailStatus();
342     }
343 
344     if (tag_ptr == NULL) {
345         NSK_COMPLAIN3("NULL tag_ptr is passed to heapObjectCallback:\n"
346                       "#   tag_ptr:        0x%p\n"
347                       "#   class_tag:      %ld\n"
348                       "#   size:           %ld\n",
349                         (void*)tag_ptr,
350                         (long)class_tag,
351                         (long)size);
352         nsk_jvmti_setFailStatus();
353     } else {
354         if (*tag_ptr == 0) {
355             foundUntagged++;
356             NSK_COMPLAIN3("Untagged object passed to heapObjectCallback:\n"
357                           "#   tag:            %ld\n"
358                           "#   class_tag:      %ld\n"
359                           "#   size:           %ld\n",
360                             (long)*tag_ptr,
361                             (long)class_tag,
362                             (long)size);
363             nsk_jvmti_setFailStatus();
364         } else {
365             int found = 0;
366             int i;
367 
368             for (i = 0; i < objectsCount; i++) {
369                 if (*tag_ptr == objectDescList[i].tag) {
370                     found++;
371                     objectDescList[i].found++;
372                     break;
373                 }
374             }
375 
376             if (found <= 0) {
377                 NSK_COMPLAIN3("Unknown tagged object passed to heapObjectCallback:\n"
378                               "#   tag:            %ld\n"
379                               "#   class_tag:      %ld\n"
380                               "#   size:           %ld\n",
381                                 (long)*tag_ptr,
382                                 (long)class_tag,
383                                 (long)size);
384                 nsk_jvmti_setFailStatus();
385             }
386         }
387     }
388 
389     if (user_data != &fakeUserData && !userDataError) {
390        NSK_COMPLAIN2("Unexpected user_data is passed to heapObjectCallback:\n"
391                       "#   expected:       0x%p\n"
392                       "#   actual:         0x%p\n",
393                       user_data,
394                       &fakeUserData);
395         nsk_jvmti_setFailStatus();
396         userDataError++;
397     }
398 
399     return JVMTI_ITERATION_CONTINUE;
400 }
401 
402 /* ============================================================================= */
403 
404 /** Agent algorithm. */
405 static void JNICALL
agentProc(jvmtiEnv * jvmti,JNIEnv * jni,void * arg)406 agentProc(jvmtiEnv* jvmti, JNIEnv* jni, void* arg) {
407     NSK_DISPLAY0("Wait for tested objects created\n");
408     if (!NSK_VERIFY(nsk_jvmti_waitForSync(timeout)))
409         return;
410 
411     {
412         NSK_DISPLAY0(">>> Obtain and tag tested objects from debugee class\n");
413         {
414             if (!NSK_VERIFY(getTestedObjects(jvmti, jni, chainLength,
415                                                 &objectsCount, &objectDescList)))
416                 return;
417         }
418 
419         NSK_DISPLAY0(">>> Enable OBJECT_FREE event and let debugee to clean links to unreachable objects\n");
420         {
421             jvmtiEvent event = JVMTI_EVENT_OBJECT_FREE;
422             if (!NSK_VERIFY(nsk_jvmti_enableEvents(JVMTI_ENABLE, 1, &event, NULL)))
423                 return;
424 
425             if (!NSK_VERIFY(nsk_jvmti_resumeSync()))
426                 return;
427             if (!NSK_VERIFY(nsk_jvmti_waitForSync(timeout)))
428                 return;
429         }
430 
431         NSK_DISPLAY0(">>> Iterate over all object in heap with filter JVMTI_HEAP_OBJECT_TAGGED\n");
432         {
433             if (!NSK_JVMTI_VERIFY(
434                     jvmti->IterateOverHeap(JVMTI_HEAP_OBJECT_TAGGED, heapObjectCallback, &fakeUserData))) {
435                 nsk_jvmti_setFailStatus();
436                 return;
437             }
438         }
439 
440         NSK_DISPLAY0(">>> Disable OBJECT_FREE event and check if tagged objects were iterated:\n");
441         {
442             jvmtiEvent event = JVMTI_EVENT_OBJECT_FREE;
443             if (!NSK_VERIFY(nsk_jvmti_enableEvents(JVMTI_DISABLE, 1, &event, NULL)))
444                 return;
445 
446             if (!checkTestedObjects(jvmti, jni, chainLength, objectDescList)) {
447                 nsk_jvmti_setFailStatus();
448             }
449         }
450 
451         NSK_DISPLAY0(">>> Clean used data\n");
452         {
453             if (!NSK_VERIFY(releaseTestedObjects(jvmti, jni, chainLength, objectDescList)))
454                 return;
455         }
456     }
457 
458     NSK_DISPLAY0("Let debugee to finish\n");
459     if (!NSK_VERIFY(nsk_jvmti_resumeSync()))
460         return;
461 }
462 
463 /* ============================================================================= */
464 
465 JNIEXPORT void JNICALL
callbackObjectFree(jvmtiEnv * jvmti,jlong tag)466 callbackObjectFree(jvmtiEnv* jvmti, jlong tag) {
467     int i;
468 
469     if (info & INFO_HEAPOBJ) {
470         NSK_DISPLAY1("  <ObjectFree>: tag=%-5ld\n", tag);
471     }
472 
473     if (tag != 0) {
474         for (i = 0; i < objectsCount; i++) {
475             if (tag == objectDescList[i].tag) {
476                 objectDescList[i].collected++;
477                 break;
478             }
479         }
480     }
481 }
482 
483 /* ============================================================================= */
484 
485 /** Agent library initialization. */
486 #ifdef STATIC_BUILD
Agent_OnLoad_iterheap002(JavaVM * jvm,char * options,void * reserved)487 JNIEXPORT jint JNICALL Agent_OnLoad_iterheap002(JavaVM *jvm, char *options, void *reserved) {
488     return Agent_Initialize(jvm, options, reserved);
489 }
Agent_OnAttach_iterheap002(JavaVM * jvm,char * options,void * reserved)490 JNIEXPORT jint JNICALL Agent_OnAttach_iterheap002(JavaVM *jvm, char *options, void *reserved) {
491     return Agent_Initialize(jvm, options, reserved);
492 }
JNI_OnLoad_iterheap002(JavaVM * jvm,char * options,void * reserved)493 JNIEXPORT jint JNI_OnLoad_iterheap002(JavaVM *jvm, char *options, void *reserved) {
494     return JNI_VERSION_1_8;
495 }
496 #endif
Agent_Initialize(JavaVM * jvm,char * options,void * reserved)497 jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
498     jvmtiEnv* jvmti = NULL;
499 
500     if (!NSK_VERIFY(nsk_jvmti_parseOptions(options)))
501         return JNI_ERR;
502 
503     timeout = nsk_jvmti_getWaitTime() * 60 * 1000;
504 
505     {
506         const char* infoOpt = nsk_jvmti_findOptionValue("info");
507         if (infoOpt != NULL) {
508             if (strcmp(infoOpt, "none") == 0)
509                 info = INFO_NONE;
510             else if (strcmp(infoOpt, "all") == 0)
511                 info = INFO_ALL;
512             else if (strcmp(infoOpt, "objref") == 0)
513                 info = INFO_OBJREF;
514             else if (strcmp(infoOpt, "stackref") == 0)
515                 info = INFO_STACKREF;
516             else if (strcmp(infoOpt, "heaproot") == 0)
517                 info = INFO_HEAPROOT;
518             else if (strcmp(infoOpt, "heapobj") == 0)
519                 info = INFO_HEAPOBJ;
520             else if (strcmp(infoOpt, "tagged") == 0)
521                 info = INFO_TAGGED;
522             else {
523                 NSK_COMPLAIN1("Unknown option value: info=%s\n", infoOpt);
524                 return JNI_ERR;
525             }
526         }
527     }
528 
529     chainLength = nsk_jvmti_findOptionIntValue("objects", DEFAULT_CHAIN_LENGTH);
530     if (!NSK_VERIFY(chainLength > 0))
531         return JNI_ERR;
532 
533     if (!NSK_VERIFY((jvmti =
534             nsk_jvmti_createJVMTIEnv(jvm, reserved)) != NULL))
535         return JNI_ERR;
536 
537     {
538         jvmtiCapabilities caps;
539 
540         memset(&caps, 0, sizeof(caps));
541         caps.can_tag_objects = 1;
542         caps.can_generate_object_free_events = 1;
543         if (!NSK_JVMTI_VERIFY(
544                 jvmti->AddCapabilities(&caps))) {
545             return JNI_ERR;
546         }
547     }
548 
549     {
550         jvmtiEventCallbacks eventCallbacks;
551         memset(&eventCallbacks, 0, sizeof(eventCallbacks));
552         eventCallbacks.ObjectFree = callbackObjectFree;
553         if (!NSK_JVMTI_VERIFY(
554                 jvmti->SetEventCallbacks(&eventCallbacks, sizeof(eventCallbacks))))
555             return JNI_ERR;
556     }
557 
558     if (!NSK_VERIFY(nsk_jvmti_setAgentProc(agentProc, NULL)))
559         return JNI_ERR;
560 
561     return JNI_OK;
562 }
563 
564 /* ============================================================================= */
565 
566 }
567