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