OSDN Git Service

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