summaryrefslogtreecommitdiffstats
path: root/vm/LinearAlloc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vm/LinearAlloc.cpp')
-rw-r--r--vm/LinearAlloc.cpp704
1 files changed, 704 insertions, 0 deletions
diff --git a/vm/LinearAlloc.cpp b/vm/LinearAlloc.cpp
new file mode 100644
index 000000000..dc937593c
--- /dev/null
+++ b/vm/LinearAlloc.cpp
@@ -0,0 +1,704 @@
+/*
+ * 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.
+ */
+
+/*
+ * Linear memory allocation, tied to class loaders.
+ */
+#include "Dalvik.h"
+
+#include <sys/mman.h>
+#include <limits.h>
+#include <errno.h>
+
+//#define DISABLE_LINEAR_ALLOC
+
+// Use ashmem to name the LinearAlloc section
+#define USE_ASHMEM 1
+
+#ifdef USE_ASHMEM
+#include <cutils/ashmem.h>
+#endif /* USE_ASHMEM */
+
+/*
+Overview
+
+This is intended to be a simple, fast allocator for "write-once" storage.
+The expectation is that this will hold small allocations that don't change,
+such as parts of classes (vtables, fields, methods, interfaces). Because
+the lifetime of these items is tied to classes, which in turn are tied
+to class loaders, we associate the storage with a ClassLoader object.
+
+[ We don't yet support class unloading, and our ClassLoader implementation
+is in flux, so for now we just have a single global region and the
+"classLoader" argument is ignored. ]
+
+By storing the data here, rather than on the system heap, we reduce heap
+clutter, speed class loading, reduce the memory footprint (reduced heap
+structure overhead), and most importantly we increase the number of pages
+that remain shared between processes launched in "Zygote mode".
+
+The 4 bytes preceding each block contain the block length. This allows us
+to support "free" and "realloc" calls in a limited way. We don't free
+storage once it has been allocated, but in some circumstances it could be
+useful to erase storage to garbage values after a "free" or "realloc".
+(Bad idea if we're trying to share pages.) We need to align to 8-byte
+boundaries for some architectures, so we have a 50-50 chance of getting
+this for free in a given block.
+
+A NULL value for the "classLoader" argument refers to the bootstrap class
+loader, which is never unloaded (until the VM shuts down).
+
+Because the memory is not expected to be updated, we can use mprotect to
+guard the pages on debug builds. Handy when tracking down corruption.
+*/
+
+/* alignment for allocations; must be power of 2, and currently >= hdr_xtra */
+#define BLOCK_ALIGN 8
+
+/* default length of memory segment (worst case is probably "dexopt") */
+#define DEFAULT_MAX_LENGTH (8*1024*1024)
+
+/* leave enough space for a length word */
+#define HEADER_EXTRA 4
+
+/* overload the length word */
+#define LENGTHFLAG_FREE 0x80000000
+#define LENGTHFLAG_RW 0x40000000
+#define LENGTHFLAG_MASK (~(LENGTHFLAG_FREE|LENGTHFLAG_RW))
+
+
+/* fwd */
+static void checkAllFree(Object* classLoader);
+
+
+/*
+ * Someday, retrieve the linear alloc struct associated with a particular
+ * class loader. For now, always use the boostrap loader's instance.
+ */
+static inline LinearAllocHdr* getHeader(Object* classLoader)
+{
+ return gDvm.pBootLoaderAlloc;
+}
+
+/*
+ * Convert a pointer to memory to a pointer to the block header (which is
+ * currently just a length word).
+ */
+static inline u4* getBlockHeader(void* mem)
+{
+ return ((u4*) mem) -1;
+}
+
+/*
+ * Create a new linear allocation block.
+ */
+LinearAllocHdr* dvmLinearAllocCreate(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return (LinearAllocHdr*) 0x12345;
+#endif
+ LinearAllocHdr* pHdr;
+
+ pHdr = (LinearAllocHdr*) malloc(sizeof(*pHdr));
+
+
+ /*
+ * "curOffset" points to the location of the next pre-block header,
+ * which means we have to advance to the next BLOCK_ALIGN address and
+ * back up.
+ *
+ * Note we leave the first page empty (see below), and start the
+ * first entry on the second page at an offset that ensures the next
+ * chunk of data will be properly aligned.
+ */
+ assert(BLOCK_ALIGN >= HEADER_EXTRA);
+ pHdr->curOffset = pHdr->firstOffset =
+ (BLOCK_ALIGN-HEADER_EXTRA) + SYSTEM_PAGE_SIZE;
+ pHdr->mapLength = DEFAULT_MAX_LENGTH;
+
+#ifdef USE_ASHMEM
+ int fd;
+
+ fd = ashmem_create_region("dalvik-LinearAlloc", DEFAULT_MAX_LENGTH);
+ if (fd < 0) {
+ LOGE("ashmem LinearAlloc failed %s", strerror(errno));
+ free(pHdr);
+ return NULL;
+ }
+
+ pHdr->mapAddr = (char*)mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd, 0);
+ if (pHdr->mapAddr == MAP_FAILED) {
+ LOGE("LinearAlloc mmap(%d) failed: %s\n", pHdr->mapLength,
+ strerror(errno));
+ free(pHdr);
+ close(fd);
+ return NULL;
+ }
+
+ close(fd);
+#else /*USE_ASHMEM*/
+ // MAP_ANON is listed as "deprecated" on Linux,
+ // but MAP_ANONYMOUS is not defined under Mac OS X.
+ pHdr->mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (pHdr->mapAddr == MAP_FAILED) {
+ LOGE("LinearAlloc mmap(%d) failed: %s\n", pHdr->mapLength,
+ strerror(errno));
+ free(pHdr);
+ return NULL;
+ }
+#endif /*USE_ASHMEM*/
+
+ /* region expected to begin on a page boundary */
+ assert(((int) pHdr->mapAddr & (SYSTEM_PAGE_SIZE-1)) == 0);
+
+ /* the system should initialize newly-mapped memory to zero */
+ assert(*(u4*) (pHdr->mapAddr + pHdr->curOffset) == 0);
+
+ /*
+ * Disable access to all except starting page. We will enable pages
+ * as we use them. This helps prevent bad pointers from working. The
+ * pages start out PROT_NONE, become read/write while we access them,
+ * then go to read-only after we finish our changes.
+ *
+ * We have to make the first page readable because we have 4 pad bytes,
+ * followed by 4 length bytes, giving an initial offset of 8. The
+ * generic code below assumes that there could have been a previous
+ * allocation that wrote into those 4 pad bytes, therefore the page
+ * must have been marked readable by the previous allocation.
+ *
+ * We insert an extra page in here to force a break in the memory map
+ * so we can see ourselves more easily in "showmap". Otherwise this
+ * stuff blends into the neighboring pages. [TODO: do we still need
+ * the extra page now that we have ashmem?]
+ */
+ if (mprotect(pHdr->mapAddr, pHdr->mapLength, PROT_NONE) != 0) {
+ LOGW("LinearAlloc init mprotect failed: %s\n", strerror(errno));
+ free(pHdr);
+ return NULL;
+ }
+ if (mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE, SYSTEM_PAGE_SIZE,
+ ENFORCE_READ_ONLY ? PROT_READ : PROT_READ|PROT_WRITE) != 0)
+ {
+ LOGW("LinearAlloc init mprotect #2 failed: %s\n", strerror(errno));
+ free(pHdr);
+ return NULL;
+ }
+
+ if (ENFORCE_READ_ONLY) {
+ /* allocate the per-page ref count */
+ int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
+ pHdr->writeRefCount = (short*)calloc(numPages, sizeof(short));
+ if (pHdr->writeRefCount == NULL) {
+ free(pHdr);
+ return NULL;
+ }
+ }
+
+ dvmInitMutex(&pHdr->lock);
+
+ LOGV("LinearAlloc: created region at %p-%p\n",
+ pHdr->mapAddr, pHdr->mapAddr + pHdr->mapLength-1);
+
+ return pHdr;
+}
+
+/*
+ * Destroy a linear allocation area.
+ *
+ * We do a trivial "has everything been freed?" check before unmapping the
+ * memory and freeing the LinearAllocHdr.
+ */
+void dvmLinearAllocDestroy(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return;
+#endif
+ LinearAllocHdr* pHdr = getHeader(classLoader);
+ if (pHdr == NULL)
+ return;
+
+ checkAllFree(classLoader);
+
+ //dvmLinearAllocDump(classLoader);
+
+ if (gDvm.verboseShutdown) {
+ LOGV("Unmapping linear allocator base=%p\n", pHdr->mapAddr);
+ LOGD("LinearAlloc %p used %d of %d (%d%%)\n",
+ classLoader, pHdr->curOffset, pHdr->mapLength,
+ (pHdr->curOffset * 100) / pHdr->mapLength);
+ }
+
+ if (munmap(pHdr->mapAddr, pHdr->mapLength) != 0) {
+ LOGW("LinearAlloc munmap(%p, %d) failed: %s\n",
+ pHdr->mapAddr, pHdr->mapLength, strerror(errno));
+ }
+ free(pHdr);
+}
+
+/*
+ * Allocate "size" bytes of storage, associated with a particular class
+ * loader.
+ *
+ * It's okay for size to be zero.
+ *
+ * We always leave "curOffset" pointing at the next place where we will
+ * store the header that precedes the returned storage.
+ *
+ * This aborts the VM on failure, so it's not necessary to check for a
+ * NULL return value.
+ */
+void* dvmLinearAlloc(Object* classLoader, size_t size)
+{
+ LinearAllocHdr* pHdr = getHeader(classLoader);
+ int startOffset, nextOffset;
+ int lastGoodOff, firstWriteOff, lastWriteOff;
+
+#ifdef DISABLE_LINEAR_ALLOC
+ return calloc(1, size);
+#endif
+
+ LOGVV("--- LinearAlloc(%p, %d)\n", classLoader, size);
+
+ /*
+ * What we'd like to do is just determine the new end-of-alloc size
+ * and atomic-swap the updated value in. The trouble is that, the
+ * first time we reach a new page, we need to call mprotect() to
+ * make the page available, and we don't want to call mprotect() on
+ * every allocation. The troubled situation is:
+ * - thread A allocs across a page boundary, but gets preempted
+ * before mprotect() completes
+ * - thread B allocs within the new page, and doesn't call mprotect()
+ */
+ dvmLockMutex(&pHdr->lock);
+
+ startOffset = pHdr->curOffset;
+ assert(((startOffset + HEADER_EXTRA) & (BLOCK_ALIGN-1)) == 0);
+
+ /*
+ * Compute the new offset. The old offset points at the address where
+ * we will store the hidden block header, so we advance past that,
+ * add the size of data they want, add another header's worth so we
+ * know we have room for that, and round up to BLOCK_ALIGN. That's
+ * the next location where we'll put user data. We then subtract the
+ * chunk header size off so we're back to the header pointer.
+ *
+ * Examples:
+ * old=12 size=3 new=((12+(4*2)+3+7) & ~7)-4 = 24-4 --> 20
+ * old=12 size=5 new=((12+(4*2)+5+7) & ~7)-4 = 32-4 --> 28
+ */
+ nextOffset = ((startOffset + HEADER_EXTRA*2 + size + (BLOCK_ALIGN-1))
+ & ~(BLOCK_ALIGN-1)) - HEADER_EXTRA;
+ LOGVV("--- old=%d size=%d new=%d\n", startOffset, size, nextOffset);
+
+ if (nextOffset > pHdr->mapLength) {
+ /*
+ * We don't have to abort here. We could fall back on the system
+ * malloc(), and have our "free" call figure out what to do. Only
+ * works if the users of these functions actually free everything
+ * they allocate.
+ */
+ LOGE("LinearAlloc exceeded capacity (%d), last=%d\n",
+ pHdr->mapLength, (int) size);
+ dvmAbort();
+ }
+
+ /*
+ * Round up "size" to encompass the entire region, including the 0-7
+ * pad bytes before the next chunk header. This way we get maximum
+ * utility out of "realloc", and when we're doing ENFORCE_READ_ONLY
+ * stuff we always treat the full extent.
+ */
+ size = nextOffset - (startOffset + HEADER_EXTRA);
+ LOGVV("--- (size now %d)\n", size);
+
+ /*
+ * See if we are starting on or have crossed into a new page. If so,
+ * call mprotect on the page(s) we're about to write to. We have to
+ * page-align the start address, but don't have to make the length a
+ * SYSTEM_PAGE_SIZE multiple (but we do it anyway).
+ *
+ * Note that "startOffset" is not the last *allocated* byte, but rather
+ * the offset of the first *unallocated* byte (which we are about to
+ * write the chunk header to). "nextOffset" is similar.
+ *
+ * If ENFORCE_READ_ONLY is enabled, we have to call mprotect even if
+ * we've written to this page before, because it might be read-only.
+ */
+ lastGoodOff = (startOffset-1) & ~(SYSTEM_PAGE_SIZE-1);
+ firstWriteOff = startOffset & ~(SYSTEM_PAGE_SIZE-1);
+ lastWriteOff = (nextOffset-1) & ~(SYSTEM_PAGE_SIZE-1);
+ LOGVV("--- lastGood=0x%04x firstWrite=0x%04x lastWrite=0x%04x\n",
+ lastGoodOff, firstWriteOff, lastWriteOff);
+ if (lastGoodOff != lastWriteOff || ENFORCE_READ_ONLY) {
+ int cc, start, len;
+
+ start = firstWriteOff;
+ assert(start <= nextOffset);
+ len = (lastWriteOff - firstWriteOff) + SYSTEM_PAGE_SIZE;
+
+ LOGVV("--- calling mprotect(start=%d len=%d RW)\n", start, len);
+ cc = mprotect(pHdr->mapAddr + start, len, PROT_READ | PROT_WRITE);
+ if (cc != 0) {
+ LOGE("LinearAlloc mprotect (+%d %d) failed: %s\n",
+ start, len, strerror(errno));
+ /* we're going to fail soon, might as do it now */
+ dvmAbort();
+ }
+ }
+
+ /* update the ref counts on the now-writable pages */
+ if (ENFORCE_READ_ONLY) {
+ int i, start, end;
+
+ start = firstWriteOff / SYSTEM_PAGE_SIZE;
+ end = lastWriteOff / SYSTEM_PAGE_SIZE;
+
+ LOGVV("--- marking pages %d-%d RW (alloc %d at %p)\n",
+ start, end, size, pHdr->mapAddr + startOffset + HEADER_EXTRA);
+ for (i = start; i <= end; i++)
+ pHdr->writeRefCount[i]++;
+ }
+
+ /* stow the size in the header */
+ if (ENFORCE_READ_ONLY)
+ *(u4*)(pHdr->mapAddr + startOffset) = size | LENGTHFLAG_RW;
+ else
+ *(u4*)(pHdr->mapAddr + startOffset) = size;
+
+ /*
+ * Update data structure.
+ */
+ pHdr->curOffset = nextOffset;
+
+ dvmUnlockMutex(&pHdr->lock);
+ return pHdr->mapAddr + startOffset + HEADER_EXTRA;
+}
+
+/*
+ * Helper function, replaces strdup().
+ */
+char* dvmLinearStrdup(Object* classLoader, const char* str)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return strdup(str);
+#endif
+ int len = strlen(str);
+ void* mem = dvmLinearAlloc(classLoader, len+1);
+ memcpy(mem, str, len+1);
+ if (ENFORCE_READ_ONLY)
+ dvmLinearSetReadOnly(classLoader, mem);
+ return (char*) mem;
+}
+
+/*
+ * "Reallocate" a piece of memory.
+ *
+ * If the new size is <= the old size, we return the original pointer
+ * without doing anything.
+ *
+ * If the new size is > the old size, we allocate new storage, copy the
+ * old stuff over, and mark the new stuff as free.
+ */
+void* dvmLinearRealloc(Object* classLoader, void* mem, size_t newSize)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return realloc(mem, newSize);
+#endif
+ /* make sure we have the right region (and mem != NULL) */
+ assert(mem != NULL);
+ assert(mem >= (void*) getHeader(classLoader)->mapAddr &&
+ mem < (void*) (getHeader(classLoader)->mapAddr +
+ getHeader(classLoader)->curOffset));
+
+ const u4* pLen = getBlockHeader(mem);
+ LOGV("--- LinearRealloc(%d) old=%d\n", newSize, *pLen);
+
+ /* handle size reduction case */
+ if (*pLen >= newSize) {
+ if (ENFORCE_READ_ONLY)
+ dvmLinearSetReadWrite(classLoader, mem);
+ return mem;
+ }
+
+ void* newMem;
+
+ newMem = dvmLinearAlloc(classLoader, newSize);
+ assert(newMem != NULL);
+ memcpy(newMem, mem, *pLen);
+ dvmLinearFree(classLoader, mem);
+
+ return newMem;
+}
+
+
+/*
+ * Update the read/write status of one or more pages.
+ */
+static void updatePages(Object* classLoader, void* mem, int direction)
+{
+ LinearAllocHdr* pHdr = getHeader(classLoader);
+ dvmLockMutex(&pHdr->lock);
+
+ /* make sure we have the right region */
+ assert(mem >= (void*) pHdr->mapAddr &&
+ mem < (void*) (pHdr->mapAddr + pHdr->curOffset));
+
+ u4* pLen = getBlockHeader(mem);
+ u4 len = *pLen & LENGTHFLAG_MASK;
+ int firstPage, lastPage;
+
+ firstPage = ((u1*)pLen - (u1*)pHdr->mapAddr) / SYSTEM_PAGE_SIZE;
+ lastPage = ((u1*)mem - (u1*)pHdr->mapAddr + (len-1)) / SYSTEM_PAGE_SIZE;
+ LOGVV("--- updating pages %d-%d (%d)\n", firstPage, lastPage, direction);
+
+ int i, cc;
+
+ /*
+ * Update individual pages. We could do some sort of "lazy update" to
+ * combine mprotect calls, but that's almost certainly more trouble
+ * than it's worth.
+ */
+ for (i = firstPage; i <= lastPage; i++) {
+ if (direction < 0) {
+ /*
+ * Trying to mark read-only.
+ */
+ if (i == firstPage) {
+ if ((*pLen & LENGTHFLAG_RW) == 0) {
+ LOGW("Double RO on %p\n", mem);
+ dvmAbort();
+ } else
+ *pLen &= ~LENGTHFLAG_RW;
+ }
+
+ if (pHdr->writeRefCount[i] == 0) {
+ LOGE("Can't make page %d any less writable\n", i);
+ dvmAbort();
+ }
+ pHdr->writeRefCount[i]--;
+ if (pHdr->writeRefCount[i] == 0) {
+ LOGVV("--- prot page %d RO\n", i);
+ cc = mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE * i,
+ SYSTEM_PAGE_SIZE, PROT_READ);
+ assert(cc == 0);
+ }
+ } else {
+ /*
+ * Trying to mark writable.
+ */
+ if (pHdr->writeRefCount[i] >= 32767) {
+ LOGE("Can't make page %d any more writable\n", i);
+ dvmAbort();
+ }
+ if (pHdr->writeRefCount[i] == 0) {
+ LOGVV("--- prot page %d RW\n", i);
+ cc = mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE * i,
+ SYSTEM_PAGE_SIZE, PROT_READ | PROT_WRITE);
+ assert(cc == 0);
+ }
+ pHdr->writeRefCount[i]++;
+
+ if (i == firstPage) {
+ if ((*pLen & LENGTHFLAG_RW) != 0) {
+ LOGW("Double RW on %p\n", mem);
+ dvmAbort();
+ } else
+ *pLen |= LENGTHFLAG_RW;
+ }
+ }
+ }
+
+ dvmUnlockMutex(&pHdr->lock);
+}
+
+/*
+ * Try to mark the pages in which a chunk of memory lives as read-only.
+ * Whether or not the pages actually change state depends on how many
+ * others are trying to access the same pages.
+ *
+ * Only call here if ENFORCE_READ_ONLY is true.
+ */
+void dvmLinearSetReadOnly(Object* classLoader, void* mem)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return;
+#endif
+ updatePages(classLoader, mem, -1);
+}
+
+/*
+ * Make the pages on which "mem" sits read-write.
+ *
+ * This covers the header as well as the data itself. (We could add a
+ * "header-only" mode for dvmLinearFree.)
+ *
+ * Only call here if ENFORCE_READ_ONLY is true.
+ */
+void dvmLinearSetReadWrite(Object* classLoader, void* mem)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return;
+#endif
+ updatePages(classLoader, mem, 1);
+}
+
+/*
+ * Mark an allocation as free.
+ */
+void dvmLinearFree(Object* classLoader, void* mem)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ free(mem);
+ return;
+#endif
+ if (mem == NULL)
+ return;
+
+ /* make sure we have the right region */
+ assert(mem >= (void*) getHeader(classLoader)->mapAddr &&
+ mem < (void*) (getHeader(classLoader)->mapAddr +
+ getHeader(classLoader)->curOffset));
+
+ if (ENFORCE_READ_ONLY)
+ dvmLinearSetReadWrite(classLoader, mem);
+
+ u4* pLen = getBlockHeader(mem);
+ *pLen |= LENGTHFLAG_FREE;
+
+ if (ENFORCE_READ_ONLY)
+ dvmLinearSetReadOnly(classLoader, mem);
+}
+
+/*
+ * For debugging, dump the contents of a linear alloc area.
+ *
+ * We grab the lock so that the header contents and list output are
+ * consistent.
+ */
+void dvmLinearAllocDump(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return;
+#endif
+ LinearAllocHdr* pHdr = getHeader(classLoader);
+
+ dvmLockMutex(&pHdr->lock);
+
+ LOGI("LinearAlloc classLoader=%p\n", classLoader);
+ LOGI(" mapAddr=%p mapLength=%d firstOffset=%d\n",
+ pHdr->mapAddr, pHdr->mapLength, pHdr->firstOffset);
+ LOGI(" curOffset=%d\n", pHdr->curOffset);
+
+ int off = pHdr->firstOffset;
+ u4 rawLen, fullLen;
+
+ while (off < pHdr->curOffset) {
+ rawLen = *(u4*) (pHdr->mapAddr + off);
+ fullLen = ((HEADER_EXTRA*2 + (rawLen & LENGTHFLAG_MASK))
+ & ~(BLOCK_ALIGN-1));
+
+ LOGI(" %p (%3d): %clen=%d%s\n", pHdr->mapAddr + off + HEADER_EXTRA,
+ (int) ((off + HEADER_EXTRA) / SYSTEM_PAGE_SIZE),
+ (rawLen & LENGTHFLAG_FREE) != 0 ? '*' : ' ',
+ rawLen & LENGTHFLAG_MASK,
+ (rawLen & LENGTHFLAG_RW) != 0 ? " [RW]" : "");
+
+ off += fullLen;
+ }
+
+ if (ENFORCE_READ_ONLY) {
+ LOGI("writeRefCount map:\n");
+
+ int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
+ int zstart = 0;
+ int i;
+
+ for (i = 0; i < numPages; i++) {
+ int count = pHdr->writeRefCount[i];
+
+ if (count != 0) {
+ if (zstart < i-1)
+ printf(" %d-%d: zero\n", zstart, i-1);
+ else if (zstart == i-1)
+ printf(" %d: zero\n", zstart);
+ zstart = i+1;
+ printf(" %d: %d\n", i, count);
+ }
+ }
+ if (zstart < i)
+ printf(" %d-%d: zero\n", zstart, i-1);
+ }
+
+ LOGD("LinearAlloc %p using %d of %d (%d%%)\n",
+ classLoader, pHdr->curOffset, pHdr->mapLength,
+ (pHdr->curOffset * 100) / pHdr->mapLength);
+
+ dvmUnlockMutex(&pHdr->lock);
+}
+
+/*
+ * Verify that all blocks are freed.
+ *
+ * This should only be done as we're shutting down, but there could be a
+ * daemon thread that's still trying to do something, so we grab the locks.
+ */
+static void checkAllFree(Object* classLoader)
+{
+#ifdef DISABLE_LINEAR_ALLOC
+ return;
+#endif
+ LinearAllocHdr* pHdr = getHeader(classLoader);
+
+ dvmLockMutex(&pHdr->lock);
+
+ int off = pHdr->firstOffset;
+ u4 rawLen, fullLen;
+
+ while (off < pHdr->curOffset) {
+ rawLen = *(u4*) (pHdr->mapAddr + off);
+ fullLen = ((HEADER_EXTRA*2 + (rawLen & LENGTHFLAG_MASK))
+ & ~(BLOCK_ALIGN-1));
+
+ if ((rawLen & LENGTHFLAG_FREE) == 0) {
+ LOGW("LinearAlloc %p not freed: %p len=%d\n", classLoader,
+ pHdr->mapAddr + off + HEADER_EXTRA, rawLen & LENGTHFLAG_MASK);
+ }
+
+ off += fullLen;
+ }
+
+ dvmUnlockMutex(&pHdr->lock);
+}
+
+/*
+ * Determine if [start, start+length) is contained in the in-use area of
+ * a single LinearAlloc. The full set of linear allocators is scanned.
+ *
+ * [ Since we currently only have one region, this is pretty simple. In
+ * the future we'll need to traverse a table of class loaders. ]
+ */
+bool dvmLinearAllocContains(const void* start, size_t length)
+{
+ LinearAllocHdr* pHdr = getHeader(NULL);
+
+ if (pHdr == NULL)
+ return false;
+
+ return (char*) start >= pHdr->mapAddr &&
+ ((char*)start + length) <= (pHdr->mapAddr + pHdr->curOffset);
+}