blob: a8010a2517cb69fda7ac6b48a33879df7c9b0304 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
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 */
Andy McFaddend5ab7262009-08-25 07:19:34 -070016
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080017/*
18 * Reference table management.
19 */
20#include "Dalvik.h"
21
22/*
23 * Initialize a ReferenceTable structure.
24 */
25bool 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 */
47void 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 */
57bool dvmAddToReferenceTable(ReferenceTable* pRef, Object* obj)
58{
59 assert(dvmIsValidObject(obj));
60 assert(obj != NULL);
61 assert(pRef->table != NULL);
Andy McFadden734155e2009-07-16 18:11:22 -070062 assert(pRef->allocEntries <= pRef->maxEntries);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080063
Andy McFadden734155e2009-07-16 18:11:22 -070064 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)\n", pRef->maxEntries);
68 return false;
69 }
70
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080071 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)\n",
82 pRef->allocEntries, newSize, sizeof(Object*));
83 return false;
84 }
85 LOGVV("Growing %p from %d to %d\n", 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 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700100Object** dvmFindInReferenceTable(const ReferenceTable* pRef, Object** bottom,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800101 Object* obj)
102{
103 Object** ptr;
104
105 ptr = pRef->nextEntry;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700106 while (--ptr >= bottom) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800107 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
Andy McFaddend5ab7262009-08-25 07:19:34 -0700116 * examining the element at "bottom".
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800117 *
118 * Most of the time "obj" is at or near the end of the list. If not, we
119 * compact it down.
120 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700121bool dvmRemoveFromReferenceTable(ReferenceTable* pRef, Object** bottom,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800122 Object* obj)
123{
124 Object** ptr;
125
126 assert(pRef->table != NULL);
127
128 /*
Andy McFaddend5ab7262009-08-25 07:19:34 -0700129 * Scan from the most-recently-added entry up to the bottom entry for
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800130 * this frame.
131 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700132 ptr = dvmFindInReferenceTable(pRef, bottom, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800133 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\n", obj, moveCount);
145 } else {
146 /* last entry, falls off the end */
147 //LOGV("LREF delete %p from end\n", obj);
148 }
149
150 return true;
151}
152
153/*
154 * This is a qsort() callback. We sort Object* by class, allocation size,
155 * and then by the Object* itself.
156 */
157static int compareObject(const void* vobj1, const void* vobj2)
158{
159 Object* obj1 = *((Object**) vobj1);
160 Object* obj2 = *((Object**) vobj2);
161
162 if (obj1 == NULL || obj2 == NULL)
163 return (u1*)obj1 - (u1*)obj2;
164
165 if (obj1->clazz != obj2->clazz) {
166 return (u1*)obj1->clazz - (u1*)obj2->clazz;
167 } else {
168 int size1 = dvmObjectSizeInHeap(obj1);
169 int size2 = dvmObjectSizeInHeap(obj2);
170 if (size1 != size2) {
171 return size1 - size2;
172 } else {
173 return (u1*)obj1 - (u1*)obj2;
174 }
175 }
176}
177
178/*
179 * Log an object with some additional info.
180 *
181 * Pass in the number of additional elements that are identical to or
182 * equivalent to the original.
183 */
184static void logObject(Object* obj, int size, int identical, int equiv)
185{
186 if (obj == NULL) {
187 LOGW(" NULL reference (count=%d)\n", equiv);
188 return;
189 }
190
191 if (identical + equiv != 0) {
192 LOGW("%5d of %s %dB (%d unique)\n", identical + equiv +1,
193 obj->clazz->descriptor, size, equiv +1);
194 } else {
195 LOGW("%5d of %s %dB\n", identical + equiv +1,
196 obj->clazz->descriptor, size);
197 }
198}
199
200/*
201 * Dump the contents of a ReferenceTable to the log.
202 *
203 * The caller should lock any external sync before calling.
204 *
205 * (This was originally written to be tolerant of null entries in the table.
206 * I don't think that can happen anymore.)
207 */
208void dvmDumpReferenceTable(const ReferenceTable* pRef, const char* descr)
209{
210 const int kLast = 10;
211 int count = dvmReferenceTableEntries(pRef);
212 Object** refs;
213 int i;
214
215 if (count == 0) {
216 LOGW("Reference table has no entries\n");
217 return;
218 }
219 assert(count > 0);
220
221 /*
222 * Dump the most recent N entries.
223 */
224 LOGW("Last %d entries in %s reference table:\n", kLast, descr);
225 refs = pRef->table; // use unsorted list
226 int size;
227 int start = count - kLast;
228 if (start < 0)
229 start = 0;
230
231 for (i = start; i < count; i++) {
232 size = (refs[i] == NULL) ? 0 : dvmObjectSizeInHeap(refs[i]);
233 Object* ref = refs[i];
234 if (ref->clazz == gDvm.classJavaLangClass) {
235 ClassObject* clazz = (ClassObject*) ref;
236 LOGW("%5d: %p cls=%s '%s' (%d bytes)\n", i, ref,
237 (refs[i] == NULL) ? "-" : ref->clazz->descriptor,
238 clazz->descriptor, size);
239 } else {
240 LOGW("%5d: %p cls=%s (%d bytes)\n", i, ref,
241 (refs[i] == NULL) ? "-" : ref->clazz->descriptor, size);
242 }
243 }
244
245 /*
246 * Make a copy of the table, and sort it.
247 */
248 Object** tableCopy = (Object**)malloc(sizeof(Object*) * count);
249 memcpy(tableCopy, pRef->table, sizeof(Object*) * count);
250 qsort(tableCopy, count, sizeof(Object*), compareObject);
251 refs = tableCopy; // use sorted list
252
253 /*
254 * Dump uniquified table summary. While we're at it, generate a
255 * cumulative total amount of pinned memory based on the unique entries.
256 */
257 LOGW("%s reference table summary (%d entries):\n", descr, count);
258 int equiv, identical, total;
259 total = equiv = identical = 0;
260 for (i = 1; i < count; i++) {
261 size = (refs[i-1] == NULL) ? 0 : dvmObjectSizeInHeap(refs[i-1]);
262
263 if (refs[i] == refs[i-1]) {
264 /* same reference, added more than once */
265 identical++;
266 } else if (refs[i]->clazz == refs[i-1]->clazz &&
267 (int) dvmObjectSizeInHeap(refs[i]) == size)
268 {
269 /* same class / size, different object */
270 total += size;
271 equiv++;
272 } else {
273 /* different class */
274 total += size;
275 logObject(refs[i-1], size, identical, equiv);
276 equiv = identical = 0;
277 }
278 }
279
280 /* handle the last entry (everything above outputs refs[i-1]) */
281 size = (refs[count-1] == NULL) ? 0 : dvmObjectSizeInHeap(refs[count-1]);
282 total += size;
283 logObject(refs[count-1], size, identical, equiv);
284
285 LOGW("Memory held directly by native code is %d bytes\n", total);
286 free(tableCopy);
287}
288