blob: 14d804b59b6f2080145bee6d128dfcbc9f911be3 [file] [log] [blame]
Elliott Hughes6c1a3942011-08-17 15:00:06 -07001/*
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.h"
Elliott Hughesa2501992011-08-26 19:39:54 -070018#include "jni_internal.h"
Elliott Hughes6c1a3942011-08-17 15:00:06 -070019#include "reference_table.h"
Elliott Hughesa2501992011-08-26 19:39:54 -070020#include "runtime.h"
Ian Rogers5a7a74a2011-09-26 16:32:29 -070021#include "thread.h"
Ian Rogerscdd1d2d2011-08-18 09:58:17 -070022#include "utils.h"
Elliott Hughes6c1a3942011-08-17 15:00:06 -070023
24#include <cstdlib>
25
26namespace art {
27
Elliott Hughes6c1a3942011-08-17 15:00:06 -070028static void AbortMaybe() {
Elliott Hughesa2501992011-08-26 19:39:54 -070029 // If -Xcheck:jni is on, it'll give a more detailed error before aborting.
30 if (!Runtime::Current()->GetJavaVM()->check_jni) {
31 // Otherwise, we want to abort rather than hand back a bad reference.
32 LOG(FATAL) << "JNI ERROR (app bug): see above.";
33 }
Elliott Hughes6c1a3942011-08-17 15:00:06 -070034}
35
36IndirectReferenceTable::IndirectReferenceTable(size_t initialCount,
Elliott Hughesba8eee12012-01-24 20:25:24 -080037 size_t maxCount, IndirectRefKind desiredKind) {
Elliott Hughes6c1a3942011-08-17 15:00:06 -070038 CHECK_GT(initialCount, 0U);
39 CHECK_LE(initialCount, maxCount);
Ian Rogers408f79a2011-08-23 18:22:33 -070040 CHECK_NE(desiredKind, kSirtOrInvalid);
Elliott Hughes6c1a3942011-08-17 15:00:06 -070041
Elliott Hughescf4c6c42011-09-01 15:16:42 -070042 table_ = reinterpret_cast<const Object**>(malloc(initialCount * sizeof(const Object*)));
Elliott Hughes6c1a3942011-08-17 15:00:06 -070043 CHECK(table_ != NULL);
Elliott Hughescf4c6c42011-09-01 15:16:42 -070044 memset(table_, 0xd1, initialCount * sizeof(const Object*));
Elliott Hughes6c1a3942011-08-17 15:00:06 -070045
46 slot_data_ = reinterpret_cast<IndirectRefSlot*>(calloc(initialCount, sizeof(IndirectRefSlot)));
47 CHECK(slot_data_ != NULL);
48
Ian Rogersdc51b792011-09-22 20:41:37 -070049 segment_state_.all = IRT_FIRST_SEGMENT;
Elliott Hughes6c1a3942011-08-17 15:00:06 -070050 alloc_entries_ = initialCount;
51 max_entries_ = maxCount;
52 kind_ = desiredKind;
53}
54
55IndirectReferenceTable::~IndirectReferenceTable() {
56 free(table_);
57 free(slot_data_);
58 table_ = NULL;
59 slot_data_ = NULL;
60 alloc_entries_ = max_entries_ = -1;
61}
62
63/*
64 * Make sure that the entry at "idx" is correctly paired with "iref".
65 */
66bool IndirectReferenceTable::CheckEntry(const char* what, IndirectRef iref, int idx) const {
Elliott Hughescf4c6c42011-09-01 15:16:42 -070067 const Object* obj = table_[idx];
Elliott Hughes6c1a3942011-08-17 15:00:06 -070068 IndirectRef checkRef = ToIndirectRef(obj, idx);
69 if (checkRef != iref) {
70 LOG(ERROR) << "JNI ERROR (app bug): attempt to " << what
71 << " stale " << kind_ << " " << iref
72 << " (should be " << checkRef << ")";
73 AbortMaybe();
74 return false;
75 }
76 return true;
77}
78
Elliott Hughescf4c6c42011-09-01 15:16:42 -070079IndirectRef IndirectReferenceTable::Add(uint32_t cookie, const Object* obj) {
Elliott Hughes6c1a3942011-08-17 15:00:06 -070080 IRTSegmentState prevState;
81 prevState.all = cookie;
Ian Rogersdc51b792011-09-22 20:41:37 -070082 size_t topIndex = segment_state_.parts.topIndex;
Elliott Hughes6c1a3942011-08-17 15:00:06 -070083
84 DCHECK(obj != NULL);
Ian Rogerscdd1d2d2011-08-18 09:58:17 -070085 // TODO: stronger sanity check on the object (such as in heap)
Elliott Hughesd07986f2011-12-06 18:27:45 -080086 DCHECK_ALIGNED(reinterpret_cast<uintptr_t>(obj), 8);
Elliott Hughes6c1a3942011-08-17 15:00:06 -070087 DCHECK(table_ != NULL);
88 DCHECK_LE(alloc_entries_, max_entries_);
Ian Rogersdc51b792011-09-22 20:41:37 -070089 DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
Elliott Hughes6c1a3942011-08-17 15:00:06 -070090
91 if (topIndex == alloc_entries_) {
92 /* reached end of allocated space; did we hit buffer max? */
93 if (topIndex == max_entries_) {
94 LOG(ERROR) << "JNI ERROR (app bug): " << kind_ << " table overflow "
95 << "(max=" << max_entries_ << ")";
96 Dump();
97 LOG(FATAL); // TODO: operator<< for IndirectReferenceTable
98 }
99
100 size_t newSize = alloc_entries_ * 2;
101 if (newSize > max_entries_) {
102 newSize = max_entries_;
103 }
104 DCHECK_GT(newSize, alloc_entries_);
105
Elliott Hughesba8eee12012-01-24 20:25:24 -0800106 table_ = reinterpret_cast<const Object**>(realloc(table_, newSize * sizeof(const Object*)));
107 slot_data_ = reinterpret_cast<IndirectRefSlot*>(realloc(slot_data_, newSize * sizeof(IndirectRefSlot)));
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700108 if (table_ == NULL || slot_data_ == NULL) {
109 LOG(ERROR) << "JNI ERROR (app bug): unable to expand "
110 << kind_ << " table (from "
111 << alloc_entries_ << " to " << newSize
112 << ", max=" << max_entries_ << ")";
113 Dump();
114 LOG(FATAL); // TODO: operator<< for IndirectReferenceTable
115 }
116
117 // Clear the newly-allocated slot_data_ elements.
118 memset(slot_data_ + alloc_entries_, 0, (newSize - alloc_entries_) * sizeof(IndirectRefSlot));
119
120 alloc_entries_ = newSize;
121 }
122
123 /*
124 * We know there's enough room in the table. Now we just need to find
125 * the right spot. If there's a hole, find it and fill it; otherwise,
126 * add to the end of the list.
127 */
128 IndirectRef result;
Ian Rogersdc51b792011-09-22 20:41:37 -0700129 int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles;
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700130 if (numHoles > 0) {
131 DCHECK_GT(topIndex, 1U);
132 /* find the first hole; likely to be near the end of the list */
Elliott Hughescf4c6c42011-09-01 15:16:42 -0700133 const Object** pScan = &table_[topIndex - 1];
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700134 DCHECK(*pScan != NULL);
135 while (*--pScan != NULL) {
136 DCHECK_GE(pScan, table_ + prevState.parts.topIndex);
137 }
138 UpdateSlotAdd(obj, pScan - table_);
139 result = ToIndirectRef(obj, pScan - table_);
140 *pScan = obj;
Ian Rogersdc51b792011-09-22 20:41:37 -0700141 segment_state_.parts.numHoles--;
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700142 } else {
143 /* add to the end */
144 UpdateSlotAdd(obj, topIndex);
145 result = ToIndirectRef(obj, topIndex);
146 table_[topIndex++] = obj;
Ian Rogersdc51b792011-09-22 20:41:37 -0700147 segment_state_.parts.topIndex = topIndex;
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700148 }
Ian Rogers5a7a74a2011-09-26 16:32:29 -0700149 if (false) {
150 LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.parts.topIndex
151 << " holes=" << segment_state_.parts.numHoles;
152 }
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700153
154 DCHECK(result != NULL);
155 return result;
156}
157
Elliott Hughes726079d2011-10-07 18:43:44 -0700158void IndirectReferenceTable::AssertEmpty() {
159 if (begin() != end()) {
160 Dump();
161 LOG(FATAL) << "Internal Error: non-empty local reference table";
162 }
163}
164
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700165/*
166 * Verify that the indirect table lookup is valid.
167 *
168 * Returns "false" if something looks bad.
169 */
170bool IndirectReferenceTable::GetChecked(IndirectRef iref) const {
171 if (iref == NULL) {
172 LOG(WARNING) << "Attempt to look up NULL " << kind_;
173 return false;
174 }
Ian Rogers408f79a2011-08-23 18:22:33 -0700175 if (GetIndirectRefKind(iref) == kSirtOrInvalid) {
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700176 LOG(ERROR) << "JNI ERROR (app bug): invalid " << kind_ << " " << iref;
177 AbortMaybe();
178 return false;
179 }
180
Ian Rogersdc51b792011-09-22 20:41:37 -0700181 int topIndex = segment_state_.parts.topIndex;
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700182 int idx = ExtractIndex(iref);
183 if (idx >= topIndex) {
184 /* bad -- stale reference? */
Ian Rogers5a7a74a2011-09-26 16:32:29 -0700185 LOG(ERROR) << "JNI ERROR (app bug): accessed stale " << kind_ << " "
186 << iref << " (index " << idx << " in a table of size " << topIndex << ")";
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700187 AbortMaybe();
188 return false;
189 }
190
191 if (table_[idx] == NULL) {
192 LOG(ERROR) << "JNI ERROR (app bug): accessed deleted " << kind_ << " " << iref;
193 AbortMaybe();
194 return false;
195 }
196
197 if (!CheckEntry("use", iref, idx)) {
198 return false;
199 }
200
201 return true;
202}
203
Elliott Hughes2ced6a52011-10-16 18:44:48 -0700204static int Find(Object* direct_pointer, int bottomIndex, int topIndex, const Object** table) {
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700205 for (int i = bottomIndex; i < topIndex; ++i) {
Elliott Hughes2ced6a52011-10-16 18:44:48 -0700206 if (table[i] == direct_pointer) {
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700207 return i;
208 }
209 }
210 return -1;
211}
212
Elliott Hughes2ced6a52011-10-16 18:44:48 -0700213bool IndirectReferenceTable::ContainsDirectPointer(Object* direct_pointer) const {
214 return Find(direct_pointer, 0, segment_state_.parts.topIndex, table_) != -1;
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700215}
216
217/*
218 * Remove "obj" from "pRef". We extract the table offset bits from "iref"
219 * and zap the corresponding entry, leaving a hole if it's not at the top.
220 *
221 * If the entry is not between the current top index and the bottom index
222 * specified by the cookie, we don't remove anything. This is the behavior
223 * required by JNI's DeleteLocalRef function.
224 *
225 * Note this is NOT called when a local frame is popped. This is only used
226 * for explicit single removals.
227 *
228 * Returns "false" if nothing was removed.
229 */
230bool IndirectReferenceTable::Remove(uint32_t cookie, IndirectRef iref) {
231 IRTSegmentState prevState;
232 prevState.all = cookie;
Ian Rogersdc51b792011-09-22 20:41:37 -0700233 int topIndex = segment_state_.parts.topIndex;
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700234 int bottomIndex = prevState.parts.topIndex;
235
236 DCHECK(table_ != NULL);
237 DCHECK_LE(alloc_entries_, max_entries_);
Ian Rogersdc51b792011-09-22 20:41:37 -0700238 DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700239
240 int idx = ExtractIndex(iref);
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700241
Elliott Hughesc5bfa8f2011-08-30 14:32:49 -0700242 JavaVMExt* vm = Runtime::Current()->GetJavaVM();
Ian Rogers5a7a74a2011-09-26 16:32:29 -0700243 if (GetIndirectRefKind(iref) == kSirtOrInvalid &&
TDYa12728f1a142012-03-15 21:51:52 -0700244 Thread::Current()->StackReferencesContain(reinterpret_cast<jobject>(iref))) {
Ian Rogers5a7a74a2011-09-26 16:32:29 -0700245 LOG(WARNING) << "Attempt to remove local SIRT entry from IRT, ignoring";
246 return true;
247 }
Ian Rogers467c9692012-02-21 11:05:16 -0800248 if (GetIndirectRefKind(iref) == kSirtOrInvalid && vm->work_around_app_jni_bugs) {
Elliott Hughes2ced6a52011-10-16 18:44:48 -0700249 Object* direct_pointer = reinterpret_cast<Object*>(iref);
250 idx = Find(direct_pointer, bottomIndex, topIndex, table_);
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700251 if (idx == -1) {
252 LOG(WARNING) << "trying to work around app JNI bugs, but didn't find " << iref << " in table!";
253 return false;
254 }
255 }
256
257 if (idx < bottomIndex) {
Elliott Hughes726079d2011-10-07 18:43:44 -0700258 // Wrong segment.
259 LOG(WARNING) << "Attempt to remove index outside index area (" << idx
260 << " vs " << bottomIndex << "-" << topIndex << ")";
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700261 return false;
262 }
263 if (idx >= topIndex) {
Elliott Hughes726079d2011-10-07 18:43:44 -0700264 // Bad --- stale reference?
265 LOG(WARNING) << "Attempt to remove invalid index " << idx
266 << " (bottom=" << bottomIndex << " top=" << topIndex << ")";
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700267 return false;
268 }
269
270 if (idx == topIndex-1) {
271 // Top-most entry. Scan up and consume holes.
272
Elliott Hughesc5bfa8f2011-08-30 14:32:49 -0700273 if (!vm->work_around_app_jni_bugs && !CheckEntry("remove", iref, idx)) {
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700274 return false;
275 }
276
277 table_[idx] = NULL;
Ian Rogersdc51b792011-09-22 20:41:37 -0700278 int numHoles = segment_state_.parts.numHoles - prevState.parts.numHoles;
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700279 if (numHoles != 0) {
280 while (--topIndex > bottomIndex && numHoles != 0) {
Ian Rogers5a7a74a2011-09-26 16:32:29 -0700281 if (false) {
282 LOG(INFO) << "+++ checking for hole at " << topIndex-1
283 << " (cookie=" << cookie << ") val=" << table_[topIndex - 1];
284 }
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700285 if (table_[topIndex-1] != NULL) {
286 break;
287 }
Ian Rogers5a7a74a2011-09-26 16:32:29 -0700288 if (false) {
289 LOG(INFO) << "+++ ate hole at " << (topIndex - 1);
290 }
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700291 numHoles--;
292 }
Ian Rogersdc51b792011-09-22 20:41:37 -0700293 segment_state_.parts.numHoles = numHoles + prevState.parts.numHoles;
294 segment_state_.parts.topIndex = topIndex;
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700295 } else {
Ian Rogersdc51b792011-09-22 20:41:37 -0700296 segment_state_.parts.topIndex = topIndex-1;
Ian Rogers5a7a74a2011-09-26 16:32:29 -0700297 if (false) {
298 LOG(INFO) << "+++ ate last entry " << topIndex - 1;
299 }
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700300 }
301 } else {
302 /*
303 * Not the top-most entry. This creates a hole. We NULL out the
304 * entry to prevent somebody from deleting it twice and screwing up
305 * the hole count.
306 */
307 if (table_[idx] == NULL) {
308 LOG(INFO) << "--- WEIRD: removing null entry " << idx;
309 return false;
310 }
Elliott Hughesc5bfa8f2011-08-30 14:32:49 -0700311 if (!vm->work_around_app_jni_bugs && !CheckEntry("remove", iref, idx)) {
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700312 return false;
313 }
314
315 table_[idx] = NULL;
Ian Rogersdc51b792011-09-22 20:41:37 -0700316 segment_state_.parts.numHoles++;
Ian Rogers5a7a74a2011-09-26 16:32:29 -0700317 if (false) {
318 LOG(INFO) << "+++ left hole at " << idx << ", holes=" << segment_state_.parts.numHoles;
319 }
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700320 }
321
322 return true;
323}
324
Elliott Hughes410c0c82011-09-01 17:58:25 -0700325void IndirectReferenceTable::VisitRoots(Heap::RootVisitor* visitor, void* arg) {
326 typedef IndirectReferenceTable::iterator It; // TODO: C++0x auto
327 for (It it = begin(), end = this->end(); it != end; ++it) {
328 visitor(**it, arg);
329 }
330}
331
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700332void IndirectReferenceTable::Dump() const {
333 LOG(WARNING) << kind_ << " table dump:";
Elliott Hughes75770752011-08-24 17:52:38 -0700334 std::vector<const Object*> entries(table_, table_ + Capacity());
Elliott Hughes6c1a3942011-08-17 15:00:06 -0700335 ReferenceTable::Dump(entries);
336}
337
338} // namespace art