1 /*
2 * Copyright (C) 2008 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 * Reference table management.
19 */
20 #include "Dalvik.h"
21
22 /*
23 * Initialize a ReferenceTable structure.
24 */
dvmInitReferenceTable(ReferenceTable * pRef,int initialCount,int maxCount)25 bool dvmInitReferenceTable(ReferenceTable* pRef, int initialCount,
26 int maxCount)
27 {
28 assert(initialCount > 0);
29 assert(initialCount <= maxCount);
30
31 pRef->table = (Object**) malloc(initialCount * sizeof(Object*));
32 if (pRef->table == NULL)
33 return false;
34 #ifndef NDEBUG
35 memset(pRef->table, 0xdd, initialCount * sizeof(Object*));
36 #endif
37 pRef->nextEntry = pRef->table;
38 pRef->allocEntries = initialCount;
39 pRef->maxEntries = maxCount;
40
41 return true;
42 }
43
44 /*
45 * Clears out the contents of a ReferenceTable, freeing allocated storage.
46 */
dvmClearReferenceTable(ReferenceTable * pRef)47 void dvmClearReferenceTable(ReferenceTable* pRef)
48 {
49 free(pRef->table);
50 pRef->table = pRef->nextEntry = NULL;
51 pRef->allocEntries = pRef->maxEntries = -1;
52 }
53
54 /*
55 * Add "obj" to "pRef".
56 */
dvmAddToReferenceTable(ReferenceTable * pRef,Object * obj)57 bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj)
58 {
59 assert(obj != NULL);
60 assert(dvmIsHeapAddress(obj));
61 assert(pRef->table != NULL);
62 assert(pRef->allocEntries <= pRef->maxEntries);
63
64 if (pRef->nextEntry == pRef->table + pRef->allocEntries) {
65 /* reached end of allocated space; did we hit buffer max? */
66 if (pRef->nextEntry == pRef->table + pRef->maxEntries) {
67 ALOGW("ReferenceTable overflow (max=%d)", pRef->maxEntries);
68 return false;
69 }
70
71 Object** newTable;
72 int newSize;
73
74 newSize = pRef->allocEntries * 2;
75 if (newSize > pRef->maxEntries)
76 newSize = pRef->maxEntries;
77 assert(newSize > pRef->allocEntries);
78
79 newTable = (Object**) realloc(pRef->table, newSize * sizeof(Object*));
80 if (newTable == NULL) {
81 ALOGE("Unable to expand ref table (from %d to %d %d-byte entries)",
82 pRef->allocEntries, newSize, sizeof(Object*));
83 return false;
84 }
85 LOGVV("Growing %p from %d to %d", pRef, pRef->allocEntries, newSize);
86
87 /* update entries; adjust "nextEntry" in case memory moved */
88 pRef->nextEntry = newTable + (pRef->nextEntry - pRef->table);
89 pRef->table = newTable;
90 pRef->allocEntries = newSize;
91 }
92
93 *pRef->nextEntry++ = obj;
94 return true;
95 }
96
97 /*
98 * Returns NULL if not found.
99 */
dvmFindInReferenceTable(const ReferenceTable * pRef,Object ** bottom,Object * obj)100 Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom,
101 Object* obj)
102 {
103 Object** ptr;
104
105 ptr = pRef->nextEntry;
106 while (--ptr >= bottom) {
107 if (*ptr == obj)
108 return ptr;
109 }
110 return NULL;
111 }
112
113 /*
114 * Remove "obj" from "pRef". We start at the end of the list (where the
115 * most-recently-added element is), and stop searching for a match after
116 * examining the element at "bottom".
117 *
118 * Most of the time "obj" is at or near the end of the list. If not, we
119 * compact it down.
120 */
dvmRemoveFromReferenceTable(ReferenceTable * pRef,Object ** bottom,Object * obj)121 bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom,
122 Object* obj)
123 {
124 Object** ptr;
125
126 assert(pRef->table != NULL);
127
128 /*
129 * Scan from the most-recently-added entry up to the bottom entry for
130 * this frame.
131 */
132 ptr = dvmFindInReferenceTable(pRef, bottom, obj);
133 if (ptr == NULL)
134 return false;
135
136 /*
137 * Delete the entry.
138 */
139 pRef->nextEntry--;
140 int moveCount = pRef->nextEntry - ptr;
141 if (moveCount != 0) {
142 /* remove from middle, slide the rest down */
143 memmove(ptr, ptr+1, moveCount * sizeof(Object*));
144 //ALOGV("LREF delete %p, shift %d down", obj, moveCount);
145 } else {
146 /* last entry, falls off the end */
147 //ALOGV("LREF delete %p from end", obj);
148 }
149
150 return true;
151 }
152
153 /*
154 * If "obj" is an array, return the number of elements in the array.
155 * Otherwise, return zero.
156 */
getElementCount(const Object * obj)157 static size_t getElementCount(const Object* obj)
158 {
159 const ArrayObject* arrayObj = (ArrayObject*) obj;
160 if (arrayObj == NULL || arrayObj == kClearedJniWeakGlobal ||
161 arrayObj->clazz == NULL || !dvmIsArray(arrayObj)) {
162 return 0;
163 }
164 return arrayObj->length;
165 }
166
167 /*
168 * This is a qsort() callback. We sort Object* by class, allocation size,
169 * and then by the Object* itself.
170 */
compareObject(const void * vobj1,const void * vobj2)171 static int compareObject(const void* vobj1, const void* vobj2)
172 {
173 const Object* obj1 = *((Object* const*) vobj1);
174 const Object* obj2 = *((Object* const*) vobj2);
175
176 // Ensure null references and cleared jweaks appear at the end.
177 if (obj1 == NULL) {
178 if (obj2 == NULL) {
179 return 0;
180 } else {
181 return 1;
182 }
183 } else if (obj2 == NULL) {
184 return -1;
185 }
186 if (obj1 == kClearedJniWeakGlobal) {
187 if (obj2 == kClearedJniWeakGlobal) {
188 return 0;
189 } else {
190 return 1;
191 }
192 } else if (obj2 == kClearedJniWeakGlobal) {
193 return -1;
194 }
195
196 if (obj1->clazz != obj2->clazz) {
197 return (u1*)obj1->clazz - (u1*)obj2->clazz;
198 } else {
199 size_t count1 = getElementCount(obj1);
200 size_t count2 = getElementCount(obj2);
201 if (count1 != count2) {
202 return count1 - count2;
203 } else {
204 return (u1*)obj1 - (u1*)obj2;
205 }
206 }
207 }
208
209 /*
210 * Log an object with some additional info.
211 *
212 * Pass in the number of elements in the array (or 0 if this is not an
213 * array object), and the number of additional objects that are identical
214 * or equivalent to the original.
215 */
logSummaryLine(const Object * obj,size_t elems,int identical,int equiv)216 static void logSummaryLine(const Object* obj, size_t elems, int identical, int equiv)
217 {
218 if (obj == NULL) {
219 ALOGW(" NULL reference (count=%d)", equiv);
220 return;
221 }
222 if (obj == kClearedJniWeakGlobal) {
223 ALOGW(" cleared jweak (count=%d)", equiv);
224 return;
225 }
226
227 std::string className(dvmHumanReadableType(obj));
228 if (obj->clazz == gDvm.classJavaLangClass) {
229 // We're summarizing multiple instances, so using the exemplar
230 // Class' type parameter here would be misleading.
231 className = "java.lang.Class";
232 }
233 if (elems != 0) {
234 StringAppendF(&className, " (%zd elements)", elems);
235 }
236
237 size_t total = identical + equiv + 1;
238 std::string msg(StringPrintf("%5d of %s", total, className.c_str()));
239 if (identical + equiv != 0) {
240 StringAppendF(&msg, " (%d unique instances)", equiv + 1);
241 }
242 ALOGW(" %s", msg.c_str());
243 }
244
245 /*
246 * Dump a summary of an array of references to the log file.
247 *
248 * This is used to dump the contents of ReferenceTable and IndirectRefTable
249 * structs.
250 */
dvmDumpReferenceTableContents(Object * const * refs,size_t count,const char * descr)251 void dvmDumpReferenceTableContents(Object* const* refs, size_t count,
252 const char* descr)
253 {
254 ALOGW("%s reference table (%p) dump:", descr, refs);
255
256 if (count == 0) {
257 ALOGW(" (empty)");
258 return;
259 }
260
261 // Dump the most recent N entries.
262 const size_t kLast = 10;
263 int first = count - kLast;
264 if (first < 0) {
265 first = 0;
266 }
267 ALOGW(" Last %d entries (of %d):", (count - first), count);
268 for (int idx = count - 1; idx >= first; --idx) {
269 const Object* ref = refs[idx];
270 if (ref == NULL) {
271 continue;
272 }
273 if (ref == kClearedJniWeakGlobal) {
274 ALOGW(" %5d: cleared jweak", idx);
275 continue;
276 }
277 if (ref->clazz == NULL) {
278 // should only be possible right after a plain dvmMalloc().
279 size_t size = dvmObjectSizeInHeap(ref);
280 ALOGW(" %5d: %p (raw) (%zd bytes)", idx, ref, size);
281 continue;
282 }
283
284 std::string className(dvmHumanReadableType(ref));
285
286 std::string extras;
287 size_t elems = getElementCount(ref);
288 if (elems != 0) {
289 StringAppendF(&extras, " (%zd elements)", elems);
290 } else if (ref->clazz == gDvm.classJavaLangString) {
291 const StringObject* str =
292 reinterpret_cast<const StringObject*>(ref);
293 extras += " \"";
294 size_t count = 0;
295 char* s = dvmCreateCstrFromString(str);
296 char* p = s;
297 for (; *p && count < 16; ++p, ++count) {
298 extras += *p;
299 }
300 if (*p == 0) {
301 extras += "\"";
302 } else {
303 StringAppendF(&extras, "... (%d chars)", str->length());
304 }
305 free(s);
306 }
307 ALOGW(" %5d: %p %s%s", idx, ref, className.c_str(), extras.c_str());
308 }
309
310 // Make a copy of the table, and sort it.
311 Object** tableCopy = (Object**)malloc(sizeof(Object*) * count);
312 if (tableCopy == NULL) {
313 ALOGE("Unable to copy table with %d elements", count);
314 return;
315 }
316 memcpy(tableCopy, refs, sizeof(Object*) * count);
317 qsort(tableCopy, count, sizeof(Object*), compareObject);
318 refs = tableCopy; // use sorted list
319
320 // Remove any uninteresting stuff from the list. The sort moved them all to the end.
321 while (count > 0 && refs[count-1] == NULL) {
322 --count;
323 }
324 while (count > 0 && refs[count-1] == kClearedJniWeakGlobal) {
325 --count;
326 }
327 if (count == 0) {
328 return;
329 }
330
331 // Dump a summary of the whole table.
332 ALOGW(" Summary:");
333 size_t equiv, identical;
334 equiv = identical = 0;
335 size_t idx;
336 size_t elems;
337 for (idx = 1; idx < count; idx++) {
338 elems = getElementCount(refs[idx-1]);
339
340 if (refs[idx] == refs[idx-1]) {
341 // same reference, added more than once.
342 identical++;
343 } else if (refs[idx]->clazz == refs[idx-1]->clazz &&
344 getElementCount(refs[idx]) == elems)
345 {
346 // same class / element count, different object.
347 equiv++;
348 } else {
349 // different class.
350 logSummaryLine(refs[idx-1], elems, identical, equiv);
351 equiv = identical = 0;
352 }
353 }
354
355 // Handle the last entry (everything above outputs refs[i-1]).
356 elems = getElementCount(refs[idx-1]);
357 logSummaryLine(refs[count-1], elems, identical, equiv);
358
359 free(tableCopy);
360 }
361
362 /*
363 * Dump the contents of a ReferenceTable to the log.
364 */
dvmDumpReferenceTable(const ReferenceTable * pRef,const char * descr)365 void dvmDumpReferenceTable(const ReferenceTable* pRef, const char* descr)
366 {
367 dvmDumpReferenceTableContents(pRef->table, dvmReferenceTableEntries(pRef),
368 descr);
369 }
370