/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Dalvik.h"
#include "HeapBitmap.h"
#include <sys/mman.h> /* for PROT_* */
/*
* Initialize a HeapBitmap so that it points to a bitmap large
* enough to cover a heap at <base> of <maxSize> bytes, where
* objects are guaranteed to be HB_OBJECT_ALIGNMENT-aligned.
*/
bool dvmHeapBitmapInit(HeapBitmap *hb, const void *base, size_t maxSize,
const char *name)
{
void *bits;
size_t bitsLen;
assert(hb != NULL);
assert(name != NULL);
bitsLen = HB_OFFSET_TO_INDEX(maxSize) * sizeof(*hb->bits);
bits = dvmAllocRegion(bitsLen, PROT_READ | PROT_WRITE, name);
if (bits == NULL) {
LOGE("Could not mmap %zd-byte ashmem region '%s'", bitsLen, name);
return false;
}
hb->bits = (unsigned long *)bits;
hb->bitsLen = hb->allocLen = bitsLen;
hb->base = (uintptr_t)base;
hb->max = hb->base - 1;
return true;
}
/*
* Clean up any resources associated with the bitmap.
*/
void dvmHeapBitmapDelete(HeapBitmap *hb)
{
assert(hb != NULL);
if (hb->bits != NULL) {
munmap((char *)hb->bits, hb->allocLen);
}
memset(hb, 0, sizeof(*hb));
}
/*
* Fill the bitmap with zeroes. Returns the bitmap's memory to
* the system as a side-effect.
*/
void dvmHeapBitmapZero(HeapBitmap *hb)
{
assert(hb != NULL);
if (hb->bits != NULL) {
/* This returns the memory to the system.
* Successive page faults will return zeroed memory.
*/
madvise(hb->bits, hb->bitsLen, MADV_DONTNEED);
hb->max = hb->base - 1;
}
}
/*
* Return true iff <obj> is within the range of pointers that this
* bitmap could potentially cover, even if a bit has not been set
* for it.
*/
bool dvmHeapBitmapCoversAddress(const HeapBitmap *hb, const void *obj)
{
assert(hb != NULL);
if (obj != NULL) {
const uintptr_t offset = (uintptr_t)obj - hb->base;
const size_t index = HB_OFFSET_TO_INDEX(offset);
return index < hb->bitsLen / sizeof(*hb->bits);
}
return false;
}
/*
* Visits set bits in address order. The callback is not permitted to
* change the bitmap bits or max during the traversal.
*/
void dvmHeapBitmapWalk(const HeapBitmap *bitmap, BitmapCallback *callback,
void *arg)
{
assert(bitmap != NULL);
assert(bitmap->bits != NULL);
assert(callback != NULL);
uintptr_t end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
for (uintptr_t i = 0; i <= end; ++i) {
unsigned long word = bitmap->bits[i];
if (UNLIKELY(word != 0)) {
unsigned long highBit = 1 << (HB_BITS_PER_WORD - 1);
uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + bitmap->base;
while (word != 0) {
const int shift = CLZ(word);
Object* obj = (Object *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
(*callback)(obj, arg);
word &= ~(highBit >> shift);
}
}
}
}
/*
* Similar to dvmHeapBitmapWalk but the callback routine is permitted
* to change the bitmap bits and max during traversal. Used by the
* the root marking scan exclusively.
*
* The callback is invoked with a finger argument. The finger is a
* pointer to an address not yet visited by the traversal. If the
* callback sets a bit for an address at or above the finger, this
* address will be visited by the traversal. If the callback sets a
* bit for an address below the finger, this address will not be
* visited.
*/
void dvmHeapBitmapScanWalk(HeapBitmap *bitmap,
BitmapScanCallback *callback, void *arg)
{
assert(bitmap != NULL);
assert(bitmap->bits != NULL);
assert(callback != NULL);
uintptr_t end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
uintptr_t i;
for (i = 0; i <= end; ++i) {
unsigned long word = bitmap->bits[i];
if (UNLIKELY(word != 0)) {
unsigned long highBit = 1 << (HB_BITS_PER_WORD - 1);
uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + bitmap->base;
void *finger = (void *)(HB_INDEX_TO_OFFSET(i + 1) + bitmap->base);
while (word != 0) {
const int shift = CLZ(word);
Object *obj = (Object *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
(*callback)(obj, finger, arg);
word &= ~(highBit >> shift);
}
end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
}
}
}
/*
* Walk through the bitmaps in increasing address order, and find the
* object pointers that correspond to garbage objects. Call
* <callback> zero or more times with lists of these object pointers.
*
* The callback is not permitted to increase the max of either bitmap.
*/
void dvmHeapBitmapSweepWalk(const HeapBitmap *liveHb, const HeapBitmap *markHb,
uintptr_t base, uintptr_t max,
BitmapSweepCallback *callback, void *callbackArg)
{
assert(liveHb != NULL);
assert(liveHb->bits != NULL);
assert(markHb != NULL);
assert(markHb->bits != NULL);
assert(liveHb->base == markHb->base);
assert(liveHb->bitsLen == markHb->bitsLen);
assert(callback != NULL);
assert(base <= max);
assert(base >= liveHb->base);
assert(max <= liveHb->max);
if (liveHb->max < liveHb->base) {
/* Easy case; both are obviously empty.
*/
return;
}
void *pointerBuf[4 * HB_BITS_PER_WORD];
void **pb = pointerBuf;
size_t start = HB_OFFSET_TO_INDEX(base - liveHb->base);
size_t end = HB_OFFSET_TO_INDEX(max - liveHb->base);
unsigned long *live = liveHb->bits;
unsigned long *mark = markHb->bits;
for (size_t i = start; i <= end; i++) {
unsigned long garbage = live[i] & ~mark[i];
if (UNLIKELY(garbage != 0)) {
unsigned long highBit = 1 << (HB_BITS_PER_WORD - 1);
uintptr_t ptrBase = HB_INDEX_TO_OFFSET(i) + liveHb->base;
while (garbage != 0) {
int shift = CLZ(garbage);
garbage &= ~(highBit >> shift);
*pb++ = (void *)(ptrBase + shift * HB_OBJECT_ALIGNMENT);
}
/* Make sure that there are always enough slots available */
/* for an entire word of 1s. */
if (pb >= &pointerBuf[NELEM(pointerBuf) - HB_BITS_PER_WORD]) {
(*callback)(pb - pointerBuf, pointerBuf, callbackArg);
pb = pointerBuf;
}
}
}
if (pb > pointerBuf) {
(*callback)(pb - pointerBuf, pointerBuf, callbackArg);
}
}