diff options
author | Brian Carlstrom <bdc@google.com> | 2013-07-12 13:46:57 -0700 |
---|---|---|
committer | Brian Carlstrom <bdc@google.com> | 2013-07-12 17:49:01 -0700 |
commit | 7940e44f4517de5e2634a7e07d58d0fb26160513 (patch) | |
tree | ac90242d96229a6942f6e24ab137bc1f8f2e0025 /runtime/indirect_reference_table.cc | |
parent | 5cd9e3b122f276f610980cbaf0d2ad6ed4cd9088 (diff) | |
download | art-7940e44f4517de5e2634a7e07d58d0fb26160513.zip art-7940e44f4517de5e2634a7e07d58d0fb26160513.tar.gz art-7940e44f4517de5e2634a7e07d58d0fb26160513.tar.bz2 |
Create separate Android.mk for main build targets
The runtime, compiler, dex2oat, and oatdump now are in seperate trees
to prevent dependency creep. They can now be individually built
without rebuilding the rest of the art projects. dalvikvm and jdwpspy
were already this way. Builds in the art directory should behave as
before, building everything including tests.
Change-Id: Ic6b1151e5ed0f823c3dd301afd2b13eb2d8feb81
Diffstat (limited to 'runtime/indirect_reference_table.cc')
-rw-r--r-- | runtime/indirect_reference_table.cc | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc new file mode 100644 index 0000000..0287d74 --- /dev/null +++ b/runtime/indirect_reference_table.cc @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "indirect_reference_table.h" +#include "jni_internal.h" +#include "reference_table.h" +#include "runtime.h" +#include "scoped_thread_state_change.h" +#include "thread.h" +#include "utils.h" + +#include <cstdlib> + +namespace art { + +static void AbortMaybe() { + // If -Xcheck:jni is on, it'll give a more detailed error before aborting. + if (!Runtime::Current()->GetJavaVM()->check_jni) { + // Otherwise, we want to abort rather than hand back a bad reference. + LOG(FATAL) << "JNI ERROR (app bug): see above."; + } +} + +IndirectReferenceTable::IndirectReferenceTable(size_t initialCount, + size_t maxCount, IndirectRefKind desiredKind) { + CHECK_GT(initialCount, 0U); + CHECK_LE(initialCount, maxCount); + CHECK_NE(desiredKind, kSirtOrInvalid); + + table_ = reinterpret_cast<const mirror::Object**>(malloc(initialCount * sizeof(const mirror::Object*))); + CHECK(table_ != NULL); + memset(table_, 0xd1, initialCount * sizeof(const mirror::Object*)); + + slot_data_ = reinterpret_cast<IndirectRefSlot*>(calloc(initialCount, sizeof(IndirectRefSlot))); + CHECK(slot_data_ != NULL); + + segment_state_.all = IRT_FIRST_SEGMENT; + alloc_entries_ = initialCount; + max_entries_ = maxCount; + kind_ = desiredKind; +} + +IndirectReferenceTable::~IndirectReferenceTable() { + free(table_); + free(slot_data_); + table_ = NULL; + slot_data_ = NULL; + alloc_entries_ = max_entries_ = -1; +} + +// Make sure that the entry at "idx" is correctly paired with "iref". +bool IndirectReferenceTable::CheckEntry(const char* what, IndirectRef iref, int idx) const { + const mirror::Object* obj = table_[idx]; + IndirectRef checkRef = ToIndirectRef(obj, idx); + if (UNLIKELY(checkRef != iref)) { + LOG(ERROR) << "JNI ERROR (app bug): attempt to " << what + << " stale " << kind_ << " " << iref + << " (should be " << checkRef << ")"; + AbortMaybe(); + return false; + } + return true; +} + +IndirectRef IndirectReferenceTable::Add(uint32_t cookie, const mirror::Object* obj) { + IRTSegmentState prevState; + prevState.all = cookie; + size_t topIndex = segment_state_.parts.topIndex; + + DCHECK(obj != NULL); + // TODO: stronger sanity check on the object (such as in heap) + DCHECK_ALIGNED(reinterpret_cast<uintptr_t>(obj), 8); + DCHECK(table_ != NULL); + DCHECK_LE(alloc_entries_, max_entries_); + DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles); + + if (topIndex == alloc_entries_) { + // reached end of allocated space; did we hit buffer max? + if (topIndex == max_entries_) { + LOG(FATAL) << "JNI ERROR (app bug): " << kind_ << " table overflow " + << "(max=" << max_entries_ << ")\n" + << MutatorLockedDumpable<IndirectReferenceTable>(*this); + } + + size_t newSize = alloc_entries_ * 2; + if (newSize > max_entries_) { + newSize = max_entries_; + } + DCHECK_GT(newSize, alloc_entries_); + + table_ = reinterpret_cast<const mirror::Object**>(realloc(table_, newSize * sizeof(const mirror::Object*))); + slot_data_ = reinterpret_cast<IndirectRefSlot*>(realloc(slot_data_, + newSize * sizeof(IndirectRefSlot))); + if (table_ == NULL || slot_data_ == NULL) { + LOG(FATAL) << "JNI ERROR (app bug): unable to expand " + << kind_ << " table (from " + << alloc_entries_ << " to " << newSize + << ", max=" << max_entries_ << ")\n" + << MutatorLockedDumpable<IndirectReferenceTable>(*this); + } + + // Clear the newly-allocated slot_data_ elements. + memset(slot_data_ + alloc_entries_, 0, (newSize - alloc_entries_) * sizeof(IndirectRefSlot)); + + alloc_entries_ = newSize; + } + + // We know there's enough room in the table. Now we just need to find + // the right spot. If there's a hole, find it and fill it; otherwise, + // add to the end of the list. + IndirectRef result; + int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles; + if (numHoles > 0) { + DCHECK_GT(topIndex, 1U); + // Find the first hole; likely to be near the end of the list. + const mirror::Object** pScan = &table_[topIndex - 1]; + DCHECK(*pScan != NULL); + while (*--pScan != NULL) { + DCHECK_GE(pScan, table_ + prevState.parts.topIndex); + } + UpdateSlotAdd(obj, pScan - table_); + result = ToIndirectRef(obj, pScan - table_); + *pScan = obj; + segment_state_.parts.numHoles--; + } else { + // Add to the end. + UpdateSlotAdd(obj, topIndex); + result = ToIndirectRef(obj, topIndex); + table_[topIndex++] = obj; + segment_state_.parts.topIndex = topIndex; + } + if (false) { + LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.parts.topIndex + << " holes=" << segment_state_.parts.numHoles; + } + + DCHECK(result != NULL); + return result; +} + +void IndirectReferenceTable::AssertEmpty() { + if (UNLIKELY(begin() != end())) { + ScopedObjectAccess soa(Thread::Current()); + LOG(FATAL) << "Internal Error: non-empty local reference table\n" + << MutatorLockedDumpable<IndirectReferenceTable>(*this); + } +} + +// Verifies that the indirect table lookup is valid. +// Returns "false" if something looks bad. +bool IndirectReferenceTable::GetChecked(IndirectRef iref) const { + if (UNLIKELY(iref == NULL)) { + LOG(WARNING) << "Attempt to look up NULL " << kind_; + return false; + } + if (UNLIKELY(GetIndirectRefKind(iref) == kSirtOrInvalid)) { + LOG(ERROR) << "JNI ERROR (app bug): invalid " << kind_ << " " << iref; + AbortMaybe(); + return false; + } + + int topIndex = segment_state_.parts.topIndex; + int idx = ExtractIndex(iref); + if (UNLIKELY(idx >= topIndex)) { + LOG(ERROR) << "JNI ERROR (app bug): accessed stale " << kind_ << " " + << iref << " (index " << idx << " in a table of size " << topIndex << ")"; + AbortMaybe(); + return false; + } + + if (UNLIKELY(table_[idx] == NULL)) { + LOG(ERROR) << "JNI ERROR (app bug): accessed deleted " << kind_ << " " << iref; + AbortMaybe(); + return false; + } + + if (UNLIKELY(!CheckEntry("use", iref, idx))) { + return false; + } + + return true; +} + +static int Find(mirror::Object* direct_pointer, int bottomIndex, int topIndex, const mirror::Object** table) { + for (int i = bottomIndex; i < topIndex; ++i) { + if (table[i] == direct_pointer) { + return i; + } + } + return -1; +} + +bool IndirectReferenceTable::ContainsDirectPointer(mirror::Object* direct_pointer) const { + return Find(direct_pointer, 0, segment_state_.parts.topIndex, table_) != -1; +} + +// Removes an object. We extract the table offset bits from "iref" +// and zap the corresponding entry, leaving a hole if it's not at the top. +// If the entry is not between the current top index and the bottom index +// specified by the cookie, we don't remove anything. This is the behavior +// required by JNI's DeleteLocalRef function. +// This method is not called when a local frame is popped; this is only used +// for explicit single removals. +// Returns "false" if nothing was removed. +bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) { + IRTSegmentState prevState; + prevState.all = cookie; + int topIndex = segment_state_.parts.topIndex; + int bottomIndex = prevState.parts.topIndex; + + DCHECK(table_ != NULL); + DCHECK_LE(alloc_entries_, max_entries_); + DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles); + + int idx = ExtractIndex(iref); + + JavaVMExt* vm = Runtime::Current()->GetJavaVM(); + if (GetIndirectRefKind(iref) == kSirtOrInvalid && + Thread::Current()->SirtContains(reinterpret_cast<jobject>(iref))) { + LOG(WARNING) << "Attempt to remove local SIRT entry from IRT, ignoring"; + return true; + } + if (GetIndirectRefKind(iref) == kSirtOrInvalid && vm->work_around_app_jni_bugs) { + mirror::Object* direct_pointer = reinterpret_cast<mirror::Object*>(iref); + idx = Find(direct_pointer, bottomIndex, topIndex, table_); + if (idx == -1) { + LOG(WARNING) << "trying to work around app JNI bugs, but didn't find " << iref << " in table!"; + return false; + } + } + + if (idx < bottomIndex) { + // Wrong segment. + LOG(WARNING) << "Attempt to remove index outside index area (" << idx + << " vs " << bottomIndex << "-" << topIndex << ")"; + return false; + } + if (idx >= topIndex) { + // Bad --- stale reference? + LOG(WARNING) << "Attempt to remove invalid index " << idx + << " (bottom=" << bottomIndex << " top=" << topIndex << ")"; + return false; + } + + if (idx == topIndex-1) { + // Top-most entry. Scan up and consume holes. + + if (!vm->work_around_app_jni_bugs && !CheckEntry("remove", iref, idx)) { + return false; + } + + table_[idx] = NULL; + int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles; + if (numHoles != 0) { + while (--topIndex > bottomIndex && numHoles != 0) { + if (false) { + LOG(INFO) << "+++ checking for hole at " << topIndex-1 + << " (cookie=" << cookie << ") val=" << table_[topIndex - 1]; + } + if (table_[topIndex-1] != NULL) { + break; + } + if (false) { + LOG(INFO) << "+++ ate hole at " << (topIndex - 1); + } + numHoles--; + } + segment_state_.parts.numHoles = numHoles + prevState.parts.numHoles; + segment_state_.parts.topIndex = topIndex; + } else { + segment_state_.parts.topIndex = topIndex-1; + if (false) { + LOG(INFO) << "+++ ate last entry " << topIndex - 1; + } + } + } else { + // Not the top-most entry. This creates a hole. We NULL out the + // entry to prevent somebody from deleting it twice and screwing up + // the hole count. + if (table_[idx] == NULL) { + LOG(INFO) << "--- WEIRD: removing null entry " << idx; + return false; + } + if (!vm->work_around_app_jni_bugs && !CheckEntry("remove", iref, idx)) { + return false; + } + + table_[idx] = NULL; + segment_state_.parts.numHoles++; + if (false) { + LOG(INFO) << "+++ left hole at " << idx << ", holes=" << segment_state_.parts.numHoles; + } + } + + return true; +} + +void IndirectReferenceTable::VisitRoots(RootVisitor* visitor, void* arg) { + typedef IndirectReferenceTable::iterator It; // TODO: C++0x auto + for (It it = begin(), end = this->end(); it != end; ++it) { + visitor(**it, arg); + } +} + +void IndirectReferenceTable::Dump(std::ostream& os) const { + os << kind_ << " table dump:\n"; + std::vector<const mirror::Object*> entries(table_, table_ + Capacity()); + // Remove NULLs. + for (int i = entries.size() - 1; i >= 0; --i) { + if (entries[i] == NULL) { + entries.erase(entries.begin() + i); + } + } + ReferenceTable::Dump(os, entries); +} + +} // namespace art |