1 /*
2 * Copyright (c) 1998, 2013, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 #if defined(_ALLBSD_SOURCE)
27 #include <stdint.h> /* for uintptr_t */
28 #endif
29
30 #include "util.h"
31 #include "commonRef.h"
32
33 #define ALL_REFS -1
34
35 /*
36 * Each object sent to the front end is tracked with the RefNode struct
37 * (see util.h).
38 * External to this module, objects are identified by a jlong id which is
39 * simply the sequence number. A weak reference is usually used so that
40 * the presence of a debugger-tracked object will not prevent
41 * its collection. Once an object is collected, its RefNode may be
42 * deleted and the weak ref inside may be reused (these may happen in
43 * either order). Using the sequence number
44 * as the object id prevents ambiguity in the object id when the weak ref
45 * is reused. The RefNode* is stored with the object as it's JVMTI Tag.
46 *
47 * The ref member is changed from weak to strong when
48 * gc of the object is to be prevented.
49 * Whether or not it is strong, it is never exported from this module.
50 *
51 * A reference count of each jobject is also maintained here. It tracks
52 * the number times an object has been referenced through
53 * commonRef_refToID. A RefNode is freed once the reference
54 * count is decremented to 0 (with commonRef_release*), even if the
55 * corresponding object has not been collected.
56 *
57 * One hash table is maintained. The mapping of ID to jobject (or RefNode*)
58 * is handled with one hash table that will re-size itself as the number
59 * of RefNode's grow.
60 */
61
62 /* Initial hash table size (must be power of 2) */
63 #define HASH_INIT_SIZE 512
64 /* If element count exceeds HASH_EXPAND_SCALE*hash_size we expand & re-hash */
65 #define HASH_EXPAND_SCALE 8
66 /* Maximum hash table size (must be power of 2) */
67 #define HASH_MAX_SIZE (1024*HASH_INIT_SIZE)
68
69 /* Map a key (ID) to a hash bucket */
70 static jint
hashBucket(jlong key)71 hashBucket(jlong key)
72 {
73 /* Size should always be a power of 2, use mask instead of mod operator */
74 /*LINTED*/
75 return ((jint)key) & (gdata->objectsByIDsize-1);
76 }
77
78 /* Generate a new ID */
79 static jlong
newSeqNum(void)80 newSeqNum(void)
81 {
82 return gdata->nextSeqNum++;
83 }
84
85 /* Create a fresh RefNode structure, create a weak ref and tag the object */
86 static RefNode *
createNode(JNIEnv * env,jobject ref)87 createNode(JNIEnv *env, jobject ref)
88 {
89 RefNode *node;
90 jobject weakRef;
91 jvmtiError error;
92
93 /* Could allocate RefNode's in blocks, not sure it would help much */
94 node = (RefNode*)jvmtiAllocate((int)sizeof(RefNode));
95 if (node == NULL) {
96 return NULL;
97 }
98
99 /* Create weak reference to make sure we have a reference */
100 weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, ref);
101 // NewWeakGlobalRef can throw OOM, clear exception here.
102 if ((*env)->ExceptionCheck(env)) {
103 (*env)->ExceptionClear(env);
104 jvmtiDeallocate(node);
105 return NULL;
106 }
107
108 /* Set tag on weakRef */
109 error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag)
110 (gdata->jvmti, weakRef, ptr_to_jlong(node));
111 if ( error != JVMTI_ERROR_NONE ) {
112 JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, weakRef);
113 jvmtiDeallocate(node);
114 return NULL;
115 }
116
117 /* Fill in RefNode */
118 node->ref = weakRef;
119 node->isStrong = JNI_FALSE;
120 node->count = 1;
121 node->seqNum = newSeqNum();
122
123 /* Count RefNode's created */
124 gdata->objectsByIDcount++;
125 return node;
126 }
127
128 /* Delete a RefNode allocation, delete weak/global ref and clear tag */
129 static void
deleteNode(JNIEnv * env,RefNode * node)130 deleteNode(JNIEnv *env, RefNode *node)
131 {
132 LOG_MISC(("Freeing %d (%x)\n", (int)node->seqNum, node->ref));
133
134 if ( node->ref != NULL ) {
135 /* Clear tag */
136 (void)JVMTI_FUNC_PTR(gdata->jvmti,SetTag)
137 (gdata->jvmti, node->ref, NULL_OBJECT_ID);
138 if (node->isStrong) {
139 JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
140 } else {
141 JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
142 }
143 }
144 gdata->objectsByIDcount--;
145 jvmtiDeallocate(node);
146 }
147
148 /* Change a RefNode to have a strong reference */
149 static jobject
strengthenNode(JNIEnv * env,RefNode * node)150 strengthenNode(JNIEnv *env, RefNode *node)
151 {
152 if (!node->isStrong) {
153 jobject strongRef;
154
155 strongRef = JNI_FUNC_PTR(env,NewGlobalRef)(env, node->ref);
156 /*
157 * NewGlobalRef on a weak ref will return NULL if the weak
158 * reference has been collected or if out of memory.
159 * It never throws OOM.
160 * We need to distinguish those two occurrences.
161 */
162 if ((strongRef == NULL) && !isSameObject(env, node->ref, NULL)) {
163 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewGlobalRef");
164 }
165 if (strongRef != NULL) {
166 JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
167 node->ref = strongRef;
168 node->isStrong = JNI_TRUE;
169 }
170 return strongRef;
171 } else {
172 return node->ref;
173 }
174 }
175
176 /* Change a RefNode to have a weak reference */
177 static jweak
weakenNode(JNIEnv * env,RefNode * node)178 weakenNode(JNIEnv *env, RefNode *node)
179 {
180 if (node->isStrong) {
181 jweak weakRef;
182
183 weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, node->ref);
184 // NewWeakGlobalRef can throw OOM, clear exception here.
185 if ((*env)->ExceptionCheck(env)) {
186 (*env)->ExceptionClear(env);
187 }
188
189 if (weakRef != NULL) {
190 JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
191 node->ref = weakRef;
192 node->isStrong = JNI_FALSE;
193 }
194 return weakRef;
195 } else {
196 return node->ref;
197 }
198 }
199
200 /*
201 * Returns the node which contains the common reference for the
202 * given object. The passed reference should not be a weak reference
203 * managed in the object hash table (i.e. returned by commonRef_idToRef)
204 * because no sequence number checking is done.
205 */
206 static RefNode *
findNodeByRef(JNIEnv * env,jobject ref)207 findNodeByRef(JNIEnv *env, jobject ref)
208 {
209 jvmtiError error;
210 jlong tag;
211
212 tag = NULL_OBJECT_ID;
213 error = JVMTI_FUNC_PTR(gdata->jvmti,GetTag)(gdata->jvmti, ref, &tag);
214 if ( error == JVMTI_ERROR_NONE ) {
215 RefNode *node;
216
217 node = (RefNode*)jlong_to_ptr(tag);
218 return node;
219 }
220 return NULL;
221 }
222
223 /* Locate and delete a node based on ID */
224 static void
deleteNodeByID(JNIEnv * env,jlong id,jint refCount)225 deleteNodeByID(JNIEnv *env, jlong id, jint refCount)
226 {
227 jint slot;
228 RefNode *node;
229 RefNode *prev;
230
231 slot = hashBucket(id);
232 node = gdata->objectsByID[slot];
233 prev = NULL;
234
235 while (node != NULL) {
236 if (id == node->seqNum) {
237 if (refCount != ALL_REFS) {
238 node->count -= refCount;
239 } else {
240 node->count = 0;
241 }
242 if (node->count <= 0) {
243 if ( node->count < 0 ) {
244 EXIT_ERROR(AGENT_ERROR_INTERNAL,"RefNode count < 0");
245 }
246 /* Detach from id hash table */
247 if (prev == NULL) {
248 gdata->objectsByID[slot] = node->next;
249 } else {
250 prev->next = node->next;
251 }
252 deleteNode(env, node);
253 }
254 break;
255 }
256 prev = node;
257 node = node->next;
258 }
259 }
260
261 /*
262 * Returns the node stored in the object hash table for the given object
263 * id. The id should be a value previously returned by
264 * commonRef_refToID.
265 *
266 * NOTE: It is possible that a match is found here, but that the object
267 * is garbage collected by the time the caller inspects node->ref.
268 * Callers should take care using the node->ref object returned here.
269 *
270 */
271 static RefNode *
findNodeByID(JNIEnv * env,jlong id)272 findNodeByID(JNIEnv *env, jlong id)
273 {
274 jint slot;
275 RefNode *node;
276 RefNode *prev;
277
278 slot = hashBucket(id);
279 node = gdata->objectsByID[slot];
280 prev = NULL;
281
282 while (node != NULL) {
283 if ( id == node->seqNum ) {
284 if ( prev != NULL ) {
285 /* Re-order hash list so this one is up front */
286 prev->next = node->next;
287 node->next = gdata->objectsByID[slot];
288 gdata->objectsByID[slot] = node;
289 }
290 break;
291 }
292 node = node->next;
293 }
294 return node;
295 }
296
297 /* Initialize the hash table stored in gdata area */
298 static void
initializeObjectsByID(int size)299 initializeObjectsByID(int size)
300 {
301 /* Size should always be a power of 2 */
302 if ( size > HASH_MAX_SIZE ) size = HASH_MAX_SIZE;
303 gdata->objectsByIDsize = size;
304 gdata->objectsByIDcount = 0;
305 gdata->objectsByID = (RefNode**)jvmtiAllocate((int)sizeof(RefNode*)*size);
306 (void)memset(gdata->objectsByID, 0, (int)sizeof(RefNode*)*size);
307 }
308
309 /* hash in a RefNode */
310 static void
hashIn(RefNode * node)311 hashIn(RefNode *node)
312 {
313 jint slot;
314
315 /* Add to id hashtable */
316 slot = hashBucket(node->seqNum);
317 node->next = gdata->objectsByID[slot];
318 gdata->objectsByID[slot] = node;
319 }
320
321 /* Allocate and add RefNode to hash table */
322 static RefNode *
newCommonRef(JNIEnv * env,jobject ref)323 newCommonRef(JNIEnv *env, jobject ref)
324 {
325 RefNode *node;
326
327 /* Allocate the node and set it up */
328 node = createNode(env, ref);
329 if ( node == NULL ) {
330 return NULL;
331 }
332
333 /* See if hash table needs expansion */
334 if ( gdata->objectsByIDcount > gdata->objectsByIDsize*HASH_EXPAND_SCALE &&
335 gdata->objectsByIDsize < HASH_MAX_SIZE ) {
336 RefNode **old;
337 int oldsize;
338 int newsize;
339 int i;
340
341 /* Save old information */
342 old = gdata->objectsByID;
343 oldsize = gdata->objectsByIDsize;
344 /* Allocate new hash table */
345 gdata->objectsByID = NULL;
346 newsize = oldsize*HASH_EXPAND_SCALE;
347 if ( newsize > HASH_MAX_SIZE ) newsize = HASH_MAX_SIZE;
348 initializeObjectsByID(newsize);
349 /* Walk over old one and hash in all the RefNodes */
350 for ( i = 0 ; i < oldsize ; i++ ) {
351 RefNode *onode;
352
353 onode = old[i];
354 while (onode != NULL) {
355 RefNode *next;
356
357 next = onode->next;
358 hashIn(onode);
359 onode = next;
360 }
361 }
362 jvmtiDeallocate(old);
363 }
364
365 /* Add to id hashtable */
366 hashIn(node);
367 return node;
368 }
369
370 /* Initialize the commonRefs usage */
371 void
commonRef_initialize(void)372 commonRef_initialize(void)
373 {
374 gdata->refLock = debugMonitorCreate("JDWP Reference Table Monitor");
375 gdata->nextSeqNum = 1; /* 0 used for error indication */
376 initializeObjectsByID(HASH_INIT_SIZE);
377 }
378
379 /* Reset the commonRefs usage */
380 void
commonRef_reset(JNIEnv * env)381 commonRef_reset(JNIEnv *env)
382 {
383 debugMonitorEnter(gdata->refLock); {
384 int i;
385
386 for (i = 0; i < gdata->objectsByIDsize; i++) {
387 RefNode *node;
388
389 node = gdata->objectsByID[i];
390 while (node != NULL) {
391 RefNode *next;
392
393 next = node->next;
394 deleteNode(env, node);
395 node = next;
396 }
397 gdata->objectsByID[i] = NULL;
398 }
399
400 /* Toss entire hash table and re-create a new one */
401 jvmtiDeallocate(gdata->objectsByID);
402 gdata->objectsByID = NULL;
403 gdata->nextSeqNum = 1; /* 0 used for error indication */
404 initializeObjectsByID(HASH_INIT_SIZE);
405
406 } debugMonitorExit(gdata->refLock);
407 }
408
409 /*
410 * Given a reference obtained from JNI or JVMTI, return an object
411 * id suitable for sending to the debugger front end.
412 */
413 jlong
commonRef_refToID(JNIEnv * env,jobject ref)414 commonRef_refToID(JNIEnv *env, jobject ref)
415 {
416 jlong id;
417
418 if (ref == NULL) {
419 return NULL_OBJECT_ID;
420 }
421
422 id = NULL_OBJECT_ID;
423 debugMonitorEnter(gdata->refLock); {
424 RefNode *node;
425
426 node = findNodeByRef(env, ref);
427 if (node == NULL) {
428 node = newCommonRef(env, ref);
429 if ( node != NULL ) {
430 id = node->seqNum;
431 }
432 } else {
433 id = node->seqNum;
434 node->count++;
435 }
436 } debugMonitorExit(gdata->refLock);
437 return id;
438 }
439
440 /*
441 * Given an object ID obtained from the debugger front end, return a
442 * strong, global reference to that object (or NULL if the object
443 * has been collected). The reference can then be used for JNI and
444 * JVMTI calls. Caller is resposible for deleting the returned reference.
445 */
446 jobject
commonRef_idToRef(JNIEnv * env,jlong id)447 commonRef_idToRef(JNIEnv *env, jlong id)
448 {
449 jobject ref;
450
451 ref = NULL;
452 debugMonitorEnter(gdata->refLock); {
453 RefNode *node;
454
455 node = findNodeByID(env, id);
456 if (node != NULL) {
457 if (node->isStrong) {
458 saveGlobalRef(env, node->ref, &ref);
459 } else {
460 jobject lref;
461
462 lref = JNI_FUNC_PTR(env,NewLocalRef)(env, node->ref);
463 // NewLocalRef never throws OOM.
464 if ( lref == NULL ) {
465 /* Object was GC'd shortly after we found the node */
466 deleteNodeByID(env, node->seqNum, ALL_REFS);
467 } else {
468 saveGlobalRef(env, node->ref, &ref);
469 JNI_FUNC_PTR(env,DeleteLocalRef)(env, lref);
470 }
471 }
472 }
473 } debugMonitorExit(gdata->refLock);
474 return ref;
475 }
476
477 /* Deletes the global reference that commonRef_idToRef() created */
478 void
commonRef_idToRef_delete(JNIEnv * env,jobject ref)479 commonRef_idToRef_delete(JNIEnv *env, jobject ref)
480 {
481 if ( ref==NULL ) {
482 return;
483 }
484 tossGlobalRef(env, &ref);
485 }
486
487
488 /* Prevent garbage collection of an object */
489 jvmtiError
commonRef_pin(jlong id)490 commonRef_pin(jlong id)
491 {
492 jvmtiError error;
493
494 error = JVMTI_ERROR_NONE;
495 if (id == NULL_OBJECT_ID) {
496 return error;
497 }
498 debugMonitorEnter(gdata->refLock); {
499 JNIEnv *env;
500 RefNode *node;
501
502 env = getEnv();
503 node = findNodeByID(env, id);
504 if (node == NULL) {
505 error = AGENT_ERROR_INVALID_OBJECT;
506 } else {
507 jobject strongRef;
508
509 strongRef = strengthenNode(env, node);
510 if (strongRef == NULL) {
511 /*
512 * Referent has been collected, clean up now.
513 */
514 error = AGENT_ERROR_INVALID_OBJECT;
515 deleteNodeByID(env, id, ALL_REFS);
516 }
517 }
518 } debugMonitorExit(gdata->refLock);
519 return error;
520 }
521
522 /* Permit garbage collection of an object */
523 jvmtiError
commonRef_unpin(jlong id)524 commonRef_unpin(jlong id)
525 {
526 jvmtiError error;
527
528 error = JVMTI_ERROR_NONE;
529 debugMonitorEnter(gdata->refLock); {
530 JNIEnv *env;
531 RefNode *node;
532
533 env = getEnv();
534 node = findNodeByID(env, id);
535 if (node != NULL) {
536 jweak weakRef;
537
538 weakRef = weakenNode(env, node);
539 if (weakRef == NULL) {
540 error = AGENT_ERROR_OUT_OF_MEMORY;
541 }
542 }
543 } debugMonitorExit(gdata->refLock);
544 return error;
545 }
546
547 /* Release tracking of an object by ID */
548 void
commonRef_release(JNIEnv * env,jlong id)549 commonRef_release(JNIEnv *env, jlong id)
550 {
551 debugMonitorEnter(gdata->refLock); {
552 deleteNodeByID(env, id, 1);
553 } debugMonitorExit(gdata->refLock);
554 }
555
556 void
commonRef_releaseMultiple(JNIEnv * env,jlong id,jint refCount)557 commonRef_releaseMultiple(JNIEnv *env, jlong id, jint refCount)
558 {
559 debugMonitorEnter(gdata->refLock); {
560 deleteNodeByID(env, id, refCount);
561 } debugMonitorExit(gdata->refLock);
562 }
563
564 /* Get rid of RefNodes for objects that no longer exist */
565 void
commonRef_compact(void)566 commonRef_compact(void)
567 {
568 JNIEnv *env;
569 RefNode *node;
570 RefNode *prev;
571 int i;
572
573 env = getEnv();
574 debugMonitorEnter(gdata->refLock); {
575 if ( gdata->objectsByIDsize > 0 ) {
576 /*
577 * Walk through the id-based hash table. Detach any nodes
578 * for which the ref has been collected.
579 */
580 for (i = 0; i < gdata->objectsByIDsize; i++) {
581 node = gdata->objectsByID[i];
582 prev = NULL;
583 while (node != NULL) {
584 /* Has the object been collected? */
585 if ( (!node->isStrong) &&
586 isSameObject(env, node->ref, NULL)) {
587 RefNode *freed;
588
589 /* Detach from the ID list */
590 if (prev == NULL) {
591 gdata->objectsByID[i] = node->next;
592 } else {
593 prev->next = node->next;
594 }
595 freed = node;
596 node = node->next;
597 deleteNode(env, freed);
598 } else {
599 prev = node;
600 node = node->next;
601 }
602 }
603 }
604 }
605 } debugMonitorExit(gdata->refLock);
606 }
607
608 /* Lock the commonRef tables */
609 void
commonRef_lock(void)610 commonRef_lock(void)
611 {
612 debugMonitorEnter(gdata->refLock);
613 }
614
615 /* Unlock the commonRef tables */
616 void
commonRef_unlock(void)617 commonRef_unlock(void)
618 {
619 debugMonitorExit(gdata->refLock);
620 }
621