blob: 4b5c0a3ce5b67debff523e78a8796dc11d292c90 [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/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080081 * Clean up any resources associated with the bitmap.
82 */
83void
84dvmHeapBitmapDelete(HeapBitmap *hb)
85{
86 assert(hb != NULL);
87
88 if (hb->bits != NULL) {
Carl Shapiro8d724802010-02-14 18:40:47 -080089 munmap((char *)hb->bits, hb->allocLen);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080090 }
91 memset(hb, 0, sizeof(*hb));
92}
93
94/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080095 * Fill the bitmap with zeroes. Returns the bitmap's memory to
96 * the system as a side-effect.
97 */
98void
99dvmHeapBitmapZero(HeapBitmap *hb)
100{
101 assert(hb != NULL);
102
103 if (hb->bits != NULL) {
104 /* This returns the memory to the system.
105 * Successive page faults will return zeroed memory.
106 */
107 madvise(hb->bits, hb->bitsLen, MADV_DONTNEED);
108 hb->max = hb->base - 1;
109 }
110}
111
112/*
113 * Walk through the bitmaps in increasing address order, and find the
114 * object pointers that correspond to places where the bitmaps differ.
115 * Call <callback> zero or more times with lists of these object pointers.
116 *
117 * The <finger> argument to the callback indicates the next-highest
118 * address that hasn't been visited yet; setting bits for objects whose
119 * addresses are less than <finger> are not guaranteed to be seen by
120 * the current XorWalk. <finger> will be set to ULONG_MAX when the
121 * end of the bitmap is reached.
122 */
123bool
124dvmHeapBitmapXorWalk(const HeapBitmap *hb1, const HeapBitmap *hb2,
125 bool (*callback)(size_t numPtrs, void **ptrs,
126 const void *finger, void *arg),
127 void *callbackArg)
128{
129 static const size_t kPointerBufSize = 128;
130 void *pointerBuf[kPointerBufSize];
131 void **pb = pointerBuf;
132 size_t index;
133 size_t i;
134
135#define FLUSH_POINTERBUF(finger_) \
136 do { \
137 if (!callback(pb - pointerBuf, (void **)pointerBuf, \
138 (void *)(finger_), callbackArg)) \
139 { \
140 LOGW("dvmHeapBitmapXorWalk: callback failed\n"); \
141 return false; \
142 } \
143 pb = pointerBuf; \
144 } while (false)
145
146#define DECODE_BITS(hb_, bits_, update_index_) \
147 do { \
148 if (UNLIKELY(bits_ != 0)) { \
149 static const unsigned long kHighBit = \
150 (unsigned long)1 << (HB_BITS_PER_WORD - 1); \
151 const uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + hb_->base; \
152/*TODO: hold onto ptrBase so we can shrink max later if possible */ \
153/*TODO: see if this is likely or unlikely */ \
154 while (bits_ != 0) { \
155 const int rshift = CLZ(bits_); \
156 bits_ &= ~(kHighBit >> rshift); \
157 *pb++ = (void *)(ptrBase + rshift * HB_OBJECT_ALIGNMENT); \
158 } \
159 /* Make sure that there are always enough slots available */ \
160 /* for an entire word of 1s. */ \
161 if (kPointerBufSize - (pb - pointerBuf) < HB_BITS_PER_WORD) { \
162 FLUSH_POINTERBUF(ptrBase + \
163 HB_BITS_PER_WORD * HB_OBJECT_ALIGNMENT); \
164 if (update_index_) { \
165 /* The callback may have caused hb_->max to grow. */ \
166 index = HB_OFFSET_TO_INDEX(hb_->max - hb_->base); \
167 } \
168 } \
169 } \
170 } while (false)
171
172 assert(hb1 != NULL);
173 assert(hb1->bits != NULL);
174 assert(hb2 != NULL);
175 assert(hb2->bits != NULL);
176 assert(callback != NULL);
177
178 if (hb1->base != hb2->base) {
179 LOGW("dvmHeapBitmapXorWalk: bitmaps cover different heaps "
180 "(0x%08x != 0x%08x)\n",
181 (uintptr_t)hb1->base, (uintptr_t)hb2->base);
182 return false;
183 }
184 if (hb1->bitsLen != hb2->bitsLen) {
185 LOGW("dvmHeapBitmapXorWalk: size of bitmaps differ (%zd != %zd)\n",
186 hb1->bitsLen, hb2->bitsLen);
187 return false;
188 }
189 if (hb1->max < hb1->base && hb2->max < hb2->base) {
190 /* Easy case; both are obviously empty.
191 */
192 return true;
193 }
194
195 /* First, walk along the section of the bitmaps that may be the same.
196 */
197 if (hb1->max >= hb1->base && hb2->max >= hb2->base) {
198 unsigned long int *p1, *p2;
199 uintptr_t offset;
200
201 offset = ((hb1->max < hb2->max) ? hb1->max : hb2->max) - hb1->base;
202//TODO: keep track of which (and whether) one is longer for later
203 index = HB_OFFSET_TO_INDEX(offset);
204
205 p1 = hb1->bits;
206 p2 = hb2->bits;
207 for (i = 0; i <= index; i++) {
208//TODO: unroll this. pile up a few in locals?
209 unsigned long int diff = *p1++ ^ *p2++;
210 DECODE_BITS(hb1, diff, false);
211//BUG: if the callback was called, either max could have changed.
212 }
213 /* The next index to look at.
214 */
215 index++;
216 } else {
217 /* One of the bitmaps is empty.
218 */
219 index = 0;
220 }
221
222 /* If one bitmap's max is larger, walk through the rest of the
223 * set bits.
224 */
225const HeapBitmap *longHb;
226unsigned long int *p;
227//TODO: may be the same size, in which case this is wasted work
228 longHb = (hb1->max > hb2->max) ? hb1 : hb2;
229 i = index;
230 index = HB_OFFSET_TO_INDEX(longHb->max - longHb->base);
231 p = longHb->bits + i;
232 for (/* i = i */; i <= index; i++) {
233//TODO: unroll this
234 unsigned long bits = *p++;
235 DECODE_BITS(longHb, bits, true);
236 }
237
238 if (pb > pointerBuf) {
239 /* Set the finger to the end of the heap (rather than longHb->max)
240 * so that the callback doesn't expect to be called again
241 * if it happens to change the current max.
242 */
243 FLUSH_POINTERBUF(longHb->base + HB_MAX_OFFSET(longHb));
244 }
245
246 return true;
247
248#undef FLUSH_POINTERBUF
249#undef DECODE_BITS
250}
251
252/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800253 * Similar to dvmHeapBitmapXorWalk(), but visit the set bits
254 * in a single bitmap.
255 */
256bool
257dvmHeapBitmapWalk(const HeapBitmap *hb,
258 bool (*callback)(size_t numPtrs, void **ptrs,
259 const void *finger, void *arg),
260 void *callbackArg)
261{
262 /* Create an empty bitmap with the same extent as <hb>.
263 * Don't actually allocate any memory.
264 */
265 HeapBitmap emptyHb = *hb;
266 emptyHb.max = emptyHb.base - 1; // empty
267 emptyHb.bits = (void *)1; // non-NULL but intentionally bad
268
269 return dvmHeapBitmapXorWalk(hb, &emptyHb, callback, callbackArg);
270}