blob: 359893f64fb421c6c06605b51259338fd495e8ef [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") */
Elliott Hughes259298d2012-05-09 17:45:01 -070072#define DEFAULT_MAX_LENGTH (16*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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080082
83/* fwd */
84static void checkAllFree(Object* classLoader);
85
86
87/*
88 * Someday, retrieve the linear alloc struct associated with a particular
89 * class loader. For now, always use the boostrap loader's instance.
90 */
91static inline LinearAllocHdr* getHeader(Object* classLoader)
92{
93 return gDvm.pBootLoaderAlloc;
94}
95
96/*
97 * Convert a pointer to memory to a pointer to the block header (which is
98 * currently just a length word).
99 */
100static inline u4* getBlockHeader(void* mem)
101{
102 return ((u4*) mem) -1;
103}
104
105/*
106 * Create a new linear allocation block.
107 */
108LinearAllocHdr* dvmLinearAllocCreate(Object* classLoader)
109{
110#ifdef DISABLE_LINEAR_ALLOC
111 return (LinearAllocHdr*) 0x12345;
112#endif
113 LinearAllocHdr* pHdr;
114
115 pHdr = (LinearAllocHdr*) malloc(sizeof(*pHdr));
116
117
118 /*
119 * "curOffset" points to the location of the next pre-block header,
120 * which means we have to advance to the next BLOCK_ALIGN address and
121 * back up.
122 *
123 * Note we leave the first page empty (see below), and start the
124 * first entry on the second page at an offset that ensures the next
125 * chunk of data will be properly aligned.
126 */
127 assert(BLOCK_ALIGN >= HEADER_EXTRA);
Andy McFadden96516932009-10-28 17:39:02 -0700128 pHdr->curOffset = pHdr->firstOffset =
129 (BLOCK_ALIGN-HEADER_EXTRA) + SYSTEM_PAGE_SIZE;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800130 pHdr->mapLength = DEFAULT_MAX_LENGTH;
131
132#ifdef USE_ASHMEM
133 int fd;
134
135 fd = ashmem_create_region("dalvik-LinearAlloc", DEFAULT_MAX_LENGTH);
136 if (fd < 0) {
Steve Blockc1a4ab92012-01-06 19:16:58 +0000137 ALOGE("ashmem LinearAlloc failed %s", strerror(errno));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800138 free(pHdr);
139 return NULL;
140 }
141
Carl Shapirofc75f3e2010-12-07 11:43:38 -0800142 pHdr->mapAddr = (char*)mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800143 MAP_PRIVATE, fd, 0);
144 if (pHdr->mapAddr == MAP_FAILED) {
Steve Blockc1a4ab92012-01-06 19:16:58 +0000145 ALOGE("LinearAlloc mmap(%d) failed: %s", pHdr->mapLength,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800146 strerror(errno));
147 free(pHdr);
Andy McFaddenb51ea112009-05-08 16:50:17 -0700148 close(fd);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800149 return NULL;
150 }
151
152 close(fd);
153#else /*USE_ASHMEM*/
Bob Leecdacef52009-07-30 18:17:37 -0700154 // MAP_ANON is listed as "deprecated" on Linux,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800155 // but MAP_ANONYMOUS is not defined under Mac OS X.
156 pHdr->mapAddr = mmap(NULL, pHdr->mapLength, PROT_READ | PROT_WRITE,
157 MAP_PRIVATE | MAP_ANON, -1, 0);
158 if (pHdr->mapAddr == MAP_FAILED) {
Steve Blockc1a4ab92012-01-06 19:16:58 +0000159 ALOGE("LinearAlloc mmap(%d) failed: %s", pHdr->mapLength,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800160 strerror(errno));
161 free(pHdr);
162 return NULL;
163 }
164#endif /*USE_ASHMEM*/
165
166 /* region expected to begin on a page boundary */
Andy McFadden96516932009-10-28 17:39:02 -0700167 assert(((int) pHdr->mapAddr & (SYSTEM_PAGE_SIZE-1)) == 0);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800168
169 /* the system should initialize newly-mapped memory to zero */
170 assert(*(u4*) (pHdr->mapAddr + pHdr->curOffset) == 0);
171
172 /*
173 * Disable access to all except starting page. We will enable pages
174 * as we use them. This helps prevent bad pointers from working. The
175 * pages start out PROT_NONE, become read/write while we access them,
176 * then go to read-only after we finish our changes.
177 *
178 * We have to make the first page readable because we have 4 pad bytes,
179 * followed by 4 length bytes, giving an initial offset of 8. The
180 * generic code below assumes that there could have been a previous
181 * allocation that wrote into those 4 pad bytes, therefore the page
182 * must have been marked readable by the previous allocation.
183 *
184 * We insert an extra page in here to force a break in the memory map
185 * so we can see ourselves more easily in "showmap". Otherwise this
186 * stuff blends into the neighboring pages. [TODO: do we still need
187 * the extra page now that we have ashmem?]
188 */
189 if (mprotect(pHdr->mapAddr, pHdr->mapLength, PROT_NONE) != 0) {
Steve Blocke8e1ddc2012-01-05 23:21:27 +0000190 ALOGW("LinearAlloc init mprotect failed: %s", strerror(errno));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800191 free(pHdr);
192 return NULL;
193 }
Andy McFadden96516932009-10-28 17:39:02 -0700194 if (mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE, SYSTEM_PAGE_SIZE,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800195 ENFORCE_READ_ONLY ? PROT_READ : PROT_READ|PROT_WRITE) != 0)
196 {
Steve Blocke8e1ddc2012-01-05 23:21:27 +0000197 ALOGW("LinearAlloc init mprotect #2 failed: %s", strerror(errno));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800198 free(pHdr);
199 return NULL;
200 }
201
202 if (ENFORCE_READ_ONLY) {
203 /* allocate the per-page ref count */
Andy McFadden96516932009-10-28 17:39:02 -0700204 int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
Carl Shapirofc75f3e2010-12-07 11:43:38 -0800205 pHdr->writeRefCount = (short*)calloc(numPages, sizeof(short));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800206 if (pHdr->writeRefCount == NULL) {
207 free(pHdr);
208 return NULL;
209 }
210 }
211
212 dvmInitMutex(&pHdr->lock);
213
Steve Block92c1f6f2011-10-20 11:55:54 +0100214 ALOGV("LinearAlloc: created region at %p-%p",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800215 pHdr->mapAddr, pHdr->mapAddr + pHdr->mapLength-1);
216
217 return pHdr;
218}
219
220/*
221 * Destroy a linear allocation area.
222 *
223 * We do a trivial "has everything been freed?" check before unmapping the
224 * memory and freeing the LinearAllocHdr.
225 */
226void dvmLinearAllocDestroy(Object* classLoader)
227{
228#ifdef DISABLE_LINEAR_ALLOC
229 return;
230#endif
231 LinearAllocHdr* pHdr = getHeader(classLoader);
232 if (pHdr == NULL)
233 return;
234
235 checkAllFree(classLoader);
236
237 //dvmLinearAllocDump(classLoader);
238
Andy McFadden43eb5012010-02-01 16:56:53 -0800239 if (gDvm.verboseShutdown) {
Steve Block92c1f6f2011-10-20 11:55:54 +0100240 ALOGV("Unmapping linear allocator base=%p", pHdr->mapAddr);
Steve Block062bf502011-12-20 16:22:13 +0000241 ALOGD("LinearAlloc %p used %d of %d (%d%%)",
Andy McFadden43eb5012010-02-01 16:56:53 -0800242 classLoader, pHdr->curOffset, pHdr->mapLength,
243 (pHdr->curOffset * 100) / pHdr->mapLength);
244 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800245
246 if (munmap(pHdr->mapAddr, pHdr->mapLength) != 0) {
Steve Blocke8e1ddc2012-01-05 23:21:27 +0000247 ALOGW("LinearAlloc munmap(%p, %d) failed: %s",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800248 pHdr->mapAddr, pHdr->mapLength, strerror(errno));
249 }
250 free(pHdr);
251}
252
253/*
254 * Allocate "size" bytes of storage, associated with a particular class
255 * loader.
256 *
257 * It's okay for size to be zero.
258 *
259 * We always leave "curOffset" pointing at the next place where we will
260 * store the header that precedes the returned storage.
261 *
262 * This aborts the VM on failure, so it's not necessary to check for a
263 * NULL return value.
264 */
265void* dvmLinearAlloc(Object* classLoader, size_t size)
266{
267 LinearAllocHdr* pHdr = getHeader(classLoader);
268 int startOffset, nextOffset;
269 int lastGoodOff, firstWriteOff, lastWriteOff;
270
271#ifdef DISABLE_LINEAR_ALLOC
272 return calloc(1, size);
273#endif
274
Dan Bornstein60fc8062011-05-26 10:11:58 -0700275 LOGVV("--- LinearAlloc(%p, %d)", classLoader, size);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800276
277 /*
278 * What we'd like to do is just determine the new end-of-alloc size
279 * and atomic-swap the updated value in. The trouble is that, the
280 * first time we reach a new page, we need to call mprotect() to
281 * make the page available, and we don't want to call mprotect() on
282 * every allocation. The troubled situation is:
283 * - thread A allocs across a page boundary, but gets preempted
284 * before mprotect() completes
285 * - thread B allocs within the new page, and doesn't call mprotect()
286 */
287 dvmLockMutex(&pHdr->lock);
288
289 startOffset = pHdr->curOffset;
290 assert(((startOffset + HEADER_EXTRA) & (BLOCK_ALIGN-1)) == 0);
291
292 /*
293 * Compute the new offset. The old offset points at the address where
294 * we will store the hidden block header, so we advance past that,
295 * add the size of data they want, add another header's worth so we
296 * know we have room for that, and round up to BLOCK_ALIGN. That's
297 * the next location where we'll put user data. We then subtract the
298 * chunk header size off so we're back to the header pointer.
299 *
300 * Examples:
301 * old=12 size=3 new=((12+(4*2)+3+7) & ~7)-4 = 24-4 --> 20
302 * old=12 size=5 new=((12+(4*2)+5+7) & ~7)-4 = 32-4 --> 28
303 */
304 nextOffset = ((startOffset + HEADER_EXTRA*2 + size + (BLOCK_ALIGN-1))
305 & ~(BLOCK_ALIGN-1)) - HEADER_EXTRA;
Dan Bornstein60fc8062011-05-26 10:11:58 -0700306 LOGVV("--- old=%d size=%d new=%d", startOffset, size, nextOffset);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800307
308 if (nextOffset > pHdr->mapLength) {
309 /*
310 * We don't have to abort here. We could fall back on the system
311 * malloc(), and have our "free" call figure out what to do. Only
312 * works if the users of these functions actually free everything
313 * they allocate.
314 */
Steve Blockc1a4ab92012-01-06 19:16:58 +0000315 ALOGE("LinearAlloc exceeded capacity (%d), last=%d",
Andy McFadden68825972009-05-08 11:25:35 -0700316 pHdr->mapLength, (int) size);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800317 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);
Dan Bornstein60fc8062011-05-26 10:11:58 -0700327 LOGVV("--- (size now %d)", size);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800328
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
Andy McFadden96516932009-10-28 17:39:02 -0700333 * SYSTEM_PAGE_SIZE multiple (but we do it anyway).
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800334 *
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 */
Andy McFadden96516932009-10-28 17:39:02 -0700342 lastGoodOff = (startOffset-1) & ~(SYSTEM_PAGE_SIZE-1);
343 firstWriteOff = startOffset & ~(SYSTEM_PAGE_SIZE-1);
344 lastWriteOff = (nextOffset-1) & ~(SYSTEM_PAGE_SIZE-1);
Dan Bornstein60fc8062011-05-26 10:11:58 -0700345 LOGVV("--- lastGood=0x%04x firstWrite=0x%04x lastWrite=0x%04x",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800346 lastGoodOff, firstWriteOff, lastWriteOff);
347 if (lastGoodOff != lastWriteOff || ENFORCE_READ_ONLY) {
348 int cc, start, len;
349
350 start = firstWriteOff;
351 assert(start <= nextOffset);
Andy McFadden96516932009-10-28 17:39:02 -0700352 len = (lastWriteOff - firstWriteOff) + SYSTEM_PAGE_SIZE;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800353
Dan Bornstein60fc8062011-05-26 10:11:58 -0700354 LOGVV("--- calling mprotect(start=%d len=%d RW)", start, len);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800355 cc = mprotect(pHdr->mapAddr + start, len, PROT_READ | PROT_WRITE);
356 if (cc != 0) {
Steve Blockc1a4ab92012-01-06 19:16:58 +0000357 ALOGE("LinearAlloc mprotect (+%d %d) failed: %s",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800358 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
Andy McFadden96516932009-10-28 17:39:02 -0700368 start = firstWriteOff / SYSTEM_PAGE_SIZE;
369 end = lastWriteOff / SYSTEM_PAGE_SIZE;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800370
Dan Bornstein60fc8062011-05-26 10:11:58 -0700371 LOGVV("--- marking pages %d-%d RW (alloc %d at %p)",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800372 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 */
395char* 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 */
417void* dvmLinearRealloc(Object* classLoader, void* mem, size_t newSize)
418{
419#ifdef DISABLE_LINEAR_ALLOC
420 return realloc(mem, newSize);
421#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800422 /* make sure we have the right region (and mem != NULL) */
423 assert(mem != NULL);
Carl Shapirob31b3012010-05-25 18:35:37 -0700424 assert(mem >= (void*) getHeader(classLoader)->mapAddr &&
425 mem < (void*) (getHeader(classLoader)->mapAddr +
426 getHeader(classLoader)->curOffset));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800427
428 const u4* pLen = getBlockHeader(mem);
Steve Block92c1f6f2011-10-20 11:55:54 +0100429 ALOGV("--- LinearRealloc(%d) old=%d", newSize, *pLen);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800430
431 /* handle size reduction case */
432 if (*pLen >= newSize) {
433 if (ENFORCE_READ_ONLY)
434 dvmLinearSetReadWrite(classLoader, mem);
435 return mem;
436 }
437
438 void* newMem;
439
440 newMem = dvmLinearAlloc(classLoader, newSize);
441 assert(newMem != NULL);
442 memcpy(newMem, mem, *pLen);
443 dvmLinearFree(classLoader, mem);
444
445 return newMem;
446}
447
448
449/*
450 * Update the read/write status of one or more pages.
451 */
452static void updatePages(Object* classLoader, void* mem, int direction)
453{
454 LinearAllocHdr* pHdr = getHeader(classLoader);
455 dvmLockMutex(&pHdr->lock);
456
457 /* make sure we have the right region */
458 assert(mem >= (void*) pHdr->mapAddr &&
459 mem < (void*) (pHdr->mapAddr + pHdr->curOffset));
460
461 u4* pLen = getBlockHeader(mem);
462 u4 len = *pLen & LENGTHFLAG_MASK;
463 int firstPage, lastPage;
464
Andy McFadden96516932009-10-28 17:39:02 -0700465 firstPage = ((u1*)pLen - (u1*)pHdr->mapAddr) / SYSTEM_PAGE_SIZE;
466 lastPage = ((u1*)mem - (u1*)pHdr->mapAddr + (len-1)) / SYSTEM_PAGE_SIZE;
Dan Bornstein60fc8062011-05-26 10:11:58 -0700467 LOGVV("--- updating pages %d-%d (%d)", firstPage, lastPage, direction);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800468
469 int i, cc;
470
471 /*
472 * Update individual pages. We could do some sort of "lazy update" to
473 * combine mprotect calls, but that's almost certainly more trouble
474 * than it's worth.
475 */
476 for (i = firstPage; i <= lastPage; i++) {
477 if (direction < 0) {
478 /*
479 * Trying to mark read-only.
480 */
481 if (i == firstPage) {
482 if ((*pLen & LENGTHFLAG_RW) == 0) {
Steve Blocke8e1ddc2012-01-05 23:21:27 +0000483 ALOGW("Double RO on %p", mem);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800484 dvmAbort();
485 } else
486 *pLen &= ~LENGTHFLAG_RW;
487 }
488
489 if (pHdr->writeRefCount[i] == 0) {
Steve Blockc1a4ab92012-01-06 19:16:58 +0000490 ALOGE("Can't make page %d any less writable", i);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800491 dvmAbort();
492 }
493 pHdr->writeRefCount[i]--;
494 if (pHdr->writeRefCount[i] == 0) {
Dan Bornstein60fc8062011-05-26 10:11:58 -0700495 LOGVV("--- prot page %d RO", i);
Andy McFadden96516932009-10-28 17:39:02 -0700496 cc = mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE * i,
497 SYSTEM_PAGE_SIZE, PROT_READ);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800498 assert(cc == 0);
499 }
500 } else {
501 /*
502 * Trying to mark writable.
503 */
504 if (pHdr->writeRefCount[i] >= 32767) {
Steve Blockc1a4ab92012-01-06 19:16:58 +0000505 ALOGE("Can't make page %d any more writable", i);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800506 dvmAbort();
507 }
508 if (pHdr->writeRefCount[i] == 0) {
Dan Bornstein60fc8062011-05-26 10:11:58 -0700509 LOGVV("--- prot page %d RW", i);
Andy McFadden96516932009-10-28 17:39:02 -0700510 cc = mprotect(pHdr->mapAddr + SYSTEM_PAGE_SIZE * i,
511 SYSTEM_PAGE_SIZE, PROT_READ | PROT_WRITE);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800512 assert(cc == 0);
513 }
514 pHdr->writeRefCount[i]++;
515
516 if (i == firstPage) {
517 if ((*pLen & LENGTHFLAG_RW) != 0) {
Steve Blocke8e1ddc2012-01-05 23:21:27 +0000518 ALOGW("Double RW on %p", mem);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800519 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 */
536void 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 */
552void 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 */
563void 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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800572 /* make sure we have the right region */
Carl Shapirob31b3012010-05-25 18:35:37 -0700573 assert(mem >= (void*) getHeader(classLoader)->mapAddr &&
574 mem < (void*) (getHeader(classLoader)->mapAddr +
575 getHeader(classLoader)->curOffset));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800576
577 if (ENFORCE_READ_ONLY)
578 dvmLinearSetReadWrite(classLoader, mem);
579
580 u4* pLen = getBlockHeader(mem);
581 *pLen |= LENGTHFLAG_FREE;
582
583 if (ENFORCE_READ_ONLY)
584 dvmLinearSetReadOnly(classLoader, mem);
585}
586
587/*
588 * For debugging, dump the contents of a linear alloc area.
589 *
590 * We grab the lock so that the header contents and list output are
591 * consistent.
592 */
593void dvmLinearAllocDump(Object* classLoader)
594{
595#ifdef DISABLE_LINEAR_ALLOC
596 return;
597#endif
598 LinearAllocHdr* pHdr = getHeader(classLoader);
599
600 dvmLockMutex(&pHdr->lock);
601
Steve Block43084172012-01-04 20:04:51 +0000602 ALOGI("LinearAlloc classLoader=%p", classLoader);
603 ALOGI(" mapAddr=%p mapLength=%d firstOffset=%d",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800604 pHdr->mapAddr, pHdr->mapLength, pHdr->firstOffset);
Steve Block43084172012-01-04 20:04:51 +0000605 ALOGI(" curOffset=%d", pHdr->curOffset);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800606
607 int off = pHdr->firstOffset;
608 u4 rawLen, fullLen;
609
610 while (off < pHdr->curOffset) {
611 rawLen = *(u4*) (pHdr->mapAddr + off);
612 fullLen = ((HEADER_EXTRA*2 + (rawLen & LENGTHFLAG_MASK))
613 & ~(BLOCK_ALIGN-1));
614
Steve Block43084172012-01-04 20:04:51 +0000615 ALOGI(" %p (%3d): %clen=%d%s", pHdr->mapAddr + off + HEADER_EXTRA,
Andy McFadden96516932009-10-28 17:39:02 -0700616 (int) ((off + HEADER_EXTRA) / SYSTEM_PAGE_SIZE),
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800617 (rawLen & LENGTHFLAG_FREE) != 0 ? '*' : ' ',
618 rawLen & LENGTHFLAG_MASK,
619 (rawLen & LENGTHFLAG_RW) != 0 ? " [RW]" : "");
620
621 off += fullLen;
622 }
623
624 if (ENFORCE_READ_ONLY) {
Steve Block43084172012-01-04 20:04:51 +0000625 ALOGI("writeRefCount map:");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800626
Andy McFadden96516932009-10-28 17:39:02 -0700627 int numPages = (pHdr->mapLength+SYSTEM_PAGE_SIZE-1) / SYSTEM_PAGE_SIZE;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800628 int zstart = 0;
629 int i;
630
631 for (i = 0; i < numPages; i++) {
632 int count = pHdr->writeRefCount[i];
633
634 if (count != 0) {
635 if (zstart < i-1)
636 printf(" %d-%d: zero\n", zstart, i-1);
637 else if (zstart == i-1)
638 printf(" %d: zero\n", zstart);
639 zstart = i+1;
640 printf(" %d: %d\n", i, count);
641 }
642 }
643 if (zstart < i)
644 printf(" %d-%d: zero\n", zstart, i-1);
645 }
646
Steve Block062bf502011-12-20 16:22:13 +0000647 ALOGD("LinearAlloc %p using %d of %d (%d%%)",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800648 classLoader, pHdr->curOffset, pHdr->mapLength,
649 (pHdr->curOffset * 100) / pHdr->mapLength);
650
651 dvmUnlockMutex(&pHdr->lock);
652}
653
654/*
655 * Verify that all blocks are freed.
656 *
657 * This should only be done as we're shutting down, but there could be a
658 * daemon thread that's still trying to do something, so we grab the locks.
659 */
660static void checkAllFree(Object* classLoader)
661{
662#ifdef DISABLE_LINEAR_ALLOC
663 return;
664#endif
665 LinearAllocHdr* pHdr = getHeader(classLoader);
666
667 dvmLockMutex(&pHdr->lock);
668
669 int off = pHdr->firstOffset;
670 u4 rawLen, fullLen;
671
672 while (off < pHdr->curOffset) {
673 rawLen = *(u4*) (pHdr->mapAddr + off);
674 fullLen = ((HEADER_EXTRA*2 + (rawLen & LENGTHFLAG_MASK))
675 & ~(BLOCK_ALIGN-1));
676
677 if ((rawLen & LENGTHFLAG_FREE) == 0) {
Steve Blocke8e1ddc2012-01-05 23:21:27 +0000678 ALOGW("LinearAlloc %p not freed: %p len=%d", classLoader,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800679 pHdr->mapAddr + off + HEADER_EXTRA, rawLen & LENGTHFLAG_MASK);
680 }
681
682 off += fullLen;
683 }
684
685 dvmUnlockMutex(&pHdr->lock);
686}
687
Andy McFadden7605a842009-07-27 14:44:22 -0700688/*
689 * Determine if [start, start+length) is contained in the in-use area of
690 * a single LinearAlloc. The full set of linear allocators is scanned.
691 *
692 * [ Since we currently only have one region, this is pretty simple. In
693 * the future we'll need to traverse a table of class loaders. ]
694 */
Bob Leecdacef52009-07-30 18:17:37 -0700695bool dvmLinearAllocContains(const void* start, size_t length)
Andy McFadden7605a842009-07-27 14:44:22 -0700696{
697 LinearAllocHdr* pHdr = getHeader(NULL);
698
699 if (pHdr == NULL)
700 return false;
701
702 return (char*) start >= pHdr->mapAddr &&
703 ((char*)start + length) <= (pHdr->mapAddr + pHdr->curOffset);
704}