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 if (weakRef == NULL) {
102 jvmtiDeallocate(node);
103 return NULL;
104 }
105
106 /* Set tag on weakRef */
107 error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag)
108 (gdata->jvmti, weakRef, ptr_to_jlong(node));
109 if ( error != JVMTI_ERROR_NONE ) {
110 JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, weakRef);
111 jvmtiDeallocate(node);
112 return NULL;
113 }
114
115 /* Fill in RefNode */
116 node->ref = weakRef;
117 node->isStrong = JNI_FALSE;
118 node->count = 1;
119 node->seqNum = newSeqNum();
120
121 /* Count RefNode's created */
122 gdata->objectsByIDcount++;
123 return node;
124 }
125
126 /* Delete a RefNode allocation, delete weak/global ref and clear tag */
127 static void
deleteNode(JNIEnv * env,RefNode * node)128 deleteNode(JNIEnv *env, RefNode *node)
129 {
130 LOG_MISC(("Freeing %d (%x)\n", (int)node->seqNum, node->ref));
131
132 if ( node->ref != NULL ) {
133 /* Clear tag */
134 (void)JVMTI_FUNC_PTR(gdata->jvmti,SetTag)
135 (gdata->jvmti, node->ref, NULL_OBJECT_ID);
136 if (node->isStrong) {
137 JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
138 } else {
139 JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
140 }
141 }
142 gdata->objectsByIDcount--;
143 jvmtiDeallocate(node);
144 }
145
146 /* Change a RefNode to have a strong reference */
147 static jobject
strengthenNode(JNIEnv * env,RefNode * node)148 strengthenNode(JNIEnv *env, RefNode *node)
149 {
150 if (!node->isStrong) {
151 jobject strongRef;
152
153 strongRef = JNI_FUNC_PTR(env,NewGlobalRef)(env, node->ref);
154 /*
155 * NewGlobalRef on a weak ref will return NULL if the weak
156 * reference has been collected or if out of memory.
157 * We need to distinguish those two occurrences.
158 */
159 if ((strongRef == NULL) && !isSameObject(env, node->ref, NULL)) {
160 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewGlobalRef");
161 }
162 if (strongRef != NULL) {
163 JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
164 node->ref = strongRef;
165 node->isStrong = JNI_TRUE;
166 }
167 return strongRef;
168 } else {
169 return node->ref;
170 }
171 }
172
173 /* Change a RefNode to have a weak reference */
174 static jweak
weakenNode(JNIEnv * env,RefNode * node)175 weakenNode(JNIEnv *env, RefNode *node)
176 {
177 if (node->isStrong) {
178 jweak weakRef;
179
180 weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, node->ref);
181 if (weakRef != NULL) {
182 JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
183 node->ref = weakRef;
184 node->isStrong = JNI_FALSE;
185 }
186 return weakRef;
187 } else {
188 return node->ref;
189 }
190 }
191
192 /*
193 * Returns the node which contains the common reference for the
194 * given object. The passed reference should not be a weak reference
195 * managed in the object hash table (i.e. returned by commonRef_idToRef)
196 * because no sequence number checking is done.
197 */
198 static RefNode *
findNodeByRef(JNIEnv * env,jobject ref)199 findNodeByRef(JNIEnv *env, jobject ref)
200 {
201 jvmtiError error;
202 jlong tag;
203
204 tag = NULL_OBJECT_ID;
205 error = JVMTI_FUNC_PTR(gdata->jvmti,GetTag)(gdata->jvmti, ref, &tag);
206 if ( error == JVMTI_ERROR_NONE ) {
207 RefNode *node;
208
209 node = (RefNode*)jlong_to_ptr(tag);
210 return node;
211 }
212 return NULL;
213 }
214
215 /* Locate and delete a node based on ID */
216 static void
deleteNodeByID(JNIEnv * env,jlong id,jint refCount)217 deleteNodeByID(JNIEnv *env, jlong id, jint refCount)
218 {
219 jint slot;
220 RefNode *node;
221 RefNode *prev;
222
223 slot = hashBucket(id);
224 node = gdata->objectsByID[slot];
225 prev = NULL;
226
227 while (node != NULL) {
228 if (id == node->seqNum) {
229 if (refCount != ALL_REFS) {
230 node->count -= refCount;
231 } else {
232 node->count = 0;
233 }
234 if (node->count <= 0) {
235 if ( node->count < 0 ) {
236 EXIT_ERROR(AGENT_ERROR_INTERNAL,"RefNode count < 0");
237 }
238 /* Detach from id hash table */
239 if (prev == NULL) {
240 gdata->objectsByID[slot] = node->next;
241 } else {
242 prev->next = node->next;
243 }
244 deleteNode(env, node);
245 }
246 break;
247 }
248 prev = node;
249 node = node->next;
250 }
251 }
252
253 /*
254 * Returns the node stored in the object hash table for the given object
255 * id. The id should be a value previously returned by
256 * commonRef_refToID.
257 *
258 * NOTE: It is possible that a match is found here, but that the object
259 * is garbage collected by the time the caller inspects node->ref.
260 * Callers should take care using the node->ref object returned here.
261 *
262 */
263 static RefNode *
findNodeByID(JNIEnv * env,jlong id)264 findNodeByID(JNIEnv *env, jlong id)
265 {
266 jint slot;
267 RefNode *node;
268 RefNode *prev;
269
270 slot = hashBucket(id);
271 node = gdata->objectsByID[slot];
272 prev = NULL;
273
274 while (node != NULL) {
275 if ( id == node->seqNum ) {
276 if ( prev != NULL ) {
277 /* Re-order hash list so this one is up front */
278 prev->next = node->next;
279 node->next = gdata->objectsByID[slot];
280 gdata->objectsByID[slot] = node;
281 }
282 break;
283 }
284 node = node->next;
285 }
286 return node;
287 }
288
289 /* Initialize the hash table stored in gdata area */
290 static void
initializeObjectsByID(int size)291 initializeObjectsByID(int size)
292 {
293 /* Size should always be a power of 2 */
294 if ( size > HASH_MAX_SIZE ) size = HASH_MAX_SIZE;
295 gdata->objectsByIDsize = size;
296 gdata->objectsByIDcount = 0;
297 gdata->objectsByID = (RefNode**)jvmtiAllocate((int)sizeof(RefNode*)*size);
298 (void)memset(gdata->objectsByID, 0, (int)sizeof(RefNode*)*size);
299 }
300
301 /* hash in a RefNode */
302 static void
hashIn(RefNode * node)303 hashIn(RefNode *node)
304 {
305 jint slot;
306
307 /* Add to id hashtable */
308 slot = hashBucket(node->seqNum);
309 node->next = gdata->objectsByID[slot];
310 gdata->objectsByID[slot] = node;
311 }
312
313 /* Allocate and add RefNode to hash table */
314 static RefNode *
newCommonRef(JNIEnv * env,jobject ref)315 newCommonRef(JNIEnv *env, jobject ref)
316 {
317 RefNode *node;
318
319 /* Allocate the node and set it up */
320 node = createNode(env, ref);
321 if ( node == NULL ) {
322 return NULL;
323 }
324
325 /* See if hash table needs expansion */
326 if ( gdata->objectsByIDcount > gdata->objectsByIDsize*HASH_EXPAND_SCALE &&
327 gdata->objectsByIDsize < HASH_MAX_SIZE ) {
328 RefNode **old;
329 int oldsize;
330 int newsize;
331 int i;
332
333 /* Save old information */
334 old = gdata->objectsByID;
335 oldsize = gdata->objectsByIDsize;
336 /* Allocate new hash table */
337 gdata->objectsByID = NULL;
338 newsize = oldsize*HASH_EXPAND_SCALE;
339 if ( newsize > HASH_MAX_SIZE ) newsize = HASH_MAX_SIZE;
340 initializeObjectsByID(newsize);
341 /* Walk over old one and hash in all the RefNodes */
342 for ( i = 0 ; i < oldsize ; i++ ) {
343 RefNode *onode;
344
345 onode = old[i];
346 while (onode != NULL) {
347 RefNode *next;
348
349 next = onode->next;
350 hashIn(onode);
351 onode = next;
352 }
353 }
354 jvmtiDeallocate(old);
355 }
356
357 /* Add to id hashtable */
358 hashIn(node);
359 return node;
360 }
361
362 /* Initialize the commonRefs usage */
363 void
commonRef_initialize(void)364 commonRef_initialize(void)
365 {
366 gdata->refLock = debugMonitorCreate("JDWP Reference Table Monitor");
367 gdata->nextSeqNum = 1; /* 0 used for error indication */
368 initializeObjectsByID(HASH_INIT_SIZE);
369 }
370
371 /* Reset the commonRefs usage */
372 void
commonRef_reset(JNIEnv * env)373 commonRef_reset(JNIEnv *env)
374 {
375 debugMonitorEnter(gdata->refLock); {
376 int i;
377
378 for (i = 0; i < gdata->objectsByIDsize; i++) {
379 RefNode *node;
380
381 node = gdata->objectsByID[i];
382 while (node != NULL) {
383 RefNode *next;
384
385 next = node->next;
386 deleteNode(env, node);
387 node = next;
388 }
389 gdata->objectsByID[i] = NULL;
390 }
391
392 /* Toss entire hash table and re-create a new one */
393 jvmtiDeallocate(gdata->objectsByID);
394 gdata->objectsByID = NULL;
395 gdata->nextSeqNum = 1; /* 0 used for error indication */
396 initializeObjectsByID(HASH_INIT_SIZE);
397
398 } debugMonitorExit(gdata->refLock);
399 }
400
401 /*
402 * Given a reference obtained from JNI or JVMTI, return an object
403 * id suitable for sending to the debugger front end.
404 */
405 jlong
commonRef_refToID(JNIEnv * env,jobject ref)406 commonRef_refToID(JNIEnv *env, jobject ref)
407 {
408 jlong id;
409
410 if (ref == NULL) {
411 return NULL_OBJECT_ID;
412 }
413
414 id = NULL_OBJECT_ID;
415 debugMonitorEnter(gdata->refLock); {
416 RefNode *node;
417
418 node = findNodeByRef(env, ref);
419 if (node == NULL) {
420 node = newCommonRef(env, ref);
421 if ( node != NULL ) {
422 id = node->seqNum;
423 }
424 } else {
425 id = node->seqNum;
426 node->count++;
427 }
428 } debugMonitorExit(gdata->refLock);
429 return id;
430 }
431
432 /*
433 * Given an object ID obtained from the debugger front end, return a
434 * strong, global reference to that object (or NULL if the object
435 * has been collected). The reference can then be used for JNI and
436 * JVMTI calls. Caller is resposible for deleting the returned reference.
437 */
438 jobject
commonRef_idToRef(JNIEnv * env,jlong id)439 commonRef_idToRef(JNIEnv *env, jlong id)
440 {
441 jobject ref;
442
443 ref = NULL;
444 debugMonitorEnter(gdata->refLock); {
445 RefNode *node;
446
447 node = findNodeByID(env, id);
448 if (node != NULL) {
449 if (node->isStrong) {
450 saveGlobalRef(env, node->ref, &ref);
451 } else {
452 jobject lref;
453
454 lref = JNI_FUNC_PTR(env,NewLocalRef)(env, node->ref);
455 if ( lref == NULL ) {
456 /* Object was GC'd shortly after we found the node */
457 deleteNodeByID(env, node->seqNum, ALL_REFS);
458 } else {
459 saveGlobalRef(env, node->ref, &ref);
460 JNI_FUNC_PTR(env,DeleteLocalRef)(env, lref);
461 }
462 }
463 }
464 } debugMonitorExit(gdata->refLock);
465 return ref;
466 }
467
468 /* Deletes the global reference that commonRef_idToRef() created */
469 void
commonRef_idToRef_delete(JNIEnv * env,jobject ref)470 commonRef_idToRef_delete(JNIEnv *env, jobject ref)
471 {
472 if ( ref==NULL ) {
473 return;
474 }
475 tossGlobalRef(env, &ref);
476 }
477
478
479 /* Prevent garbage collection of an object */
480 jvmtiError
commonRef_pin(jlong id)481 commonRef_pin(jlong id)
482 {
483 jvmtiError error;
484
485 error = JVMTI_ERROR_NONE;
486 if (id == NULL_OBJECT_ID) {
487 return error;
488 }
489 debugMonitorEnter(gdata->refLock); {
490 JNIEnv *env;
491 RefNode *node;
492
493 env = getEnv();
494 node = findNodeByID(env, id);
495 if (node == NULL) {
496 error = AGENT_ERROR_INVALID_OBJECT;
497 } else {
498 jobject strongRef;
499
500 strongRef = strengthenNode(env, node);
501 if (strongRef == NULL) {
502 /*
503 * Referent has been collected, clean up now.
504 */
505 error = AGENT_ERROR_INVALID_OBJECT;
506 deleteNodeByID(env, id, ALL_REFS);
507 }
508 }
509 } debugMonitorExit(gdata->refLock);
510 return error;
511 }
512
513 /* Permit garbage collection of an object */
514 jvmtiError
commonRef_unpin(jlong id)515 commonRef_unpin(jlong id)
516 {
517 jvmtiError error;
518
519 error = JVMTI_ERROR_NONE;
520 debugMonitorEnter(gdata->refLock); {
521 JNIEnv *env;
522 RefNode *node;
523
524 env = getEnv();
525 node = findNodeByID(env, id);
526 if (node != NULL) {
527 jweak weakRef;
528
529 weakRef = weakenNode(env, node);
530 if (weakRef == NULL) {
531 error = AGENT_ERROR_OUT_OF_MEMORY;
532 }
533 }
534 } debugMonitorExit(gdata->refLock);
535 return error;
536 }
537
538 /* Release tracking of an object by ID */
539 void
commonRef_release(JNIEnv * env,jlong id)540 commonRef_release(JNIEnv *env, jlong id)
541 {
542 debugMonitorEnter(gdata->refLock); {
543 deleteNodeByID(env, id, 1);
544 } debugMonitorExit(gdata->refLock);
545 }
546
547 void
commonRef_releaseMultiple(JNIEnv * env,jlong id,jint refCount)548 commonRef_releaseMultiple(JNIEnv *env, jlong id, jint refCount)
549 {
550 debugMonitorEnter(gdata->refLock); {
551 deleteNodeByID(env, id, refCount);
552 } debugMonitorExit(gdata->refLock);
553 }
554
555 /* Get rid of RefNodes for objects that no longer exist */
556 void
commonRef_compact(void)557 commonRef_compact(void)
558 {
559 JNIEnv *env;
560 RefNode *node;
561 RefNode *prev;
562 int i;
563
564 env = getEnv();
565 debugMonitorEnter(gdata->refLock); {
566 if ( gdata->objectsByIDsize > 0 ) {
567 /*
568 * Walk through the id-based hash table. Detach any nodes
569 * for which the ref has been collected.
570 */
571 for (i = 0; i < gdata->objectsByIDsize; i++) {
572 node = gdata->objectsByID[i];
573 prev = NULL;
574 while (node != NULL) {
575 /* Has the object been collected? */
576 if ( (!node->isStrong) &&
577 isSameObject(env, node->ref, NULL)) {
578 RefNode *freed;
579
580 /* Detach from the ID list */
581 if (prev == NULL) {
582 gdata->objectsByID[i] = node->next;
583 } else {
584 prev->next = node->next;
585 }
586 freed = node;
587 node = node->next;
588 deleteNode(env, freed);
589 } else {
590 prev = node;
591 node = node->next;
592 }
593 }
594 }
595 }
596 } debugMonitorExit(gdata->refLock);
597 }
598
599 /* Lock the commonRef tables */
600 void
commonRef_lock(void)601 commonRef_lock(void)
602 {
603 debugMonitorEnter(gdata->refLock);
604 }
605
606 /* Unlock the commonRef tables */
607 void
commonRef_unlock(void)608 commonRef_unlock(void)
609 {
610 debugMonitorExit(gdata->refLock);
611 }
612