1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /* 18 * Test the indirect reference table implementation. 19 */ 20 #include "Dalvik.h" 21 22 #include <stdlib.h> 23 24 #ifndef NDEBUG 25 26 #define DBUG_MSG LOGV 27 28 /* 29 * Basic add/get/delete tests in an unsegmented table. 30 */ 31 static bool basicTest(void) 32 { 33 static const int kTableMax = 20; 34 IndirectRefTable irt; 35 IndirectRef iref0, iref1, iref2, iref3; 36 IndirectRef manyRefs[kTableMax]; 37 ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL); 38 Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 39 Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 40 Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 41 Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 42 const u4 cookie = IRT_FIRST_SEGMENT; 43 bool result = false; 44 45 if (!dvmInitIndirectRefTable(&irt, kTableMax/2, kTableMax, 46 kIndirectKindGlobal)) 47 { 48 return false; 49 } 50 51 iref0 = (IndirectRef) 0x11110; 52 if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) { 53 LOGE("unexpectedly successful removal\n"); 54 goto bail; 55 } 56 57 /* 58 * Add three, check, remove in the order in which they were added. 59 */ 60 DBUG_MSG("+++ START fifo\n"); 61 iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0); 62 iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1); 63 iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2); 64 if (iref0 == NULL || iref1 == NULL || iref2 == NULL) { 65 LOGE("trivial add1 failed\n"); 66 goto bail; 67 } 68 69 if (dvmGetFromIndirectRefTable(&irt, iref0) != obj0 || 70 dvmGetFromIndirectRefTable(&irt, iref1) != obj1 || 71 dvmGetFromIndirectRefTable(&irt, iref2) != obj2) 72 { 73 LOGE("objects don't match expected values %p %p %p vs. %p %p %p\n", 74 dvmGetFromIndirectRefTable(&irt, iref0), 75 dvmGetFromIndirectRefTable(&irt, iref1), 76 dvmGetFromIndirectRefTable(&irt, iref2), 77 obj0, obj1, obj2); 78 goto bail; 79 } else { 80 DBUG_MSG("+++ obj1=%p --> iref1=%p\n", obj1, iref1); 81 } 82 83 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) || 84 !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) || 85 !dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) 86 { 87 LOGE("fifo deletion failed\n"); 88 goto bail; 89 } 90 91 /* table should be empty now */ 92 if (dvmIndirectRefTableEntries(&irt) != 0) { 93 LOGE("fifo del not empty\n"); 94 goto bail; 95 } 96 97 /* get invalid entry (off the end of the list) */ 98 if (dvmGetFromIndirectRefTable(&irt, iref0) != NULL) { 99 LOGE("stale entry get succeeded unexpectedly\n"); 100 goto bail; 101 } 102 103 /* 104 * Add three, remove in the opposite order. 105 */ 106 DBUG_MSG("+++ START lifo\n"); 107 iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0); 108 iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1); 109 iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2); 110 if (iref0 == NULL || iref1 == NULL || iref2 == NULL) { 111 LOGE("trivial add2 failed\n"); 112 goto bail; 113 } 114 115 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) || 116 !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) || 117 !dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) 118 { 119 LOGE("lifo deletion failed\n"); 120 goto bail; 121 } 122 123 /* table should be empty now */ 124 if (dvmIndirectRefTableEntries(&irt) != 0) { 125 LOGE("lifo del not empty\n"); 126 goto bail; 127 } 128 129 /* 130 * Add three, remove middle / middle / bottom / top. (Second attempt 131 * to remove middle should fail.) 132 */ 133 DBUG_MSG("+++ START unorder\n"); 134 iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0); 135 iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1); 136 iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2); 137 if (iref0 == NULL || iref1 == NULL || iref2 == NULL) { 138 LOGE("trivial add3 failed\n"); 139 goto bail; 140 } 141 142 if (dvmIndirectRefTableEntries(&irt) != 3) { 143 LOGE("expected 3 entries, found %d\n", 144 dvmIndirectRefTableEntries(&irt)); 145 goto bail; 146 } 147 148 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) || 149 dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) 150 { 151 LOGE("unorder deletion1 failed\n"); 152 goto bail; 153 } 154 155 /* get invalid entry (from hole) */ 156 if (dvmGetFromIndirectRefTable(&irt, iref1) != NULL) { 157 LOGE("hole get succeeded unexpectedly\n"); 158 goto bail; 159 } 160 161 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) || 162 !dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) 163 { 164 LOGE("unorder deletion2 failed\n"); 165 goto bail; 166 } 167 168 /* table should be empty now */ 169 if (dvmIndirectRefTableEntries(&irt) != 0) { 170 LOGE("unorder del not empty\n"); 171 goto bail; 172 } 173 174 /* 175 * Add four entries. Remove #1, add new entry, verify that table size 176 * is still 4 (i.e. holes are getting filled). Remove #1 and #3, verify 177 * that we delete one and don't hole-compact the other. 178 */ 179 DBUG_MSG("+++ START hole fill\n"); 180 iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0); 181 iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1); 182 iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2); 183 iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3); 184 if (iref0 == NULL || iref1 == NULL || iref2 == NULL || iref3 == NULL) { 185 LOGE("trivial add4 failed\n"); 186 goto bail; 187 } 188 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) { 189 LOGE("remove 1 of 4 failed\n"); 190 goto bail; 191 } 192 iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1); 193 if (dvmIndirectRefTableEntries(&irt) != 4) { 194 LOGE("hole not filled\n"); 195 goto bail; 196 } 197 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1) || 198 !dvmRemoveFromIndirectRefTable(&irt, cookie, iref3)) 199 { 200 LOGE("remove 1/3 failed\n"); 201 goto bail; 202 } 203 if (dvmIndirectRefTableEntries(&irt) != 3) { 204 LOGE("should be 3 after two deletions\n"); 205 goto bail; 206 } 207 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) || 208 !dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) 209 { 210 LOGE("remove 2/0 failed\n"); 211 goto bail; 212 } 213 if (dvmIndirectRefTableEntries(&irt) != 0) { 214 LOGE("not empty after split remove\n"); 215 goto bail; 216 } 217 218 /* 219 * Add an entry, remove it, add a new entry, and try to use the original 220 * iref. They have the same slot number but are for different objects. 221 * With the extended checks in place, this should fail. 222 */ 223 DBUG_MSG("+++ START switched\n"); 224 iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0); 225 dvmRemoveFromIndirectRefTable(&irt, cookie, iref0); 226 iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1); 227 if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) { 228 LOGE("mismatched del succeeded (%p vs %p)\n", iref0, iref1); 229 goto bail; 230 } 231 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) { 232 LOGE("switched del failed\n"); 233 goto bail; 234 } 235 if (dvmIndirectRefTableEntries(&irt) != 0) { 236 LOGE("switching del not empty\n"); 237 goto bail; 238 } 239 240 /* 241 * Same as above, but with the same object. A more rigorous checker 242 * (e.g. with slot serialization) will catch this. 243 */ 244 iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0); 245 dvmRemoveFromIndirectRefTable(&irt, cookie, iref0); 246 iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj0); 247 if (iref0 != iref1) { 248 /* try 0, should not work */ 249 if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0)) { 250 LOGE("temporal del succeeded (%p vs %p)\n", iref0, iref1); 251 goto bail; 252 } 253 } 254 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) { 255 LOGE("temporal cleanup failed\n"); 256 goto bail; 257 } 258 if (dvmIndirectRefTableEntries(&irt) != 0) { 259 LOGE("temporal del not empty\n"); 260 goto bail; 261 } 262 263 /* 264 * Test table overflow. 265 */ 266 DBUG_MSG("+++ START overflow\n"); 267 int i; 268 for (i = 0; i < kTableMax; i++) { 269 manyRefs[i] = dvmAddToIndirectRefTable(&irt, cookie, obj0); 270 if (manyRefs[i] == NULL) { 271 LOGE("Failed adding %d of %d\n", i, kTableMax); 272 goto bail; 273 } 274 } 275 if (dvmAddToIndirectRefTable(&irt, cookie, obj0) != NULL) { 276 LOGE("Table overflow succeeded\n"); 277 goto bail; 278 } 279 if (dvmIndirectRefTableEntries(&irt) != (size_t)kTableMax) { 280 LOGE("Expected %d entries, found %d\n", 281 kTableMax, dvmIndirectRefTableEntries(&irt)); 282 goto bail; 283 } 284 for (i = 0; i < kTableMax-1; i++) { 285 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, manyRefs[i])) { 286 LOGE("multi-remove failed at %d\n", i); 287 goto bail; 288 } 289 } 290 /* because of removal order, should have 20 entries, 19 of them holes */ 291 if (dvmIndirectRefTableEntries(&irt) != (size_t)kTableMax) { 292 LOGE("Expected %d entries (with holes), found %d\n", 293 kTableMax, dvmIndirectRefTableEntries(&irt)); 294 goto bail; 295 } 296 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, manyRefs[kTableMax-1])) { 297 LOGE("multi-remove final failed\n"); 298 goto bail; 299 } 300 if (dvmIndirectRefTableEntries(&irt) != 0) { 301 LOGE("multi-del not empty\n"); 302 goto bail; 303 } 304 305 DBUG_MSG("+++ basic test complete\n"); 306 result = true; 307 308 bail: 309 dvmClearIndirectRefTable(&irt); 310 return result; 311 } 312 313 /* 314 * Test operations on a segmented table. 315 */ 316 static bool segmentTest(void) 317 { 318 static const int kTableMax = 20; 319 IndirectRefTable irt; 320 IndirectRef iref0, iref1, iref2, iref3; 321 ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL); 322 Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 323 Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 324 Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 325 Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 326 u4 cookie; 327 u4 segmentState[4]; 328 bool result = false; 329 330 if (!dvmInitIndirectRefTable(&irt, kTableMax, kTableMax, 331 kIndirectKindLocal)) 332 { 333 return false; 334 } 335 cookie = segmentState[0] = IRT_FIRST_SEGMENT; 336 DBUG_MSG("+++ objs %p %p %p %p\n", obj0, obj1, obj2, obj3); 337 338 /* 339 * Push two, create new segment, push two more, try to get all four, 340 * try to delete all 4. All four should be accessible, but only the 341 * last two should be deletable. 342 */ 343 DBUG_MSG("+++ START basic segment\n"); 344 iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0); 345 iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1); 346 cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt); 347 DBUG_MSG("+++ pushed, cookie is 0x%08x\n", cookie); 348 iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2); 349 iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3); 350 351 if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) || 352 dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) 353 { 354 LOGE("removed values from earlier segment\n"); 355 goto bail; 356 } 357 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) || 358 !dvmRemoveFromIndirectRefTable(&irt, cookie, iref3)) 359 { 360 LOGE("unable to remove values from current segment\n"); 361 goto bail; 362 } 363 if (dvmIndirectRefTableEntries(&irt) != 2) { 364 LOGE("wrong total entries\n"); 365 goto bail; 366 } 367 dvmPopIndirectRefTableSegment(&irt, segmentState[1]); 368 cookie = segmentState[0]; 369 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) || 370 !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) 371 { 372 LOGE("unable to remove values from first segment\n"); 373 goto bail; 374 } 375 if (dvmIndirectRefTableEntries(&irt) != 0) { 376 LOGE("basic push/pop not empty\n"); 377 goto bail; 378 } 379 380 /* 381 * Push two, delete first, segment, push two more, pop segment, verify 382 * the last two are no longer present and hole count is right. The 383 * adds after the segment pop should not be filling in the hole. 384 */ 385 DBUG_MSG("+++ START segment pop\n"); 386 iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0); 387 iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1); 388 dvmRemoveFromIndirectRefTable(&irt, cookie, iref0); 389 cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt); 390 iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2); 391 iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3); 392 dvmPopIndirectRefTableSegment(&irt, segmentState[1]); 393 cookie = segmentState[0]; 394 if (dvmIndirectRefTableEntries(&irt) != 2) { 395 LOGE("wrong total entries after pop\n"); 396 goto bail; 397 } 398 dvmRemoveFromIndirectRefTable(&irt, cookie, iref1); 399 if (dvmIndirectRefTableEntries(&irt) != 0) { 400 LOGE("not back to zero after pop + del\n"); 401 goto bail; 402 } 403 404 /* 405 * Multiple segments, some empty. 406 */ 407 DBUG_MSG("+++ START multiseg\n"); 408 iref0 = dvmAppendToIndirectRefTable(&irt, cookie, obj0); 409 iref1 = dvmAppendToIndirectRefTable(&irt, cookie, obj1); 410 cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt); 411 cookie = segmentState[2] = dvmPushIndirectRefTableSegment(&irt); 412 iref3 = dvmAppendToIndirectRefTable(&irt, cookie, obj3); 413 iref2 = dvmAppendToIndirectRefTable(&irt, cookie, obj2); 414 dvmRemoveFromIndirectRefTable(&irt, cookie, iref3); 415 cookie = segmentState[3] = dvmPushIndirectRefTableSegment(&irt); 416 iref3 = dvmAppendToIndirectRefTable(&irt, cookie, obj3); 417 418 if (dvmGetFromIndirectRefTable(&irt, iref0) != obj0 || 419 dvmGetFromIndirectRefTable(&irt, iref1) != obj1 || 420 dvmGetFromIndirectRefTable(&irt, iref2) != obj2 || 421 dvmGetFromIndirectRefTable(&irt, iref3) != obj3) 422 { 423 LOGE("Unable to retrieve all multiseg objects\n"); 424 goto bail; 425 } 426 427 dvmDumpIndirectRefTable(&irt, "test"); 428 429 //int i; 430 //for (i = 0; i < sizeof(segmentState) / sizeof(segmentState[0]); i++) { 431 // DBUG_MSG("+++ segment %d = 0x%08x\n", i, segmentState[i]); 432 //} 433 434 dvmRemoveFromIndirectRefTable(&irt, cookie, iref3); 435 if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) { 436 LOGE("multiseg del2 worked\n"); 437 goto bail; 438 } 439 dvmPopIndirectRefTableSegment(&irt, segmentState[3]); 440 cookie = segmentState[2]; 441 if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) { 442 LOGE("multiseg del2b failed (cookie=0x%08x ref=%p)\n", cookie, iref2); 443 goto bail; 444 } 445 iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2); 446 447 /* pop two off at once */ 448 dvmPopIndirectRefTableSegment(&irt, segmentState[1]); 449 cookie = segmentState[0]; 450 451 if (dvmIndirectRefTableEntries(&irt) != 2) { 452 LOGE("Unexpected entry count in multiseg\n"); 453 goto bail; 454 } 455 dvmRemoveFromIndirectRefTable(&irt, cookie, iref0); 456 dvmRemoveFromIndirectRefTable(&irt, cookie, iref1); 457 if (dvmIndirectRefTableEntries(&irt) != 0) { 458 LOGE("Unexpected entry count at multiseg end\n"); 459 goto bail; 460 } 461 462 DBUG_MSG("+++ segment test complete\n"); 463 result = true; 464 465 bail: 466 dvmClearIndirectRefTable(&irt); 467 return result; 468 } 469 470 471 /* 472 * Some quick tests. 473 */ 474 bool dvmTestIndirectRefTable(void) 475 { 476 if (!basicTest()) { 477 LOGE("IRT basic test failed\n"); 478 return false; 479 } 480 if (!segmentTest()) { 481 LOGE("IRT segment test failed\n"); 482 return false; 483 } 484 485 return true; 486 } 487 488 #endif /*NDEBUG*/ 489