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 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 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 * 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 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 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 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 * 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 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 * 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 601 commonRef_lock(void) 602 { 603 debugMonitorEnter(gdata->refLock); 604 } 605 606 /* Unlock the commonRef tables */ 607 void 608 commonRef_unlock(void) 609 { 610 debugMonitorExit(gdata->refLock); 611 } 612