platform_system_core/libcutils/mspace.c
Carl Shapiro b99a099cad Add a new method to export the extent of the break, akin to return
value of sbrk(0) in UNIX.  In terms of dlmalloc and our proprietary
contiguous mspace class, this is the highest address returned by its
morecore method.
2010-02-16 14:37:43 -08:00

286 lines
7.7 KiB
C

/* Copyright 2006 The Android Open Source Project */
/* A wrapper file for dlmalloc.c that compiles in the
* mspace_*() functions, which provide an interface for
* creating multiple heaps.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <cutils/ashmem.h>
/* It's a pain getting the mallinfo stuff to work
* with Linux, OSX, and klibc, so just turn it off
* for now.
* TODO: make mallinfo work
*/
#define NO_MALLINFO 1
/* Allow setting the maximum heap footprint.
*/
#define USE_MAX_ALLOWED_FOOTPRINT 1
/* Don't try to trim memory.
* TODO: support this.
*/
#define MORECORE_CANNOT_TRIM 1
/* Use mmap()d anonymous memory to guarantee
* that an mspace is contiguous.
*
* create_mspace() won't work right if this is
* defined, so hide the definition of it and
* break any users at build time.
*/
#define USE_CONTIGUOUS_MSPACES 1
#if USE_CONTIGUOUS_MSPACES
/* This combination of settings forces sys_alloc()
* to always use MORECORE(). It won't expect the
* results to be contiguous, but we'll guarantee
* that they are.
*/
#define HAVE_MMAP 0
#define HAVE_MORECORE 1
#define MORECORE_CONTIGUOUS 0
/* m is always the appropriate local when MORECORE() is called. */
#define MORECORE(S) contiguous_mspace_morecore(m, S)
#define create_mspace HIDDEN_create_mspace_HIDDEN
#define destroy_mspace HIDDEN_destroy_mspace_HIDDEN
typedef struct malloc_state *mstate0;
static void *contiguous_mspace_morecore(mstate0 m, ssize_t nb);
#endif
#define MSPACES 1
#define ONLY_MSPACES 1
#include "../../../bionic/libc/bionic/dlmalloc.c"
#ifndef PAGESIZE
#define PAGESIZE mparams.page_size
#endif
#define ALIGN_UP(p, alignment) \
(((uintptr_t)(p) + (alignment)-1) & ~((alignment)-1))
/* A direct copy of dlmalloc_usable_size(),
* which isn't compiled in when ONLY_MSPACES is set.
* The mspace parameter isn't actually necessary,
* but we include it to be consistent with the
* rest of the mspace_*() functions.
*/
size_t mspace_usable_size(mspace _unused, const void* mem) {
if (mem != 0) {
const mchunkptr p = mem2chunk(mem);
if (cinuse(p))
return chunksize(p) - overhead_for(p);
}
return 0;
}
#if USE_CONTIGUOUS_MSPACES
#include <sys/mman.h>
#include <limits.h>
#define CONTIG_STATE_MAGIC 0xf00dd00d
struct mspace_contig_state {
unsigned int magic;
char *brk;
char *top;
mspace m;
};
static void *contiguous_mspace_morecore(mstate m, ssize_t nb) {
struct mspace_contig_state *cs;
char *oldbrk;
const unsigned int pagesize = PAGESIZE;
cs = (struct mspace_contig_state *)((uintptr_t)m & ~(pagesize-1));
assert(cs->magic == CONTIG_STATE_MAGIC);
assert(cs->m == m);
assert(nb >= 0); //xxx deal with the trim case
oldbrk = cs->brk;
if (nb > 0) {
/* Break to the first page boundary that satisfies the request.
*/
char *newbrk = (char *)ALIGN_UP(oldbrk + nb, pagesize);
if (newbrk > cs->top)
return CMFAIL;
/* Update the protection on the underlying memory.
* Pages we've given to dlmalloc are read/write, and
* pages we haven't are not accessable (read or write
* will cause a seg fault).
*/
if (mprotect(cs, newbrk - (char *)cs, PROT_READ | PROT_WRITE) < 0)
return CMFAIL;
if (newbrk != cs->top) {
if (mprotect(newbrk, cs->top - newbrk, PROT_NONE) < 0)
return CMFAIL;
}
cs->brk = newbrk;
/* Make sure that dlmalloc will merge this block with the
* initial block that was passed to create_mspace_with_base().
* We don't care about extern vs. non-extern, so just clear it.
*/
m->seg.sflags &= ~EXTERN_BIT;
}
return oldbrk;
}
mspace create_contiguous_mspace_with_base(size_t starting_capacity,
size_t max_capacity, int locked, void *base) {
struct mspace_contig_state *cs;
unsigned int pagesize;
mstate m;
init_mparams();
pagesize = PAGESIZE;
assert(starting_capacity <= max_capacity);
assert(((uintptr_t)base & (pagesize-1)) == 0);
assert(((uintptr_t)max_capacity & (pagesize-1)) == 0);
starting_capacity = (size_t)ALIGN_UP(starting_capacity, pagesize);
/* Make the first page read/write. dlmalloc needs to use that page.
*/
if (mprotect(base, starting_capacity, PROT_READ | PROT_WRITE) < 0) {
goto error;
}
/* Create the mspace, pointing to the memory given.
*/
m = create_mspace_with_base((char *)base + sizeof(*cs), starting_capacity,
locked);
if (m == (mspace)0) {
goto error;
}
/* Make sure that m is in the same page as base.
*/
assert(((uintptr_t)m & (uintptr_t)~(pagesize-1)) == (uintptr_t)base);
/* Use some space for the information that our MORECORE needs.
*/
cs = (struct mspace_contig_state *)base;
/* Find out exactly how much of the memory the mspace
* is using.
*/
cs->brk = m->seg.base + m->seg.size;
cs->top = (char *)base + max_capacity;
assert((char *)base <= cs->brk);
assert(cs->brk <= cs->top);
/* Prevent access to the memory we haven't handed out yet.
*/
if (cs->brk != cs->top) {
/* mprotect() requires page-aligned arguments, but it's possible
* for cs->brk not to be page-aligned at this point.
*/
char *prot_brk = (char *)ALIGN_UP(cs->brk, pagesize);
if ((mprotect(base, prot_brk - (char *)base, PROT_READ | PROT_WRITE) < 0) ||
(mprotect(prot_brk, cs->top - prot_brk, PROT_NONE) < 0)) {
goto error;
}
}
cs->m = m;
cs->magic = CONTIG_STATE_MAGIC;
return (mspace)m;
error:
return (mspace)0;
}
mspace create_contiguous_mspace_with_name(size_t starting_capacity,
size_t max_capacity, int locked, char const *name) {
int fd, ret;
char buf[ASHMEM_NAME_LEN] = "mspace";
void *base;
unsigned int pagesize;
mstate m;
if (starting_capacity > max_capacity)
return (mspace)0;
init_mparams();
pagesize = PAGESIZE;
/* Create the anonymous memory that will back the mspace.
* This reserves all of the virtual address space we could
* ever need. Physical pages will be mapped as the memory
* is touched.
*
* Align max_capacity to a whole page.
*/
max_capacity = (size_t)ALIGN_UP(max_capacity, pagesize);
if (name)
snprintf(buf, sizeof(buf), "mspace/%s", name);
fd = ashmem_create_region(buf, max_capacity);
if (fd < 0)
return (mspace)0;
base = mmap(NULL, max_capacity, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
close(fd);
if (base == MAP_FAILED)
return (mspace)0;
/* Make sure that base is at the beginning of a page.
*/
assert(((uintptr_t)base & (pagesize-1)) == 0);
m = create_contiguous_mspace_with_base(starting_capacity, max_capacity,
locked, base);
if (m == 0) {
munmap(base, max_capacity);
}
return m;
}
mspace create_contiguous_mspace(size_t starting_capacity,
size_t max_capacity, int locked) {
return create_contiguous_mspace_with_name(starting_capacity,
max_capacity, locked, NULL);
}
size_t destroy_contiguous_mspace(mspace msp) {
mstate ms = (mstate)msp;
if (ok_magic(ms)) {
struct mspace_contig_state *cs;
size_t length;
const unsigned int pagesize = PAGESIZE;
cs = (struct mspace_contig_state *)((uintptr_t)ms & ~(pagesize-1));
assert(cs->magic == CONTIG_STATE_MAGIC);
assert(cs->m == ms);
length = cs->top - (char *)cs;
if (munmap((char *)cs, length) != 0)
return length;
}
else {
USAGE_ERROR_ACTION(ms, ms);
}
return 0;
}
void *contiguous_mspace_sbrk0(mspace msp) {
struct mspace_contig_state *cs;
mstate ms;
const unsigned int pagesize = PAGESIZE;
ms = (mstate)msp;
cs = (struct mspace_contig_state *)((uintptr_t)ms & ~(pagesize-1));
assert(cs->magic == CONTIG_STATE_MAGIC);
assert(cs->m == ms);
return cs->brk;
}
#endif