blob: 8a18af3c25e8c5c921fd9222036915b77f63f2bb [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 */
Andy McFaddenb51ea112009-05-08 16:50:17 -070016
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080017/*
18 * Linear memory allocation, tied to class loaders.
19 */
20#include "Dalvik.h"
21
22#include <sys/mman.h>
23#include <limits.h>
24#include <errno.h>
25
26//#define DISABLE_LINEAR_ALLOC
27
28// Use ashmem to name the LinearAlloc section
29#define USE_ASHMEM 1
30
31#ifdef USE_ASHMEM
32#include <cutils/ashmem.h>
33#endif /* USE_ASHMEM */
34
35/*
36Overview
37
38This is intended to be a simple, fast allocator for "write-once" storage.
39The expectation is that this will hold small allocations that don't change,
40such as parts of classes (vtables, fields, methods, interfaces). Because
41the lifetime of these items is tied to classes, which in turn are tied
42to class loaders, we associate the storage with a ClassLoader object.
43
44[ We don't yet support class unloading, and our ClassLoader implementation
45is in flux, so for now we just have a single global region and the
46"classLoader" argument is ignored. ]
47
48By storing the data here, rather than on the system heap, we reduce heap
49clutter, speed class loading, reduce the memory footprint (reduced heap
50structure overhead), and most importantly we increase the number of pages
51that remain shared between processes launched in "Zygote mode".
52
53The 4 bytes preceding each block contain the block length. This allows us
54to support "free" and "realloc" calls in a limited way. We don't free
55storage once it has been allocated, but in some circumstances it could be
56useful to erase storage to garbage values after a "free" or "realloc".
57(Bad idea if we're trying to share pages.) We need to align to 8-byte
58boundaries for some architectures, so we have a 50-50 chance of getting
59this for free in a given block.
60
61A NULL value for the "classLoader" argument refers to the bootstrap class
62loader, which is never unloaded (until the VM shuts down).
63
64Because the memory is not expected to be updated, we can use mprotect to
65guard the pages on debug builds. Handy when tracking down corruption.
66*/
67
68/* alignment for allocations; must be power of 2, and currently >= hdr_xtra */
69#define BLOCK_ALIGN 8
70
71/* default length of memory segment (worst case is probably "dexopt") */
Andy McFadden68825972009-05-08 11:25:35 -070072#define DEFAULT_MAX_LENGTH (5*1024*1024)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080073
74/* leave enough space for a length word */
75#define HEADER_EXTRA 4
76
77/* overload the length word */
78#define LENGTHFLAG_FREE 0x80000000
79#define LENGTHFLAG_RW 0x40000000
80#define LENGTHFLAG_MASK (~(LENGTHFLAG_FREE|LENGTHFLAG_RW))
81
82/* in case limits.h doesn't have it; must be a power of 2 */
83#ifndef PAGESIZE
84# define PAGESIZE 4096
85#endif
86
87
88/* fwd */
89static void checkAllFree(Object* classLoader);
90
91
92/*
93 * Someday, retrieve the linear alloc struct associated with a particular
94 * class loader. For now, always use the boostrap loader's instance.
95 */
96static inline LinearAllocHdr* getHeader(Object* classLoader)
97{
98 return gDvm.pBootLoaderAlloc;
99}
100
101/*
102 * Convert a pointer to memory to a pointer to the block header (which is
103 * currently just a length word).
104 */
105static inline u4* getBlockHeader(void* mem)
106{
107 return ((u4*) mem) -1;
108}
109
110/*
111 * Create a new linear allocation block.
112 */
113LinearAllocHdr* dvmLinearAllocCreate(Object* classLoader)
114{
115#ifdef DISABLE_LINEAR_ALLOC
116 return (LinearAllocHdr*) 0x12345;
117#endif
118 LinearAllocHdr* pHdr;
119
120 pHdr = (LinearAllocHdr*) malloc(sizeof(*pHdr));
121
122
123 /*
124 * "curOffset" points to the location of the next pre-block header,
125 * which means we have to advance to the next BLOCK_ALIGN address and
126 * back up.
127 *
128 * Note we leave the first page empty (see below), and start the
129 * first entry on the second page at an offset that ensures the next
130 * chunk of data will be properly aligned.
131 */
132 assert(BLOCK_ALIGN >= HEADER_EXTRA);
133 pHdr->curOffset = pHdr->firstOffset = (BLOCK_ALIGN-HEADER_EXTRA) + PAGESIZE;
134 pHdr->mapLength = DEFAULT_MAX_LENGTH;
135
136#ifdef USE_ASHMEM
137 int fd;
138
139 fd = ashmem_create_region("dalvik-LinearAlloc", DEFAULT_MAX_LENGTH);
140 if (fd < 0) {
Bob Leecdacef52009-07-30 18:17:37 -0700141 LOGE("ashmem LinearAlloc failed %s", strerror(errno));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800142 free(pHdr);
143 return NULL;
144 }
145
146 pHdr->mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
147 MAP_PRIVATE, fd, 0);
148 if (pHdr->mapAddr == MAP_FAILED) {
149 LOGE("LinearAlloc mmap(%d) failed: %s\n", pHdr->mapLength,
150 strerror(errno));
151 free(pHdr);
Andy McFaddenb51ea112009-05-08 16:50:17 -0700152 close(fd);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800153 return NULL;
154 }
155
156 close(fd);
157#else /*USE_ASHMEM*/
Bob Leecdacef52009-07-30 18:17:37 -0700158 // MAP_ANON is listed as "deprecated" on Linux,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800159 // but MAP_ANONYMOUS is not defined under Mac OS X.
160 pHdr->mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
161 MAP_PRIVATE | MAP_ANON, -1, 0);
162 if (pHdr->mapAddr == MAP_FAILED) {
163 LOGE("LinearAlloc mmap(%d) failed: %s\n", pHdr->mapLength,
164 strerror(errno));
165 free(pHdr);
166 return NULL;
167 }
168#endif /*USE_ASHMEM*/
169
170 /* region expected to begin on a page boundary */
171 assert(((int) pHdr->mapAddr & (PAGESIZE-1)) == 0);
172
173 /* the system should initialize newly-mapped memory to zero */
174 assert(*(u4*) (pHdr->mapAddr + pHdr->curOffset) == 0);
175
176 /*
177 * Disable access to all except starting page. We will enable pages
178 * as we use them. This helps prevent bad pointers from working. The
179 * pages start out PROT_NONE, become read/write while we access them,
180 * then go to read-only after we finish our changes.
181 *
182 * We have to make the first page readable because we have 4 pad bytes,
183 * followed by 4 length bytes, giving an initial offset of 8. The
184 * generic code below assumes that there could have been a previous
185 * allocation that wrote into those 4 pad bytes, therefore the page
186 * must have been marked readable by the previous allocation.
187 *
188 * We insert an extra page in here to force a break in the memory map
189 * so we can see ourselves more easily in "showmap". Otherwise this
190 * stuff blends into the neighboring pages. [TODO: do we still need
191 * the extra page now that we have ashmem?]
192 */
193 if (mprotect(pHdr->mapAddr, pHdr->mapLength, PROT_NONE) != 0) {
194 LOGW("LinearAlloc init mprotect failed: %s\n", strerror(errno));
195 free(pHdr);
196 return NULL;
197 }
198 if (mprotect(pHdr->mapAddr + PAGESIZE, PAGESIZE,
199 ENFORCE_READ_ONLY ? PROT_READ : PROT_READ|PROT_WRITE) != 0)
200 {
201 LOGW("LinearAlloc init mprotect #2 failed: %s\n", strerror(errno));
202 free(pHdr);
203 return NULL;
204 }
205
206 if (ENFORCE_READ_ONLY) {
207 /* allocate the per-page ref count */
208 int numPages = (pHdr->mapLength+PAGESIZE-1) / PAGESIZE;
209 pHdr->writeRefCount = calloc(numPages, sizeof(short));
210 if (pHdr->writeRefCount == NULL) {
211 free(pHdr);
212 return NULL;
213 }
214 }
215
216 dvmInitMutex(&pHdr->lock);
217
218 LOGV("LinearAlloc: created region at %p-%p\n",
219 pHdr->mapAddr, pHdr->mapAddr + pHdr->mapLength-1);
220
221 return pHdr;
222}
223
224/*
225 * Destroy a linear allocation area.
226 *
227 * We do a trivial "has everything been freed?" check before unmapping the
228 * memory and freeing the LinearAllocHdr.
229 */
230void dvmLinearAllocDestroy(Object* classLoader)
231{
232#ifdef DISABLE_LINEAR_ALLOC
233 return;
234#endif
235 LinearAllocHdr* pHdr = getHeader(classLoader);
236 if (pHdr == NULL)
237 return;
238
239 checkAllFree(classLoader);
240
241 //dvmLinearAllocDump(classLoader);
242
243 LOGV("Unmapping linear allocator base=%p\n", pHdr->mapAddr);
244 LOGD("LinearAlloc %p used %d of %d (%d%%)\n",
245 classLoader, pHdr->curOffset, pHdr->mapLength,
246 (pHdr->curOffset * 100) / pHdr->mapLength);
247
248 if (munmap(pHdr->mapAddr, pHdr->mapLength) != 0) {
249 LOGW("LinearAlloc munmap(%p, %d) failed: %s\n",
250 pHdr->mapAddr, pHdr->mapLength, strerror(errno));
251 }
252 free(pHdr);
253}
254
255/*
256 * Allocate "size" bytes of storage, associated with a particular class
257 * loader.
258 *
259 * It's okay for size to be zero.
260 *
261 * We always leave "curOffset" pointing at the next place where we will
262 * store the header that precedes the returned storage.
263 *
264 * This aborts the VM on failure, so it's not necessary to check for a
265 * NULL return value.
266 */
267void* dvmLinearAlloc(Object* classLoader, size_t size)
268{
269 LinearAllocHdr* pHdr = getHeader(classLoader);
270 int startOffset, nextOffset;
271 int lastGoodOff, firstWriteOff, lastWriteOff;
272
273#ifdef DISABLE_LINEAR_ALLOC
274 return calloc(1, size);
275#endif
276
277 LOGVV("--- LinearAlloc(%p, %d)\n", classLoader, size);
278
279 /*
280 * What we'd like to do is just determine the new end-of-alloc size
281 * and atomic-swap the updated value in. The trouble is that, the
282 * first time we reach a new page, we need to call mprotect() to
283 * make the page available, and we don't want to call mprotect() on
284 * every allocation. The troubled situation is:
285 * - thread A allocs across a page boundary, but gets preempted
286 * before mprotect() completes
287 * - thread B allocs within the new page, and doesn't call mprotect()
288 */
289 dvmLockMutex(&pHdr->lock);
290
291 startOffset = pHdr->curOffset;
292 assert(((startOffset + HEADER_EXTRA) & (BLOCK_ALIGN-1)) == 0);
293
294 /*
295 * Compute the new offset. The old offset points at the address where
296 * we will store the hidden block header, so we advance past that,
297 * add the size of data they want, add another header's worth so we
298 * know we have room for that, and round up to BLOCK_ALIGN. That's
299 * the next location where we'll put user data. We then subtract the
300 * chunk header size off so we're back to the header pointer.
301 *
302 * Examples:
303 * old=12 size=3 new=((12+(4*2)+3+7) & ~7)-4 = 24-4 --> 20
304 * old=12 size=5 new=((12+(4*2)+5+7) & ~7)-4 = 32-4 --> 28
305 */
306 nextOffset = ((startOffset + HEADER_EXTRA*2 + size + (BLOCK_ALIGN-1))
307 & ~(BLOCK_ALIGN-1)) - HEADER_EXTRA;
308 LOGVV("--- old=%d size=%d new=%d\n", startOffset, size, nextOffset);
309
310 if (nextOffset > pHdr->mapLength) {
311 /*
312 * We don't have to abort here. We could fall back on the system
313 * malloc(), and have our "free" call figure out what to do. Only
314 * works if the users of these functions actually free everything
315 * they allocate.
316 */
Andy McFadden68825972009-05-08 11:25:35 -0700317 LOGE("LinearAlloc exceeded capacity (%d), last=%d\n",
318 pHdr->mapLength, (int) size);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800319 dvmAbort();
320 }
321
322 /*
323 * Round up "size" to encompass the entire region, including the 0-7
324 * pad bytes before the next chunk header. This way we get maximum
325 * utility out of "realloc", and when we're doing ENFORCE_READ_ONLY
326 * stuff we always treat the full extent.
327 */
328 size = nextOffset - (startOffset + HEADER_EXTRA);
329 LOGVV("--- (size now %d)\n", size);
330
331 /*
332 * See if we are starting on or have crossed into a new page. If so,
333 * call mprotect on the page(s) we're about to write to. We have to
334 * page-align the start address, but don't have to make the length a
335 * PAGESIZE multiple (but we do it anyway).
336 *
337 * Note that "startOffset" is not the last *allocated* byte, but rather
338 * the offset of the first *unallocated* byte (which we are about to
339 * write the chunk header to). "nextOffset" is similar.
340 *
341 * If ENFORCE_READ_ONLY is enabled, we have to call mprotect even if
342 * we've written to this page before, because it might be read-only.
343 */
344 lastGoodOff = (startOffset-1) & ~(PAGESIZE-1);
345 firstWriteOff = startOffset & ~(PAGESIZE-1);
346 lastWriteOff = (nextOffset-1) & ~(PAGESIZE-1);
347 LOGVV("--- lastGood=0x%04x firstWrite=0x%04x lastWrite=0x%04x\n",
348 lastGoodOff, firstWriteOff, lastWriteOff);
349 if (lastGoodOff != lastWriteOff || ENFORCE_READ_ONLY) {
350 int cc, start, len;
351
352 start = firstWriteOff;
353 assert(start <= nextOffset);
354 len = (lastWriteOff - firstWriteOff) + PAGESIZE;
355
356 LOGVV("--- calling mprotect(start=%d len=%d RW)\n", start, len);
357 cc = mprotect(pHdr->mapAddr + start, len, PROT_READ | PROT_WRITE);
358 if (cc != 0) {
359 LOGE("LinearAlloc mprotect (+%d %d) failed: %s\n",
360 start, len, strerror(errno));
361 /* we're going to fail soon, might as do it now */
362 dvmAbort();
363 }
364 }
365
366 /* update the ref counts on the now-writable pages */
367 if (ENFORCE_READ_ONLY) {
368 int i, start, end;
369
370 start = firstWriteOff / PAGESIZE;
371 end = lastWriteOff / PAGESIZE;
372
373 LOGVV("--- marking pages %d-%d RW (alloc %d at %p)\n",
374 start, end, size, pHdr->mapAddr + startOffset + HEADER_EXTRA);
375 for (i = start; i <= end; i++)
376 pHdr->writeRefCount[i]++;
377 }
378
379 /* stow the size in the header */
380 if (ENFORCE_READ_ONLY)
381 *(u4*)(pHdr->mapAddr + startOffset) = size | LENGTHFLAG_RW;
382 else
383 *(u4*)(pHdr->mapAddr + startOffset) = size;
384
385 /*
386 * Update data structure.
387 */
388 pHdr->curOffset = nextOffset;
389
390 dvmUnlockMutex(&pHdr->lock);
391 return pHdr->mapAddr + startOffset + HEADER_EXTRA;
392}
393
394/*
395 * Helper function, replaces strdup().
396 */
397char* dvmLinearStrdup(Object* classLoader, const char* str)
398{
399#ifdef DISABLE_LINEAR_ALLOC
400 return strdup(str);
401#endif
402 int len = strlen(str);
403 void* mem = dvmLinearAlloc(classLoader, len+1);
404 memcpy(mem, str, len+1);
405 if (ENFORCE_READ_ONLY)
406 dvmLinearSetReadOnly(classLoader, mem);
407 return (char*) mem;
408}
409
410/*
411 * "Reallocate" a piece of memory.
412 *
413 * If the new size is <= the old size, we return the original pointer
414 * without doing anything.
415 *
416 * If the new size is > the old size, we allocate new storage, copy the
417 * old stuff over, and mark the new stuff as free.
418 */
419void* dvmLinearRealloc(Object* classLoader, void* mem, size_t newSize)
420{
421#ifdef DISABLE_LINEAR_ALLOC
422 return realloc(mem, newSize);
423#endif
424 LinearAllocHdr* pHdr = getHeader(classLoader);
425
426 /* make sure we have the right region (and mem != NULL) */
427 assert(mem != NULL);
428 assert(mem >= (void*) pHdr->mapAddr &&
429 mem < (void*) (pHdr->mapAddr + pHdr->curOffset));
430
431 const u4* pLen = getBlockHeader(mem);
432 LOGV("--- LinearRealloc(%d) old=%d\n", newSize, *pLen);
433
434 /* handle size reduction case */
435 if (*pLen >= newSize) {
436 if (ENFORCE_READ_ONLY)
437 dvmLinearSetReadWrite(classLoader, mem);
438 return mem;
439 }
440
441 void* newMem;
442
443 newMem = dvmLinearAlloc(classLoader, newSize);
444 assert(newMem != NULL);
445 memcpy(newMem, mem, *pLen);
446 dvmLinearFree(classLoader, mem);
447
448 return newMem;
449}
450
451
452/*
453 * Update the read/write status of one or more pages.
454 */
455static void updatePages(Object* classLoader, void* mem, int direction)
456{
457 LinearAllocHdr* pHdr = getHeader(classLoader);
458 dvmLockMutex(&pHdr->lock);
459
460 /* make sure we have the right region */
461 assert(mem >= (void*) pHdr->mapAddr &&
462 mem < (void*) (pHdr->mapAddr + pHdr->curOffset));
463
464 u4* pLen = getBlockHeader(mem);
465 u4 len = *pLen & LENGTHFLAG_MASK;
466 int firstPage, lastPage;
467
468 firstPage = ((u1*)pLen - (u1*)pHdr->mapAddr) / PAGESIZE;
469 lastPage = ((u1*)mem - (u1*)pHdr->mapAddr + (len-1)) / PAGESIZE;
470 LOGVV("--- updating pages %d-%d (%d)\n", firstPage, lastPage, direction);
471
472 int i, cc;
473
474 /*
475 * Update individual pages. We could do some sort of "lazy update" to
476 * combine mprotect calls, but that's almost certainly more trouble
477 * than it's worth.
478 */
479 for (i = firstPage; i <= lastPage; i++) {
480 if (direction < 0) {
481 /*
482 * Trying to mark read-only.
483 */
484 if (i == firstPage) {
485 if ((*pLen & LENGTHFLAG_RW) == 0) {
486 LOGW("Double RO on %p\n", mem);
487 dvmAbort();
488 } else
489 *pLen &= ~LENGTHFLAG_RW;
490 }
491
492 if (pHdr->writeRefCount[i] == 0) {
493 LOGE("Can't make page %d any less writable\n", i);
494 dvmAbort();
495 }
496 pHdr->writeRefCount[i]--;
497 if (pHdr->writeRefCount[i] == 0) {
498 LOGVV("--- prot page %d RO\n", i);
499 cc = mprotect(pHdr->mapAddr + PAGESIZE * i, PAGESIZE, PROT_READ);
500 assert(cc == 0);
501 }
502 } else {
503 /*
504 * Trying to mark writable.
505 */
506 if (pHdr->writeRefCount[i] >= 32767) {
507 LOGE("Can't make page %d any more writable\n", i);
508 dvmAbort();
509 }
510 if (pHdr->writeRefCount[i] == 0) {
511 LOGVV("--- prot page %d RW\n", i);
512 cc = mprotect(pHdr->mapAddr + PAGESIZE * i, PAGESIZE,
513 PROT_READ | PROT_WRITE);
514 assert(cc == 0);
515 }
516 pHdr->writeRefCount[i]++;
517
518 if (i == firstPage) {
519 if ((*pLen & LENGTHFLAG_RW) != 0) {
520 LOGW("Double RW on %p\n", mem);
521 dvmAbort();
522 } else
523 *pLen |= LENGTHFLAG_RW;
524 }
525 }
526 }
527
528 dvmUnlockMutex(&pHdr->lock);
529}
530
531/*
532 * Try to mark the pages in which a chunk of memory lives as read-only.
533 * Whether or not the pages actually change state depends on how many
534 * others are trying to access the same pages.
535 *
536 * Only call here if ENFORCE_READ_ONLY is true.
537 */
538void dvmLinearSetReadOnly(Object* classLoader, void* mem)
539{
540#ifdef DISABLE_LINEAR_ALLOC
541 return;
542#endif
543 updatePages(classLoader, mem, -1);
544}
545
546/*
547 * Make the pages on which "mem" sits read-write.
548 *
549 * This covers the header as well as the data itself. (We could add a
550 * "header-only" mode for dvmLinearFree.)
551 *
552 * Only call here if ENFORCE_READ_ONLY is true.
553 */
554void dvmLinearSetReadWrite(Object* classLoader, void* mem)
555{
556#ifdef DISABLE_LINEAR_ALLOC
557 return;
558#endif
559 updatePages(classLoader, mem, 1);
560}
561
562/*
563 * Mark an allocation as free.
564 */
565void dvmLinearFree(Object* classLoader, void* mem)
566{
567#ifdef DISABLE_LINEAR_ALLOC
568 free(mem);
569 return;
570#endif
571 if (mem == NULL)
572 return;
573
574 LinearAllocHdr* pHdr = getHeader(classLoader);
575
576 /* make sure we have the right region */
577 assert(mem >= (void*) pHdr->mapAddr &&
578 mem < (void*) (pHdr->mapAddr + pHdr->curOffset));
579
580 if (ENFORCE_READ_ONLY)
581 dvmLinearSetReadWrite(classLoader, mem);
582
583 u4* pLen = getBlockHeader(mem);
584 *pLen |= LENGTHFLAG_FREE;
585
586 if (ENFORCE_READ_ONLY)
587 dvmLinearSetReadOnly(classLoader, mem);
588}
589
590/*
591 * For debugging, dump the contents of a linear alloc area.
592 *
593 * We grab the lock so that the header contents and list output are
594 * consistent.
595 */
596void dvmLinearAllocDump(Object* classLoader)
597{
598#ifdef DISABLE_LINEAR_ALLOC
599 return;
600#endif
601 LinearAllocHdr* pHdr = getHeader(classLoader);
602
603 dvmLockMutex(&pHdr->lock);
604
605 LOGI("LinearAlloc classLoader=%p\n", classLoader);
606 LOGI(" mapAddr=%p mapLength=%d firstOffset=%d\n",
607 pHdr->mapAddr, pHdr->mapLength, pHdr->firstOffset);
608 LOGI(" curOffset=%d\n", pHdr->curOffset);
609
610 int off = pHdr->firstOffset;
611 u4 rawLen, fullLen;
612
613 while (off < pHdr->curOffset) {
614 rawLen = *(u4*) (pHdr->mapAddr + off);
615 fullLen = ((HEADER_EXTRA*2 + (rawLen & LENGTHFLAG_MASK))
616 & ~(BLOCK_ALIGN-1));
617
618 LOGI(" %p (%3d): %clen=%d%s\n", pHdr->mapAddr + off + HEADER_EXTRA,
619 (int) ((off + HEADER_EXTRA) / PAGESIZE),
620 (rawLen & LENGTHFLAG_FREE) != 0 ? '*' : ' ',
621 rawLen & LENGTHFLAG_MASK,
622 (rawLen & LENGTHFLAG_RW) != 0 ? " [RW]" : "");
623
624 off += fullLen;
625 }
626
627 if (ENFORCE_READ_ONLY) {
628 LOGI("writeRefCount map:\n");
629
630 int numPages = (pHdr->mapLength+PAGESIZE-1) / PAGESIZE;
631 int zstart = 0;
632 int i;
633
634 for (i = 0; i < numPages; i++) {
635 int count = pHdr->writeRefCount[i];
636
637 if (count != 0) {
638 if (zstart < i-1)
639 printf(" %d-%d: zero\n", zstart, i-1);
640 else if (zstart == i-1)
641 printf(" %d: zero\n", zstart);
642 zstart = i+1;
643 printf(" %d: %d\n", i, count);
644 }
645 }
646 if (zstart < i)
647 printf(" %d-%d: zero\n", zstart, i-1);
648 }
649
650 LOGD("LinearAlloc %p using %d of %d (%d%%)\n",
651 classLoader, pHdr->curOffset, pHdr->mapLength,
652 (pHdr->curOffset * 100) / pHdr->mapLength);
653
654 dvmUnlockMutex(&pHdr->lock);
655}
656
657/*
658 * Verify that all blocks are freed.
659 *
660 * This should only be done as we're shutting down, but there could be a
661 * daemon thread that's still trying to do something, so we grab the locks.
662 */
663static void checkAllFree(Object* classLoader)
664{
665#ifdef DISABLE_LINEAR_ALLOC
666 return;
667#endif
668 LinearAllocHdr* pHdr = getHeader(classLoader);
669
670 dvmLockMutex(&pHdr->lock);
671
672 int off = pHdr->firstOffset;
673 u4 rawLen, fullLen;
674
675 while (off < pHdr->curOffset) {
676 rawLen = *(u4*) (pHdr->mapAddr + off);
677 fullLen = ((HEADER_EXTRA*2 + (rawLen & LENGTHFLAG_MASK))
678 & ~(BLOCK_ALIGN-1));
679
680 if ((rawLen & LENGTHFLAG_FREE) == 0) {
681 LOGW("LinearAlloc %p not freed: %p len=%d\n", classLoader,
682 pHdr->mapAddr + off + HEADER_EXTRA, rawLen & LENGTHFLAG_MASK);
683 }
684
685 off += fullLen;
686 }
687
688 dvmUnlockMutex(&pHdr->lock);
689}
690
Andy McFadden7605a842009-07-27 14:44:22 -0700691/*
692 * Determine if [start, start+length) is contained in the in-use area of
693 * a single LinearAlloc. The full set of linear allocators is scanned.
694 *
695 * [ Since we currently only have one region, this is pretty simple. In
696 * the future we'll need to traverse a table of class loaders. ]
697 */
Bob Leecdacef52009-07-30 18:17:37 -0700698bool dvmLinearAllocContains(const void* start, size_t length)
Andy McFadden7605a842009-07-27 14:44:22 -0700699{
700 LinearAllocHdr* pHdr = getHeader(NULL);
701
702 if (pHdr == NULL)
703 return false;
704
705 return (char*) start >= pHdr->mapAddr &&
706 ((char*)start + length) <= (pHdr->mapAddr + pHdr->curOffset);
707}
708