IndirectRefTable.cpp revision 5552e62455d486d19e5986a67ae2545411d50fbe
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 * Indirect reference table management. 19 */ 20#include "Dalvik.h" 21 22static void abortMaybe() { 23 // If CheckJNI is on, it'll give a more detailed error before aborting. 24 // Otherwise, we want to abort rather than hand back a bad reference. 25 if (!gDvmJni.useCheckJni) { 26 dvmAbort(); 27 } 28} 29 30bool IndirectRefTable::init(size_t initialCount, 31 size_t maxCount, IndirectRefKind desiredKind) 32{ 33 assert(initialCount > 0); 34 assert(initialCount <= maxCount); 35 assert(desiredKind != kIndirectKindInvalid); 36 37 table_ = (IndirectRefSlot*) malloc(initialCount * sizeof(IndirectRefSlot)); 38 if (table_ == NULL) { 39 return false; 40 } 41#ifndef NDEBUG 42 memset(table_, 0xd1, initialCount * sizeof(IndirectRefSlot)); 43#endif 44 45 segmentState.all = IRT_FIRST_SEGMENT; 46 alloc_entries_ = initialCount; 47 max_entries_ = maxCount; 48 kind_ = desiredKind; 49 50 return true; 51} 52 53/* 54 * Clears out the contents of a IndirectRefTable, freeing allocated storage. 55 */ 56void IndirectRefTable::destroy() 57{ 58 free(table_); 59 table_ = NULL; 60 alloc_entries_ = max_entries_ = -1; 61} 62 63IndirectRef IndirectRefTable::add(u4 cookie, Object* obj) 64{ 65 IRTSegmentState prevState; 66 prevState.all = cookie; 67 size_t topIndex = segmentState.parts.topIndex; 68 69 assert(obj != NULL); 70 assert(dvmIsHeapAddress(obj)); 71 assert(table_ != NULL); 72 assert(alloc_entries_ <= max_entries_); 73 assert(segmentState.parts.numHoles >= prevState.parts.numHoles); 74 75 /* 76 * We know there's enough room in the table. Now we just need to find 77 * the right spot. If there's a hole, find it and fill it; otherwise, 78 * add to the end of the list. 79 */ 80 IndirectRef result; 81 IndirectRefSlot* slot; 82 int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles; 83 if (numHoles > 0) { 84 assert(topIndex > 1); 85 /* find the first hole; likely to be near the end of the list, 86 * we know the item at the topIndex is not a hole */ 87 slot = &table_[topIndex - 1]; 88 assert(slot->obj != NULL); 89 while ((--slot)->obj != NULL) { 90 assert(slot >= table_ + prevState.parts.topIndex); 91 } 92 segmentState.parts.numHoles--; 93 } else { 94 /* add to the end, grow if needed */ 95 if (topIndex == alloc_entries_) { 96 /* reached end of allocated space; did we hit buffer max? */ 97 if (topIndex == max_entries_) { 98 LOGE("JNI ERROR (app bug): %s reference table overflow (max=%d)", 99 indirectRefKindToString(kind_), max_entries_); 100 return NULL; 101 } 102 103 size_t newSize = alloc_entries_ * 2; 104 if (newSize > max_entries_) { 105 newSize = max_entries_; 106 } 107 assert(newSize > alloc_entries_); 108 109 IndirectRefSlot* newTable = 110 (IndirectRefSlot*) realloc(table_, newSize * sizeof(IndirectRefSlot)); 111 if (table_ == NULL) { 112 LOGE("JNI ERROR (app bug): unable to expand %s reference table " 113 "(from %d to %d, max=%d)", 114 indirectRefKindToString(kind_), 115 alloc_entries_, newSize, max_entries_); 116 return NULL; 117 } 118 119 alloc_entries_ = newSize; 120 table_ = newTable; 121 } 122 slot = &table_[topIndex++]; 123 segmentState.parts.topIndex = topIndex; 124 } 125 126 slot->obj = obj; 127 slot->serial = nextSerial(slot->serial); 128 result = toIndirectRef(slot - table_, slot->serial, kind_); 129 130 assert(result != NULL); 131 return result; 132} 133 134/* 135 * Get the referent of an indirect ref from the table. 136 * 137 * Returns kInvalidIndirectRefObject if iref is invalid. 138 */ 139Object* IndirectRefTable::get(IndirectRef iref) const { 140 IndirectRefKind kind = indirectRefKind(iref); 141 if (kind != kind_) { 142 if (iref == NULL) { 143 LOGW("Attempt to look up NULL %s reference", indirectRefKindToString(kind_)); 144 return kInvalidIndirectRefObject; 145 } 146 if (kind == kIndirectKindInvalid) { 147 LOGE("JNI ERROR (app bug): invalid %s reference %p", 148 indirectRefKindToString(kind_), iref); 149 abortMaybe(); 150 return kInvalidIndirectRefObject; 151 } 152 // References of the requested kind cannot appear within this table. 153 return kInvalidIndirectRefObject; 154 } 155 156 u4 topIndex = segmentState.parts.topIndex; 157 u4 index = extractIndex(iref); 158 if (index >= topIndex) { 159 /* bad -- stale reference? */ 160 LOGE("JNI ERROR (app bug): accessed stale %s reference %p (index %d in a table of size %d)", 161 indirectRefKindToString(kind_), iref, index, topIndex); 162 abortMaybe(); 163 return kInvalidIndirectRefObject; 164 } 165 166 Object* obj = table_[index].obj; 167 if (obj == NULL) { 168 LOGI("JNI ERROR (app bug): accessed deleted %s reference %p", 169 indirectRefKindToString(kind_), iref); 170 abortMaybe(); 171 return kInvalidIndirectRefObject; 172 } 173 174 u4 serial = extractSerial(iref); 175 if (serial != table_[index].serial) { 176 LOGE("JNI ERROR (app bug): attempt to use stale %s reference %p", 177 indirectRefKindToString(kind_), iref); 178 abortMaybe(); 179 return kInvalidIndirectRefObject; 180 } 181 182 return obj; 183} 184 185static int findObject(const Object* obj, int bottomIndex, int topIndex, 186 const IndirectRefSlot* table) { 187 for (int i = bottomIndex; i < topIndex; ++i) { 188 if (table[i].obj == obj) { 189 return i; 190 } 191 } 192 return -1; 193} 194 195bool IndirectRefTable::contains(const Object* obj) const { 196 return findObject(obj, 0, segmentState.parts.topIndex, table_) >= 0; 197} 198 199/* 200 * Remove "obj" from "pRef". We extract the table offset bits from "iref" 201 * and zap the corresponding entry, leaving a hole if it's not at the top. 202 * 203 * If the entry is not between the current top index and the bottom index 204 * specified by the cookie, we don't remove anything. This is the behavior 205 * required by JNI's DeleteLocalRef function. 206 * 207 * Note this is NOT called when a local frame is popped. This is only used 208 * for explicit single removals. 209 * 210 * Returns "false" if nothing was removed. 211 */ 212bool IndirectRefTable::remove(u4 cookie, IndirectRef iref) 213{ 214 IRTSegmentState prevState; 215 prevState.all = cookie; 216 u4 topIndex = segmentState.parts.topIndex; 217 u4 bottomIndex = prevState.parts.topIndex; 218 219 assert(table_ != NULL); 220 assert(alloc_entries_ <= max_entries_); 221 assert(segmentState.parts.numHoles >= prevState.parts.numHoles); 222 223 IndirectRefKind kind = indirectRefKind(iref); 224 u4 index; 225 if (kind == kind_) { 226 index = extractIndex(iref); 227 if (index < bottomIndex) { 228 /* wrong segment */ 229 ALOGV("Attempt to remove index outside index area (%ud vs %ud-%ud)", 230 index, bottomIndex, topIndex); 231 return false; 232 } 233 if (index >= topIndex) { 234 /* bad -- stale reference? */ 235 LOGD("Attempt to remove invalid index %ud (bottom=%ud top=%ud)", 236 index, bottomIndex, topIndex); 237 return false; 238 } 239 if (table_[index].obj == NULL) { 240 LOGD("Attempt to remove cleared %s reference %p", 241 indirectRefKindToString(kind_), iref); 242 return false; 243 } 244 u4 serial = extractSerial(iref); 245 if (table_[index].serial != serial) { 246 LOGD("Attempt to remove stale %s reference %p", 247 indirectRefKindToString(kind_), iref); 248 return false; 249 } 250 } else if (kind == kIndirectKindInvalid && gDvmJni.workAroundAppJniBugs) { 251 // reference looks like a pointer, scan the table to find the index 252 int i = findObject(reinterpret_cast<Object*>(iref), bottomIndex, topIndex, table_); 253 if (i < 0) { 254 LOGW("trying to work around app JNI bugs, but didn't find %p in table!", iref); 255 return false; 256 } 257 index = i; 258 } else { 259 // References of the requested kind cannot appear within this table. 260 return false; 261 } 262 263 if (index == topIndex - 1) { 264 // Top-most entry. Scan up and consume holes. 265 int numHoles = segmentState.parts.numHoles - prevState.parts.numHoles; 266 if (numHoles != 0) { 267 while (--topIndex > bottomIndex && numHoles != 0) { 268 ALOGV("+++ checking for hole at %d (cookie=0x%08x) val=%p", 269 topIndex-1, cookie, table_[topIndex-1].obj); 270 if (table_[topIndex-1].obj != NULL) { 271 break; 272 } 273 ALOGV("+++ ate hole at %d", topIndex-1); 274 numHoles--; 275 } 276 segmentState.parts.numHoles = numHoles + prevState.parts.numHoles; 277 segmentState.parts.topIndex = topIndex; 278 } else { 279 segmentState.parts.topIndex = topIndex-1; 280 ALOGV("+++ ate last entry %d", topIndex-1); 281 } 282 } else { 283 /* 284 * Not the top-most entry. This creates a hole. We NULL out the 285 * entry to prevent somebody from deleting it twice and screwing up 286 * the hole count. 287 */ 288 table_[index].obj = NULL; 289 segmentState.parts.numHoles++; 290 ALOGV("+++ left hole at %d, holes=%d", index, segmentState.parts.numHoles); 291 } 292 293 return true; 294} 295 296const char* indirectRefKindToString(IndirectRefKind kind) 297{ 298 switch (kind) { 299 case kIndirectKindInvalid: return "invalid"; 300 case kIndirectKindLocal: return "local"; 301 case kIndirectKindGlobal: return "global"; 302 case kIndirectKindWeakGlobal: return "weak global"; 303 default: return "UNKNOWN"; 304 } 305} 306 307void IndirectRefTable::dump(const char* descr) const 308{ 309 size_t count = capacity(); 310 Object** copy = new Object*[count]; 311 for (size_t i = 0; i < count; i++) { 312 copy[i] = table_[i].obj; 313 } 314 dvmDumpReferenceTableContents(copy, count, descr); 315 delete[] copy; 316} 317