blob: e2cf117b891967cd57fc560f4ee4e1214a94a727 [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 */
16
17#include "Dalvik.h"
18#include "HeapBitmap.h"
19#include "clz.h"
20#include <limits.h> // for ULONG_MAX
21#include <sys/mman.h> // for madvise(), mmap()
22#include <cutils/ashmem.h>
23
24#define HB_ASHMEM_NAME "dalvik-heap-bitmap"
25
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080026#define ALIGN_UP_TO_PAGE_SIZE(p) \
Andy McFadden96516932009-10-28 17:39:02 -070027 (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080028
29#define LIKELY(exp) (__builtin_expect((exp) != 0, true))
30#define UNLIKELY(exp) (__builtin_expect((exp) != 0, false))
31
32/*
33 * Initialize a HeapBitmap so that it points to a bitmap large
34 * enough to cover a heap at <base> of <maxSize> bytes, where
35 * objects are guaranteed to be HB_OBJECT_ALIGNMENT-aligned.
36 */
37bool
38dvmHeapBitmapInit(HeapBitmap *hb, const void *base, size_t maxSize,
39 const char *name)
40{
41 void *bits;
42 size_t bitsLen;
43 size_t allocLen;
44 int fd;
45 char nameBuf[ASHMEM_NAME_LEN] = HB_ASHMEM_NAME;
46
47 assert(hb != NULL);
48
49 bitsLen = HB_OFFSET_TO_INDEX(maxSize) * sizeof(*hb->bits);
50 allocLen = ALIGN_UP_TO_PAGE_SIZE(bitsLen); // required by ashmem
51
52 if (name != NULL) {
53 snprintf(nameBuf, sizeof(nameBuf), HB_ASHMEM_NAME "/%s", name);
54 }
55 fd = ashmem_create_region(nameBuf, allocLen);
56 if (fd < 0) {
57 LOGE("Could not create %zu-byte ashmem region \"%s\" to cover "
58 "%zu-byte heap (%d)\n",
59 allocLen, nameBuf, maxSize, fd);
60 return false;
61 }
62
63 bits = mmap(NULL, bitsLen, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
64 close(fd);
65 if (bits == MAP_FAILED) {
66 LOGE("Could not mmap %d-byte ashmem region \"%s\"\n",
67 bitsLen, nameBuf);
68 return false;
69 }
70
71 memset(hb, 0, sizeof(*hb));
72 hb->bits = bits;
Carl Shapiro8d724802010-02-14 18:40:47 -080073 hb->bitsLen = hb->allocLen = bitsLen;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080074 hb->base = (uintptr_t)base;
75 hb->max = hb->base - 1;
76
77 return true;
78}
79
80/*
81 * Initialize <hb> so that it covers the same extent as <templateBitmap>.
82 */
83bool
84dvmHeapBitmapInitFromTemplate(HeapBitmap *hb, const HeapBitmap *templateBitmap,
85 const char *name)
86{
87 return dvmHeapBitmapInit(hb,
88 (void *)templateBitmap->base, HB_MAX_OFFSET(templateBitmap), name);
89}
90
91/*
92 * Initialize the bitmaps in <out> so that they cover the same extent as
93 * the corresponding bitmaps in <templates>.
94 */
95bool
96dvmHeapBitmapInitListFromTemplates(HeapBitmap out[], HeapBitmap templates[],
97 size_t numBitmaps, const char *name)
98{
99 size_t i;
100 char fullName[PATH_MAX];
101
102 fullName[sizeof(fullName)-1] = '\0';
103 for (i = 0; i < numBitmaps; i++) {
104 bool ok;
105
106 /* If two ashmem regions have the same name, only one gets
107 * the name when looking at the maps.
108 */
109 snprintf(fullName, sizeof(fullName)-1, "%s/%zd", name, i);
110
111 ok = dvmHeapBitmapInitFromTemplate(&out[i], &templates[i], fullName);
112 if (!ok) {
113 dvmHeapBitmapDeleteList(out, i);
114 return false;
115 }
116 }
117 return true;
118}
119
120/*
121 * Clean up any resources associated with the bitmap.
122 */
123void
124dvmHeapBitmapDelete(HeapBitmap *hb)
125{
126 assert(hb != NULL);
127
128 if (hb->bits != NULL) {
Carl Shapiro8d724802010-02-14 18:40:47 -0800129 munmap((char *)hb->bits, hb->allocLen);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800130 }
131 memset(hb, 0, sizeof(*hb));
132}
133
134/*
135 * Clean up any resources associated with the bitmaps.
136 */
137void
138dvmHeapBitmapDeleteList(HeapBitmap hbs[], size_t numBitmaps)
139{
140 size_t i;
141
142 for (i = 0; i < numBitmaps; i++) {
143 dvmHeapBitmapDelete(&hbs[i]);
144 }
145}
146
147/*
148 * Fill the bitmap with zeroes. Returns the bitmap's memory to
149 * the system as a side-effect.
150 */
151void
152dvmHeapBitmapZero(HeapBitmap *hb)
153{
154 assert(hb != NULL);
155
156 if (hb->bits != NULL) {
157 /* This returns the memory to the system.
158 * Successive page faults will return zeroed memory.
159 */
160 madvise(hb->bits, hb->bitsLen, MADV_DONTNEED);
161 hb->max = hb->base - 1;
162 }
163}
164
165/*
166 * Walk through the bitmaps in increasing address order, and find the
167 * object pointers that correspond to places where the bitmaps differ.
168 * Call <callback> zero or more times with lists of these object pointers.
169 *
170 * The <finger> argument to the callback indicates the next-highest
171 * address that hasn't been visited yet; setting bits for objects whose
172 * addresses are less than <finger> are not guaranteed to be seen by
173 * the current XorWalk. <finger> will be set to ULONG_MAX when the
174 * end of the bitmap is reached.
175 */
176bool
177dvmHeapBitmapXorWalk(const HeapBitmap *hb1, const HeapBitmap *hb2,
178 bool (*callback)(size_t numPtrs, void **ptrs,
179 const void *finger, void *arg),
180 void *callbackArg)
181{
182 static const size_t kPointerBufSize = 128;
183 void *pointerBuf[kPointerBufSize];
184 void **pb = pointerBuf;
185 size_t index;
186 size_t i;
187
188#define FLUSH_POINTERBUF(finger_) \
189 do { \
190 if (!callback(pb - pointerBuf, (void **)pointerBuf, \
191 (void *)(finger_), callbackArg)) \
192 { \
193 LOGW("dvmHeapBitmapXorWalk: callback failed\n"); \
194 return false; \
195 } \
196 pb = pointerBuf; \
197 } while (false)
198
199#define DECODE_BITS(hb_, bits_, update_index_) \
200 do { \
201 if (UNLIKELY(bits_ != 0)) { \
202 static const unsigned long kHighBit = \
203 (unsigned long)1 << (HB_BITS_PER_WORD - 1); \
204 const uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + hb_->base; \
205/*TODO: hold onto ptrBase so we can shrink max later if possible */ \
206/*TODO: see if this is likely or unlikely */ \
207 while (bits_ != 0) { \
208 const int rshift = CLZ(bits_); \
209 bits_ &= ~(kHighBit >> rshift); \
210 *pb++ = (void *)(ptrBase + rshift * HB_OBJECT_ALIGNMENT); \
211 } \
212 /* Make sure that there are always enough slots available */ \
213 /* for an entire word of 1s. */ \
214 if (kPointerBufSize - (pb - pointerBuf) < HB_BITS_PER_WORD) { \
215 FLUSH_POINTERBUF(ptrBase + \
216 HB_BITS_PER_WORD * HB_OBJECT_ALIGNMENT); \
217 if (update_index_) { \
218 /* The callback may have caused hb_->max to grow. */ \
219 index = HB_OFFSET_TO_INDEX(hb_->max - hb_->base); \
220 } \
221 } \
222 } \
223 } while (false)
224
225 assert(hb1 != NULL);
226 assert(hb1->bits != NULL);
227 assert(hb2 != NULL);
228 assert(hb2->bits != NULL);
229 assert(callback != NULL);
230
231 if (hb1->base != hb2->base) {
232 LOGW("dvmHeapBitmapXorWalk: bitmaps cover different heaps "
233 "(0x%08x != 0x%08x)\n",
234 (uintptr_t)hb1->base, (uintptr_t)hb2->base);
235 return false;
236 }
237 if (hb1->bitsLen != hb2->bitsLen) {
238 LOGW("dvmHeapBitmapXorWalk: size of bitmaps differ (%zd != %zd)\n",
239 hb1->bitsLen, hb2->bitsLen);
240 return false;
241 }
242 if (hb1->max < hb1->base && hb2->max < hb2->base) {
243 /* Easy case; both are obviously empty.
244 */
245 return true;
246 }
247
248 /* First, walk along the section of the bitmaps that may be the same.
249 */
250 if (hb1->max >= hb1->base && hb2->max >= hb2->base) {
251 unsigned long int *p1, *p2;
252 uintptr_t offset;
253
254 offset = ((hb1->max < hb2->max) ? hb1->max : hb2->max) - hb1->base;
255//TODO: keep track of which (and whether) one is longer for later
256 index = HB_OFFSET_TO_INDEX(offset);
257
258 p1 = hb1->bits;
259 p2 = hb2->bits;
260 for (i = 0; i <= index; i++) {
261//TODO: unroll this. pile up a few in locals?
262 unsigned long int diff = *p1++ ^ *p2++;
263 DECODE_BITS(hb1, diff, false);
264//BUG: if the callback was called, either max could have changed.
265 }
266 /* The next index to look at.
267 */
268 index++;
269 } else {
270 /* One of the bitmaps is empty.
271 */
272 index = 0;
273 }
274
275 /* If one bitmap's max is larger, walk through the rest of the
276 * set bits.
277 */
278const HeapBitmap *longHb;
279unsigned long int *p;
280//TODO: may be the same size, in which case this is wasted work
281 longHb = (hb1->max > hb2->max) ? hb1 : hb2;
282 i = index;
283 index = HB_OFFSET_TO_INDEX(longHb->max - longHb->base);
284 p = longHb->bits + i;
285 for (/* i = i */; i <= index; i++) {
286//TODO: unroll this
287 unsigned long bits = *p++;
288 DECODE_BITS(longHb, bits, true);
289 }
290
291 if (pb > pointerBuf) {
292 /* Set the finger to the end of the heap (rather than longHb->max)
293 * so that the callback doesn't expect to be called again
294 * if it happens to change the current max.
295 */
296 FLUSH_POINTERBUF(longHb->base + HB_MAX_OFFSET(longHb));
297 }
298
299 return true;
300
301#undef FLUSH_POINTERBUF
302#undef DECODE_BITS
303}
304
305/*
306 * Fills outIndexList with indices so that for all i:
307 *
308 * hb[outIndexList[i]].base < hb[outIndexList[i+1]].base
309 */
310static void
311createSortedBitmapIndexList(const HeapBitmap hbs[], size_t numBitmaps,
312 size_t outIndexList[])
313{
314 int i, j;
315
316 /* numBitmaps is usually 2 or 3, so use a simple sort */
317 for (i = 0; i < (int) numBitmaps; i++) {
318 outIndexList[i] = i;
319 for (j = 0; j < i; j++) {
320 if (hbs[j].base > hbs[i].base) {
321 int tmp = outIndexList[i];
322 outIndexList[i] = outIndexList[j];
323 outIndexList[j] = tmp;
324 }
325 }
326 }
327}
328
329/*
330 * Similar to dvmHeapBitmapXorWalk(), but compare multiple bitmaps.
331 * Regardless of the order of the arrays, the bitmaps will be visited
332 * in address order, so that finger will increase monotonically.
333 */
334bool
335dvmHeapBitmapXorWalkLists(const HeapBitmap hbs1[], const HeapBitmap hbs2[],
336 size_t numBitmaps,
337 bool (*callback)(size_t numPtrs, void **ptrs,
338 const void *finger, void *arg),
339 void *callbackArg)
340{
341 size_t indexList[numBitmaps];
342 size_t i;
343
344 /* Sort the bitmaps by address. Assume that the two lists contain
345 * congruent bitmaps.
346 */
347 createSortedBitmapIndexList(hbs1, numBitmaps, indexList);
348
349 /* Walk each pair of bitmaps, lowest address first.
350 */
351 for (i = 0; i < numBitmaps; i++) {
352 bool ok;
353
354 ok = dvmHeapBitmapXorWalk(&hbs1[indexList[i]], &hbs2[indexList[i]],
355 callback, callbackArg);
356 if (!ok) {
357 return false;
358 }
359 }
360
361 return true;
362}
363
364/*
365 * Similar to dvmHeapBitmapXorWalk(), but visit the set bits
366 * in a single bitmap.
367 */
368bool
369dvmHeapBitmapWalk(const HeapBitmap *hb,
370 bool (*callback)(size_t numPtrs, void **ptrs,
371 const void *finger, void *arg),
372 void *callbackArg)
373{
374 /* Create an empty bitmap with the same extent as <hb>.
375 * Don't actually allocate any memory.
376 */
377 HeapBitmap emptyHb = *hb;
378 emptyHb.max = emptyHb.base - 1; // empty
379 emptyHb.bits = (void *)1; // non-NULL but intentionally bad
380
381 return dvmHeapBitmapXorWalk(hb, &emptyHb, callback, callbackArg);
382}
383
384/*
385 * Similar to dvmHeapBitmapXorWalkList(), but visit the set bits
386 * in a single list of bitmaps. Regardless of the order of the array,
387 * the bitmaps will be visited in address order, so that finger will
388 * increase monotonically.
389 */
390bool dvmHeapBitmapWalkList(const HeapBitmap hbs[], size_t numBitmaps,
391 bool (*callback)(size_t numPtrs, void **ptrs,
392 const void *finger, void *arg),
393 void *callbackArg)
394{
395 size_t indexList[numBitmaps];
396 size_t i;
397
398 /* Sort the bitmaps by address.
399 */
400 createSortedBitmapIndexList(hbs, numBitmaps, indexList);
401
402 /* Walk each bitmap, lowest address first.
403 */
404 for (i = 0; i < numBitmaps; i++) {
405 bool ok;
406
407 ok = dvmHeapBitmapWalk(&hbs[indexList[i]], callback, callbackArg);
408 if (!ok) {
409 return false;
410 }
411 }
412
413 return true;
414}