- Emulator can now test the αcτµαlly pδrταblε εxεcµταblε bootloader - Whipped up a webserver named redbean. It services 150k requests per second on a single core. Bundling assets inside zip enables extremely fast serving for two reasons. The first is that zip central directory lookups go faster than stat() system calls. The second is that both zip and gzip content-encoding use DEFLATE, therefore, compressed responses can be served via the sendfile() system call which does an in-kernel copy directly from the zip executable structure. Also note that red bean zip executables can be deployed easily to all platforms, since these native executables work on Linux, Mac, BSD, and Windows. - Address sanitizer now works very well
248 lines
7.7 KiB
C
248 lines
7.7 KiB
C
#include "third_party/dlmalloc/dlmalloc.h"
|
|
|
|
/* Check properties of any chunk, whether free, inuse, mmapped etc */
|
|
forceinline void do_check_any_chunk(mstate m, mchunkptr p) {
|
|
assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
|
|
assert(ok_address(m, p));
|
|
}
|
|
|
|
/* Check properties of top chunk */
|
|
void do_check_top_chunk(mstate m, mchunkptr p) {
|
|
msegmentptr sp = segment_holding(m, (char*)p);
|
|
size_t sz = p->head & ~INUSE_BITS; /* third-lowest bit can be set! */
|
|
assert(sp != 0);
|
|
assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
|
|
assert(ok_address(m, p));
|
|
assert(sz == m->topsize);
|
|
assert(sz > 0);
|
|
assert(sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE);
|
|
assert(pinuse(p));
|
|
assert(!pinuse(chunk_plus_offset(p, sz)));
|
|
}
|
|
|
|
/* Check properties of (inuse) mmapped chunks */
|
|
void do_check_mmapped_chunk(mstate m, mchunkptr p) {
|
|
size_t sz = chunksize(p);
|
|
size_t len = (sz + (p->prev_foot) + MMAP_FOOT_PAD);
|
|
assert(is_mmapped(p));
|
|
assert(use_mmap(m));
|
|
assert((is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD));
|
|
assert(ok_address(m, p));
|
|
assert(!is_small(sz));
|
|
assert((len & (g_mparams.page_size - SIZE_T_ONE)) == 0);
|
|
assert(chunk_plus_offset(p, sz)->head == FENCEPOST_HEAD);
|
|
assert(chunk_plus_offset(p, sz + SIZE_T_SIZE)->head == 0);
|
|
}
|
|
|
|
/* Check properties of inuse chunks */
|
|
void do_check_inuse_chunk(mstate m, mchunkptr p) {
|
|
do_check_any_chunk(m, p);
|
|
assert(is_inuse(p));
|
|
assert(next_pinuse(p));
|
|
/* If not pinuse and not mmapped, previous chunk has OK offset */
|
|
assert(is_mmapped(p) || pinuse(p) || next_chunk(prev_chunk(p)) == p);
|
|
if (is_mmapped(p)) do_check_mmapped_chunk(m, p);
|
|
}
|
|
|
|
/* Check properties of free chunks */
|
|
void do_check_free_chunk(mstate m, mchunkptr p) {
|
|
size_t sz = chunksize(p);
|
|
mchunkptr next = chunk_plus_offset(p, sz);
|
|
do_check_any_chunk(m, p);
|
|
assert(!is_inuse(p));
|
|
assert(!next_pinuse(p));
|
|
assert(!is_mmapped(p));
|
|
if (p != m->dv && p != m->top) {
|
|
if (sz >= MIN_CHUNK_SIZE) {
|
|
assert((sz & CHUNK_ALIGN_MASK) == 0);
|
|
assert(is_aligned(chunk2mem(p)));
|
|
assert(next->prev_foot == sz);
|
|
assert(pinuse(p));
|
|
assert(next == m->top || is_inuse(next));
|
|
assert(p->fd->bk == p);
|
|
assert(p->bk->fd == p);
|
|
} else /* markers are always of size SIZE_T_SIZE */
|
|
assert(sz == SIZE_T_SIZE);
|
|
}
|
|
}
|
|
|
|
/* Check properties of malloced chunks at the point they are malloced */
|
|
void do_check_malloced_chunk(mstate m, void* mem, size_t s) {
|
|
if (mem != 0) {
|
|
mchunkptr p = mem2chunk(mem);
|
|
size_t sz = p->head & ~INUSE_BITS;
|
|
do_check_inuse_chunk(m, p);
|
|
assert((sz & CHUNK_ALIGN_MASK) == 0);
|
|
assert(sz >= MIN_CHUNK_SIZE);
|
|
assert(sz >= s);
|
|
/* unless mmapped, size is less than MIN_CHUNK_SIZE more than request */
|
|
assert(is_mmapped(p) || sz < (s + MIN_CHUNK_SIZE));
|
|
}
|
|
}
|
|
|
|
/* Check a tree and its subtrees. */
|
|
static void do_check_tree(mstate m, tchunkptr t) {
|
|
tchunkptr head = 0;
|
|
tchunkptr u = t;
|
|
bindex_t tindex = t->index;
|
|
size_t tsize = chunksize(t);
|
|
bindex_t idx;
|
|
compute_tree_index(tsize, idx);
|
|
assert(tindex == idx);
|
|
assert(tsize >= MIN_LARGE_SIZE);
|
|
assert(tsize >= minsize_for_tree_index(idx));
|
|
assert((idx == NTREEBINS - 1) || (tsize < minsize_for_tree_index((idx + 1))));
|
|
|
|
do { /* traverse through chain of same-sized nodes */
|
|
do_check_any_chunk(m, ((mchunkptr)u));
|
|
assert(u->index == tindex);
|
|
assert(chunksize(u) == tsize);
|
|
assert(!is_inuse(u));
|
|
assert(!next_pinuse(u));
|
|
assert(u->fd->bk == u);
|
|
assert(u->bk->fd == u);
|
|
if (u->parent == 0) {
|
|
assert(u->child[0] == 0);
|
|
assert(u->child[1] == 0);
|
|
} else {
|
|
assert(head == 0); /* only one node on chain has parent */
|
|
head = u;
|
|
assert(u->parent != u);
|
|
assert(u->parent->child[0] == u || u->parent->child[1] == u ||
|
|
*((tbinptr*)(u->parent)) == u);
|
|
if (u->child[0] != 0) {
|
|
assert(u->child[0]->parent == u);
|
|
assert(u->child[0] != u);
|
|
do_check_tree(m, u->child[0]);
|
|
}
|
|
if (u->child[1] != 0) {
|
|
assert(u->child[1]->parent == u);
|
|
assert(u->child[1] != u);
|
|
do_check_tree(m, u->child[1]);
|
|
}
|
|
if (u->child[0] != 0 && u->child[1] != 0) {
|
|
assert(chunksize(u->child[0]) < chunksize(u->child[1]));
|
|
}
|
|
}
|
|
u = u->fd;
|
|
} while (u != t);
|
|
assert(head != 0);
|
|
}
|
|
|
|
/* Check all the chunks in a treebin. */
|
|
static void do_check_treebin(mstate m, bindex_t i) {
|
|
tbinptr* tb = treebin_at(m, i);
|
|
tchunkptr t = *tb;
|
|
int empty = (m->treemap & (1U << i)) == 0;
|
|
if (t == 0) assert(empty);
|
|
if (!empty) do_check_tree(m, t);
|
|
}
|
|
|
|
/* Check all the chunks in a smallbin. */
|
|
static void do_check_smallbin(mstate m, bindex_t i) {
|
|
sbinptr b = smallbin_at(m, i);
|
|
mchunkptr p = b->bk;
|
|
unsigned int empty = (m->smallmap & (1U << i)) == 0;
|
|
if (p == b) assert(empty);
|
|
if (!empty) {
|
|
for (; p != b; p = p->bk) {
|
|
size_t size = chunksize(p);
|
|
mchunkptr q;
|
|
/* each chunk claims to be free */
|
|
do_check_free_chunk(m, p);
|
|
/* chunk belongs in bin */
|
|
assert(small_index(size) == i);
|
|
assert(p->bk == b || chunksize(p->bk) == chunksize(p));
|
|
/* chunk is followed by an inuse chunk */
|
|
q = next_chunk(p);
|
|
if (q->head != FENCEPOST_HEAD) do_check_inuse_chunk(m, q);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Find x in a bin. Used in other check functions. */
|
|
static int bin_find(mstate m, mchunkptr x) {
|
|
size_t size = chunksize(x);
|
|
if (is_small(size)) {
|
|
bindex_t sidx = small_index(size);
|
|
sbinptr b = smallbin_at(m, sidx);
|
|
if (smallmap_is_marked(m, sidx)) {
|
|
mchunkptr p = b;
|
|
do {
|
|
if (p == x) return 1;
|
|
} while ((p = p->fd) != b);
|
|
}
|
|
} else {
|
|
bindex_t tidx;
|
|
compute_tree_index(size, tidx);
|
|
if (treemap_is_marked(m, tidx)) {
|
|
tchunkptr t = *treebin_at(m, tidx);
|
|
size_t sizebits = size << leftshift_for_tree_index(tidx);
|
|
while (t != 0 && chunksize(t) != size) {
|
|
t = t->child[(sizebits >> (SIZE_T_BITSIZE - SIZE_T_ONE)) & 1];
|
|
sizebits <<= 1;
|
|
}
|
|
if (t != 0) {
|
|
tchunkptr u = t;
|
|
do {
|
|
if (u == (tchunkptr)x) return 1;
|
|
} while ((u = u->fd) != t);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Traverse each chunk and check it; return total */
|
|
static size_t traverse_and_check(mstate m) {
|
|
size_t sum = 0;
|
|
if (is_initialized(m)) {
|
|
msegmentptr s = &m->seg;
|
|
sum += m->topsize + TOP_FOOT_SIZE;
|
|
while (s != 0) {
|
|
mchunkptr q = align_as_chunk(s->base);
|
|
mchunkptr lastq = 0;
|
|
assert(pinuse(q));
|
|
while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) {
|
|
sum += chunksize(q);
|
|
if (is_inuse(q)) {
|
|
assert(!bin_find(m, q));
|
|
do_check_inuse_chunk(m, q);
|
|
} else {
|
|
assert(q == m->dv || bin_find(m, q));
|
|
assert(lastq == 0 || is_inuse(lastq)); /* Not 2 consecutive free */
|
|
do_check_free_chunk(m, q);
|
|
}
|
|
lastq = q;
|
|
q = next_chunk(q);
|
|
}
|
|
s = s->next;
|
|
}
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
/* Check all properties of MallocState. */
|
|
void do_check_malloc_state(mstate m) {
|
|
bindex_t i;
|
|
size_t total;
|
|
/* check bins */
|
|
for (i = 0; i < NSMALLBINS; ++i) do_check_smallbin(m, i);
|
|
for (i = 0; i < NTREEBINS; ++i) do_check_treebin(m, i);
|
|
if (m->dvsize != 0) { /* check dv chunk */
|
|
do_check_any_chunk(m, m->dv);
|
|
assert(m->dvsize == chunksize(m->dv));
|
|
assert(m->dvsize >= MIN_CHUNK_SIZE);
|
|
assert(bin_find(m, m->dv) == 0);
|
|
}
|
|
if (m->top != 0) { /* check top chunk */
|
|
do_check_top_chunk(m, m->top);
|
|
/*assert(m->topsize == chunksize(m->top)); redundant */
|
|
assert(m->topsize > 0);
|
|
assert(bin_find(m, m->top) == 0);
|
|
}
|
|
total = traverse_and_check(m);
|
|
assert(total <= m->footprint);
|
|
assert(m->footprint <= m->max_footprint);
|
|
}
|