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