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