• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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             LOGW("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             LOGE("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         //LOGV("LREF delete %p, shift %d down", obj, moveCount);
145     } else {
146         /* last entry, falls off the end */
147         //LOGV("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         LOGW("    NULL reference (count=%d)", equiv);
220         return;
221     }
222     if (obj == kClearedJniWeakGlobal) {
223         LOGW("    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     LOGW("    %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     LOGW("%s reference table (%p) dump:", descr, refs);
255 
256     if (count == 0) {
257         LOGW("  (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     LOGW("  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             LOGW("    %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             LOGW("    %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         LOGW("    %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         LOGE("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     LOGW("  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