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 #include "indirect_reference_table-inl.h" 18 19 #include "jni_internal.h" 20 #include "reference_table.h" 21 #include "runtime.h" 22 #include "scoped_thread_state_change.h" 23 #include "thread.h" 24 #include "utils.h" 25 #include "verify_object-inl.h" 26 27 #include <cstdlib> 28 29 namespace art { 30 31 template<typename T> 32 class MutatorLockedDumpable { 33 public: 34 explicit MutatorLockedDumpable(T& value) 35 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : value_(value) { 36 } 37 38 void Dump(std::ostream& os) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 39 value_.Dump(os); 40 } 41 42 private: 43 T& value_; 44 45 DISALLOW_COPY_AND_ASSIGN(MutatorLockedDumpable); 46 }; 47 48 template<typename T> 49 std::ostream& operator<<(std::ostream& os, const MutatorLockedDumpable<T>& rhs) 50 // TODO: should be SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) however annotalysis 51 // currently fails for this. 52 NO_THREAD_SAFETY_ANALYSIS { 53 rhs.Dump(os); 54 return os; 55 } 56 57 void IndirectReferenceTable::AbortIfNoCheckJNI() { 58 // If -Xcheck:jni is on, it'll give a more detailed error before aborting. 59 if (!Runtime::Current()->GetJavaVM()->check_jni) { 60 // Otherwise, we want to abort rather than hand back a bad reference. 61 LOG(FATAL) << "JNI ERROR (app bug): see above."; 62 } 63 } 64 65 IndirectReferenceTable::IndirectReferenceTable(size_t initialCount, 66 size_t maxCount, IndirectRefKind desiredKind) { 67 CHECK_GT(initialCount, 0U); 68 CHECK_LE(initialCount, maxCount); 69 CHECK_NE(desiredKind, kHandleScopeOrInvalid); 70 71 std::string error_str; 72 const size_t initial_bytes = initialCount * sizeof(const mirror::Object*); 73 const size_t table_bytes = maxCount * sizeof(const mirror::Object*); 74 table_mem_map_.reset(MemMap::MapAnonymous("indirect ref table", nullptr, table_bytes, 75 PROT_READ | PROT_WRITE, false, &error_str)); 76 CHECK(table_mem_map_.get() != nullptr) << error_str; 77 CHECK_EQ(table_mem_map_->Size(), table_bytes); 78 79 table_ = reinterpret_cast<GcRoot<mirror::Object>*>(table_mem_map_->Begin()); 80 CHECK(table_ != nullptr); 81 memset(table_, 0xd1, initial_bytes); 82 83 const size_t slot_bytes = maxCount * sizeof(IndirectRefSlot); 84 slot_mem_map_.reset(MemMap::MapAnonymous("indirect ref table slots", nullptr, slot_bytes, 85 PROT_READ | PROT_WRITE, false, &error_str)); 86 CHECK(slot_mem_map_.get() != nullptr) << error_str; 87 slot_data_ = reinterpret_cast<IndirectRefSlot*>(slot_mem_map_->Begin()); 88 CHECK(slot_data_ != nullptr); 89 90 segment_state_.all = IRT_FIRST_SEGMENT; 91 alloc_entries_ = initialCount; 92 max_entries_ = maxCount; 93 kind_ = desiredKind; 94 } 95 96 IndirectReferenceTable::~IndirectReferenceTable() { 97 } 98 99 IndirectRef IndirectReferenceTable::Add(uint32_t cookie, mirror::Object* obj) { 100 IRTSegmentState prevState; 101 prevState.all = cookie; 102 size_t topIndex = segment_state_.parts.topIndex; 103 104 CHECK(obj != NULL); 105 VerifyObject(obj); 106 DCHECK(table_ != NULL); 107 DCHECK_LE(alloc_entries_, max_entries_); 108 DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles); 109 110 if (topIndex == alloc_entries_) { 111 // reached end of allocated space; did we hit buffer max? 112 if (topIndex == max_entries_) { 113 LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow " 114 << "(max=" << max_entries_ << ")\n" 115 << MutatorLockedDumpable<IndirectReferenceTable>(*this); 116 } 117 118 size_t newSize = alloc_entries_ * 2; 119 if (newSize > max_entries_) { 120 newSize = max_entries_; 121 } 122 DCHECK_GT(newSize, alloc_entries_); 123 124 alloc_entries_ = newSize; 125 } 126 127 // We know there's enough room in the table. Now we just need to find 128 // the right spot. If there's a hole, find it and fill it; otherwise, 129 // add to the end of the list. 130 IndirectRef result; 131 int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles; 132 if (numHoles > 0) { 133 DCHECK_GT(topIndex, 1U); 134 // Find the first hole; likely to be near the end of the list. 135 GcRoot<mirror::Object>* pScan = &table_[topIndex - 1]; 136 DCHECK(!pScan->IsNull()); 137 --pScan; 138 while (!pScan->IsNull()) { 139 DCHECK_GE(pScan, table_ + prevState.parts.topIndex); 140 --pScan; 141 } 142 UpdateSlotAdd(obj, pScan - table_); 143 result = ToIndirectRef(pScan - table_); 144 *pScan = GcRoot<mirror::Object>(obj); 145 segment_state_.parts.numHoles--; 146 } else { 147 // Add to the end. 148 UpdateSlotAdd(obj, topIndex); 149 result = ToIndirectRef(topIndex); 150 table_[topIndex++] = GcRoot<mirror::Object>(obj); 151 segment_state_.parts.topIndex = topIndex; 152 } 153 if (false) { 154 LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.parts.topIndex 155 << " holes=" << segment_state_.parts.numHoles; 156 } 157 158 DCHECK(result != NULL); 159 return result; 160 } 161 162 void IndirectReferenceTable::AssertEmpty() { 163 if (UNLIKELY(begin() != end())) { 164 ScopedObjectAccess soa(Thread::Current()); 165 LOG(FATAL) << "Internal Error: non-empty local reference table\n" 166 << MutatorLockedDumpable<IndirectReferenceTable>(*this); 167 } 168 } 169 170 // Removes an object. We extract the table offset bits from "iref" 171 // and zap the corresponding entry, leaving a hole if it's not at the top. 172 // If the entry is not between the current top index and the bottom index 173 // specified by the cookie, we don't remove anything. This is the behavior 174 // required by JNI's DeleteLocalRef function. 175 // This method is not called when a local frame is popped; this is only used 176 // for explicit single removals. 177 // Returns "false" if nothing was removed. 178 bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) { 179 IRTSegmentState prevState; 180 prevState.all = cookie; 181 int topIndex = segment_state_.parts.topIndex; 182 int bottomIndex = prevState.parts.topIndex; 183 184 DCHECK(table_ != NULL); 185 DCHECK_LE(alloc_entries_, max_entries_); 186 DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles); 187 188 int idx = ExtractIndex(iref); 189 190 if (GetIndirectRefKind(iref) == kHandleScopeOrInvalid && 191 Thread::Current()->HandleScopeContains(reinterpret_cast<jobject>(iref))) { 192 LOG(WARNING) << "Attempt to remove local handle scope entry from IRT, ignoring"; 193 return true; 194 } 195 196 if (idx < bottomIndex) { 197 // Wrong segment. 198 LOG(WARNING) << "Attempt to remove index outside index area (" << idx 199 << " vs " << bottomIndex << "-" << topIndex << ")"; 200 return false; 201 } 202 if (idx >= topIndex) { 203 // Bad --- stale reference? 204 LOG(WARNING) << "Attempt to remove invalid index " << idx 205 << " (bottom=" << bottomIndex << " top=" << topIndex << ")"; 206 return false; 207 } 208 209 if (idx == topIndex-1) { 210 // Top-most entry. Scan up and consume holes. 211 212 if (!CheckEntry("remove", iref, idx)) { 213 return false; 214 } 215 216 table_[idx] = GcRoot<mirror::Object>(nullptr); 217 int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles; 218 if (numHoles != 0) { 219 while (--topIndex > bottomIndex && numHoles != 0) { 220 if (false) { 221 LOG(INFO) << "+++ checking for hole at " << topIndex-1 222 << " (cookie=" << cookie << ") val=" 223 << table_[topIndex - 1].Read<kWithoutReadBarrier>(); 224 } 225 if (!table_[topIndex-1].IsNull()) { 226 break; 227 } 228 if (false) { 229 LOG(INFO) << "+++ ate hole at " << (topIndex - 1); 230 } 231 numHoles--; 232 } 233 segment_state_.parts.numHoles = numHoles + prevState.parts.numHoles; 234 segment_state_.parts.topIndex = topIndex; 235 } else { 236 segment_state_.parts.topIndex = topIndex-1; 237 if (false) { 238 LOG(INFO) << "+++ ate last entry " << topIndex - 1; 239 } 240 } 241 } else { 242 // Not the top-most entry. This creates a hole. We NULL out the 243 // entry to prevent somebody from deleting it twice and screwing up 244 // the hole count. 245 if (table_[idx].IsNull()) { 246 LOG(INFO) << "--- WEIRD: removing null entry " << idx; 247 return false; 248 } 249 if (!CheckEntry("remove", iref, idx)) { 250 return false; 251 } 252 253 table_[idx] = GcRoot<mirror::Object>(nullptr); 254 segment_state_.parts.numHoles++; 255 if (false) { 256 LOG(INFO) << "+++ left hole at " << idx << ", holes=" << segment_state_.parts.numHoles; 257 } 258 } 259 260 return true; 261 } 262 263 void IndirectReferenceTable::VisitRoots(RootCallback* callback, void* arg, uint32_t tid, 264 RootType root_type) { 265 for (auto ref : *this) { 266 callback(ref, arg, tid, root_type); 267 DCHECK(*ref != nullptr); 268 } 269 } 270 271 void IndirectReferenceTable::Dump(std::ostream& os) const { 272 os << kind_ << " table dump:\n"; 273 ReferenceTable::Table entries; 274 for (size_t i = 0; i < Capacity(); ++i) { 275 mirror::Object* obj = table_[i].Read<kWithoutReadBarrier>(); 276 if (UNLIKELY(obj == nullptr)) { 277 // Remove NULLs. 278 } else if (UNLIKELY(obj == kClearedJniWeakGlobal)) { 279 // ReferenceTable::Dump() will handle kClearedJniWeakGlobal 280 // while the read barrier won't. 281 entries.push_back(GcRoot<mirror::Object>(obj)); 282 } else { 283 obj = table_[i].Read(); 284 entries.push_back(GcRoot<mirror::Object>(obj)); 285 } 286 } 287 ReferenceTable::Dump(os, entries); 288 } 289 290 } // namespace art 291