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