16 Commits
1.0.0 ... 1.0.3

Author SHA1 Message Date
Jason Evans
e139ab8b4f Merge branch 'dev' 2010-08-12 12:11:58 -07:00
Jason Evans
dcd15098a8 Move assert() calls up in arena_run_reg_alloc().
Move assert() calls up in arena_run_reg_alloc(), so that a corrupt
pointer will likely be caught by an assertion *before* it is
dereferenced.
2010-08-05 12:13:42 -07:00
Jason Evans
2541e1b083 Add a missing mutex unlock in malloc_init_hard().
If multiple threads race to initialize malloc, the loser(s) busy-wait
until initialization is complete.  Add a missing mutex lock so that the
loser(s) properly release the initialization mutex.  Under some
race conditions, this flaw could have caused one or more threads to
become permanently blocked.

Reported by Terrell Magee.
2010-07-22 11:35:59 -07:00
Jason Evans
b43b7750a6 Fix the libunwind version of prof_backtrace().
Fix the libunwind version of prof_backtrace() to set the backtrace depth
for all possible code paths.  This fixes the zero-length backtrace
problem when using libunwind.
2010-06-04 15:10:43 -07:00
Jason Evans
e13243eb63 Merge branch 'dev' 2010-05-11 18:24:19 -07:00
Jason Evans
7013d10a9e Avoid unnecessary isalloc() calls.
When heap profiling is enabled but deactivated, there is no need to call
isalloc(ptr) in prof_{malloc,realloc}().  Avoid these calls, so that
profiling overhead under such conditions is negligible.
2010-05-11 18:17:02 -07:00
Jason Evans
ed3d152ea0 Fix next_arena initialization.
If there is more than one arena, initialize next_arena so that the
first and second threads to allocate memory use arenas 0 and 1, rather
than both using arena 0.
2010-05-11 12:00:22 -07:00
Jordan DeLong
2206e1acc1 Add MAP_NORESERVE support.
Add MAP_NORESERVE to the chunk_mmap() case being used by
chunk_swap_enable(), if the system supports it.
2010-05-11 11:46:53 -07:00
Jason Evans
ecea0f6125 Fix junk filling of cached large objects.
Use the size argument to tcache_dalloc_large() to control the number of
bytes set to 0x5a when junk filling is enabled, rather than accessing a
non-existent arena bin.  This bug was capable of corrupting an
arbitrarily large memory region, depending on what followed the arena
data structure in memory (typically zeroed memory, another arena_t, or a
red-black tree node for a huge object).
2010-04-28 12:00:59 -07:00
Jason Evans
1af6ac42e3 Merge branch 'dev' 2010-04-14 15:07:37 -07:00
Jason Evans
5055f4516c Fix tcache crash during thread cleanup.
Properly maintain tcache_bin_t's avail pointer such that it is NULL if
no objects are cached.  This only caused problems during thread cache
destruction, since cache flushing otherwise never occurs on an empty
bin.
2010-04-14 11:27:13 -07:00
Jason Evans
38cda690dd Fix profiling regression caused by bugfix.
Properly set the context associated with each allocated object, even
when the object is not sampled.

Remove debug print code that slipped in.
2010-04-14 11:24:45 -07:00
Jason Evans
6d68ed6492 Remove autom4te.cache in distclean (not relclean). 2010-04-13 22:01:55 -07:00
Jason Evans
8d4203c72d Fix arena chunk purge/dealloc race conditions.
Fix arena_chunk_dealloc() to put the new spare in a consistent state before
dropping the arena mutex to deallocate the previous spare.

Fix arena_run_dalloc() to insert a newly dirtied chunk into the
chunks_dirty list before potentially deallocating the chunk, so that dirty
page accounting is self-consistent.
2010-04-13 21:17:18 -07:00
Jason Evans
5065156f3f Fix threads-related profiling bugs.
Initialize bt2cnt_tsd so that cleanup at thread exit actually happens.

Associate (prof_ctx_t *) with allocated objects, rather than
(prof_thr_cnt_t *).  Each thread must always operate on its own
(prof_thr_cnt_t *), and an object may outlive the thread that allocated it.
2010-04-13 21:17:11 -07:00
Jason Evans
1bb602125c Update stale JEMALLOC_FILL code.
Fix a compilation error due to stale data structure access code in
tcache_dalloc_large() for junk filling.
2010-04-13 21:17:02 -07:00
14 changed files with 221 additions and 156 deletions

View File

@@ -112,6 +112,7 @@ clean:
rm -f $(DSOS) rm -f $(DSOS)
distclean: clean distclean: clean
rm -rf @objroot@autom4te.cache
rm -f @objroot@config.log rm -f @objroot@config.log
rm -f @objroot@config.status rm -f @objroot@config.status
rm -f @objroot@cfghdrs.stamp rm -f @objroot@cfghdrs.stamp
@@ -120,7 +121,6 @@ distclean: clean
rm -f @cfgoutputs_out@ rm -f @cfgoutputs_out@
relclean: distclean relclean: distclean
rm -rf @objroot@autom4te.cache
rm -f @objroot@configure rm -f @objroot@configure
rm -f @srcroot@VERSION rm -f @srcroot@VERSION

View File

@@ -98,7 +98,7 @@ struct arena_chunk_map_s {
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
/* Profile counters, used for large object runs. */ /* Profile counters, used for large object runs. */
prof_thr_cnt_t *prof_cnt; prof_ctx_t *prof_ctx;
#endif #endif
/* /*
@@ -246,10 +246,10 @@ struct arena_bin_s {
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
/* /*
* Offset of first (prof_cnt_t *) in a run header for this bin's size * Offset of first (prof_ctx_t *) in a run header for this bin's size
* class, or 0 if (opt_prof == false). * class, or 0 if (opt_prof == false).
*/ */
uint32_t cnt0_offset; uint32_t ctx0_offset;
#endif #endif
/* Offset of first region in a run for this bin's size class. */ /* Offset of first region in a run for this bin's size class. */
@@ -438,8 +438,8 @@ size_t arena_salloc(const void *ptr);
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
void arena_prof_promoted(const void *ptr, size_t size); void arena_prof_promoted(const void *ptr, size_t size);
size_t arena_salloc_demote(const void *ptr); size_t arena_salloc_demote(const void *ptr);
prof_thr_cnt_t *arena_prof_cnt_get(const void *ptr); prof_ctx_t *arena_prof_ctx_get(const void *ptr);
void arena_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt); void arena_prof_ctx_set(const void *ptr, prof_ctx_t *ctx);
#endif #endif
void arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr, void arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr,
arena_chunk_map_t *mapelm); arena_chunk_map_t *mapelm);

View File

@@ -10,6 +10,7 @@
#ifdef JEMALLOC_H_EXTERNS #ifdef JEMALLOC_H_EXTERNS
void *chunk_alloc_mmap(size_t size); void *chunk_alloc_mmap(size_t size);
void *chunk_alloc_mmap_noreserve(size_t size);
void chunk_dealloc_mmap(void *chunk, size_t size); void chunk_dealloc_mmap(void *chunk, size_t size);
#endif /* JEMALLOC_H_EXTERNS */ #endif /* JEMALLOC_H_EXTERNS */

View File

@@ -19,7 +19,7 @@ struct extent_node_s {
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
/* Profile counters, used for huge objects. */ /* Profile counters, used for huge objects. */
prof_thr_cnt_t *prof_cnt; prof_ctx_t *prof_ctx;
#endif #endif
/* Pointer to the extent that this tree node is responsible for. */ /* Pointer to the extent that this tree node is responsible for. */

View File

@@ -25,8 +25,8 @@ void *huge_ralloc(void *ptr, size_t size, size_t oldsize);
void huge_dalloc(void *ptr); void huge_dalloc(void *ptr);
size_t huge_salloc(const void *ptr); size_t huge_salloc(const void *ptr);
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
prof_thr_cnt_t *huge_prof_cnt_get(const void *ptr); prof_ctx_t *huge_prof_ctx_get(const void *ptr);
void huge_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt); void huge_prof_ctx_set(const void *ptr, prof_ctx_t *ctx);
#endif #endif
bool huge_boot(void); bool huge_boot(void);

View File

@@ -98,6 +98,9 @@ struct prof_thr_cnt_s {
}; };
struct prof_ctx_s { struct prof_ctx_s {
/* Associated backtrace. */
prof_bt_t *bt;
/* Protects cnt_merged and sets_ql. */ /* Protects cnt_merged and sets_ql. */
malloc_mutex_t lock; malloc_mutex_t lock;
@@ -151,10 +154,10 @@ bool prof_init(prof_t *prof, bool master);
void prof_destroy(prof_t *prof); void prof_destroy(prof_t *prof);
prof_thr_cnt_t *prof_alloc_prep(size_t size); prof_thr_cnt_t *prof_alloc_prep(size_t size);
prof_thr_cnt_t *prof_cnt_get(const void *ptr); prof_ctx_t *prof_ctx_get(const void *ptr);
void prof_malloc(const void *ptr, prof_thr_cnt_t *cnt); void prof_malloc(const void *ptr, prof_thr_cnt_t *cnt);
void prof_realloc(const void *ptr, prof_thr_cnt_t *cnt, const void *old_ptr, void prof_realloc(const void *ptr, prof_thr_cnt_t *cnt, const void *old_ptr,
size_t old_size, prof_thr_cnt_t *old_cnt); size_t old_size, prof_ctx_t *old_ctx);
void prof_free(const void *ptr); void prof_free(const void *ptr);
void prof_idump(void); void prof_idump(void);
bool prof_mdump(const char *filename); bool prof_mdump(const char *filename);

View File

@@ -353,7 +353,7 @@ tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size)
#ifdef JEMALLOC_FILL #ifdef JEMALLOC_FILL
if (opt_junk) if (opt_junk)
memset(ptr, 0x5a, bin->reg_size); memset(ptr, 0x5a, size);
#endif #endif
tbin = &tcache->tbins[binind]; tbin = &tcache->tbins[binind];

View File

@@ -254,7 +254,6 @@ arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin)
run->nfree--; run->nfree--;
ret = run->avail; ret = run->avail;
if (ret != NULL) { if (ret != NULL) {
run->avail = *(void **)ret;
/* Double free can cause assertion failure.*/ /* Double free can cause assertion failure.*/
assert(ret != NULL); assert(ret != NULL);
/* Write-after free can cause assertion failure. */ /* Write-after free can cause assertion failure. */
@@ -264,6 +263,7 @@ arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin)
assert(((uintptr_t)ret - ((uintptr_t)run + assert(((uintptr_t)ret - ((uintptr_t)run +
(uintptr_t)bin->reg0_offset)) % (uintptr_t)bin->reg_size == (uintptr_t)bin->reg0_offset)) % (uintptr_t)bin->reg_size ==
0); 0);
run->avail = *(void **)ret;
return (ret); return (ret);
} }
ret = run->next; ret = run->next;
@@ -470,23 +470,6 @@ arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk)
{ {
arena_avail_tree_t *runs_avail; arena_avail_tree_t *runs_avail;
while (arena->spare != NULL) {
arena_chunk_t *spare = arena->spare;
arena->spare = NULL;
if (spare->dirtied) {
ql_remove(&chunk->arena->chunks_dirty, spare,
link_dirty);
arena->ndirty -= spare->ndirty;
}
malloc_mutex_unlock(&arena->lock);
chunk_dealloc((void *)spare, chunksize);
malloc_mutex_lock(&arena->lock);
#ifdef JEMALLOC_STATS
arena->stats.mapped -= chunksize;
#endif
}
/* /*
* Remove run from the appropriate runs_avail_* tree, so that the arena * Remove run from the appropriate runs_avail_* tree, so that the arena
* does not use it. * does not use it.
@@ -499,7 +482,23 @@ arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk)
arena_avail_tree_remove(runs_avail, arena_avail_tree_remove(runs_avail,
&chunk->map[arena_chunk_header_npages]); &chunk->map[arena_chunk_header_npages]);
arena->spare = chunk; if (arena->spare != NULL) {
arena_chunk_t *spare = arena->spare;
arena->spare = chunk;
if (spare->dirtied) {
ql_remove(&chunk->arena->chunks_dirty, spare,
link_dirty);
arena->ndirty -= spare->ndirty;
}
malloc_mutex_unlock(&arena->lock);
chunk_dealloc((void *)spare, chunksize);
malloc_mutex_lock(&arena->lock);
#ifdef JEMALLOC_STATS
arena->stats.mapped -= chunksize;
#endif
} else
arena->spare = chunk;
} }
static arena_run_t * static arena_run_t *
@@ -925,6 +924,18 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty)
/* Insert into runs_avail, now that coalescing is complete. */ /* Insert into runs_avail, now that coalescing is complete. */
arena_avail_tree_insert(runs_avail, &chunk->map[run_ind]); arena_avail_tree_insert(runs_avail, &chunk->map[run_ind]);
if (dirty) {
/*
* Insert into chunks_dirty before potentially calling
* arena_chunk_dealloc(), so that chunks_dirty and
* arena->ndirty are consistent.
*/
if (chunk->dirtied == false) {
ql_tail_insert(&arena->chunks_dirty, chunk, link_dirty);
chunk->dirtied = true;
}
}
/* /*
* Deallocate chunk if it is now completely unused. The bit * Deallocate chunk if it is now completely unused. The bit
* manipulation checks whether the first run is unallocated and extends * manipulation checks whether the first run is unallocated and extends
@@ -935,19 +946,14 @@ arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty)
arena_chunk_dealloc(arena, chunk); arena_chunk_dealloc(arena, chunk);
/* /*
* It is okay to do dirty page processing even if the chunk was * It is okay to do dirty page processing here even if the chunk was
* deallocated above, since in that case it is the spare. Waiting * deallocated above, since in that case it is the spare. Waiting
* until after possible chunk deallocation to do dirty processing * until after possible chunk deallocation to do dirty processing
* allows for an old spare to be fully deallocated, thus decreasing the * allows for an old spare to be fully deallocated, thus decreasing the
* chances of spuriously crossing the dirty page purging threshold. * chances of spuriously crossing the dirty page purging threshold.
*/ */
if (dirty) { if (dirty)
if (chunk->dirtied == false) {
ql_tail_insert(&arena->chunks_dirty, chunk, link_dirty);
chunk->dirtied = true;
}
arena_maybe_purge(arena); arena_maybe_purge(arena);
}
} }
static void static void
@@ -1198,7 +1204,7 @@ arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size)
uint32_t try_nregs, good_nregs; uint32_t try_nregs, good_nregs;
uint32_t try_hdr_size, good_hdr_size; uint32_t try_hdr_size, good_hdr_size;
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
uint32_t try_cnt0_offset, good_cnt0_offset; uint32_t try_ctx0_offset, good_ctx0_offset;
#endif #endif
uint32_t try_reg0_offset, good_reg0_offset; uint32_t try_reg0_offset, good_reg0_offset;
@@ -1225,11 +1231,11 @@ arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size)
if (opt_prof && prof_promote == false) { if (opt_prof && prof_promote == false) {
/* Pad to a quantum boundary. */ /* Pad to a quantum boundary. */
try_hdr_size = QUANTUM_CEILING(try_hdr_size); try_hdr_size = QUANTUM_CEILING(try_hdr_size);
try_cnt0_offset = try_hdr_size; try_ctx0_offset = try_hdr_size;
/* Add space for one (prof_thr_cnt_t *) per region. */ /* Add space for one (prof_ctx_t *) per region. */
try_hdr_size += try_nregs * sizeof(prof_thr_cnt_t *); try_hdr_size += try_nregs * sizeof(prof_ctx_t *);
} else } else
try_cnt0_offset = 0; try_ctx0_offset = 0;
#endif #endif
try_reg0_offset = try_run_size - (try_nregs * bin->reg_size); try_reg0_offset = try_run_size - (try_nregs * bin->reg_size);
} while (try_hdr_size > try_reg0_offset); } while (try_hdr_size > try_reg0_offset);
@@ -1243,7 +1249,7 @@ arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size)
good_nregs = try_nregs; good_nregs = try_nregs;
good_hdr_size = try_hdr_size; good_hdr_size = try_hdr_size;
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
good_cnt0_offset = try_cnt0_offset; good_ctx0_offset = try_ctx0_offset;
#endif #endif
good_reg0_offset = try_reg0_offset; good_reg0_offset = try_reg0_offset;
@@ -1258,13 +1264,12 @@ arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size)
if (opt_prof && prof_promote == false) { if (opt_prof && prof_promote == false) {
/* Pad to a quantum boundary. */ /* Pad to a quantum boundary. */
try_hdr_size = QUANTUM_CEILING(try_hdr_size); try_hdr_size = QUANTUM_CEILING(try_hdr_size);
try_cnt0_offset = try_hdr_size; try_ctx0_offset = try_hdr_size;
/* /*
* Add space for one (prof_thr_cnt_t *) per * Add space for one (prof_ctx_t *) per region.
* region.
*/ */
try_hdr_size += try_nregs * try_hdr_size += try_nregs *
sizeof(prof_thr_cnt_t *); sizeof(prof_ctx_t *);
} }
#endif #endif
try_reg0_offset = try_run_size - (try_nregs * try_reg0_offset = try_run_size - (try_nregs *
@@ -1282,7 +1287,7 @@ arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size)
bin->run_size = good_run_size; bin->run_size = good_run_size;
bin->nregs = good_nregs; bin->nregs = good_nregs;
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
bin->cnt0_offset = good_cnt0_offset; bin->ctx0_offset = good_ctx0_offset;
#endif #endif
bin->reg0_offset = good_reg0_offset; bin->reg0_offset = good_reg0_offset;
@@ -1639,10 +1644,10 @@ arena_run_regind(arena_run_t *run, arena_bin_t *bin, const void *ptr,
return (regind); return (regind);
} }
prof_thr_cnt_t * prof_ctx_t *
arena_prof_cnt_get(const void *ptr) arena_prof_ctx_get(const void *ptr)
{ {
prof_thr_cnt_t *ret; prof_ctx_t *ret;
arena_chunk_t *chunk; arena_chunk_t *chunk;
size_t pageind, mapbits; size_t pageind, mapbits;
@@ -1655,7 +1660,7 @@ arena_prof_cnt_get(const void *ptr)
assert((mapbits & CHUNK_MAP_ALLOCATED) != 0); assert((mapbits & CHUNK_MAP_ALLOCATED) != 0);
if ((mapbits & CHUNK_MAP_LARGE) == 0) { if ((mapbits & CHUNK_MAP_LARGE) == 0) {
if (prof_promote) if (prof_promote)
ret = (prof_thr_cnt_t *)(uintptr_t)1U; ret = (prof_ctx_t *)(uintptr_t)1U;
else { else {
arena_run_t *run = (arena_run_t *)((uintptr_t)chunk + arena_run_t *run = (arena_run_t *)((uintptr_t)chunk +
(uintptr_t)((pageind - (mapbits >> PAGE_SHIFT)) << (uintptr_t)((pageind - (mapbits >> PAGE_SHIFT)) <<
@@ -1665,18 +1670,18 @@ arena_prof_cnt_get(const void *ptr)
assert(run->magic == ARENA_RUN_MAGIC); assert(run->magic == ARENA_RUN_MAGIC);
regind = arena_run_regind(run, bin, ptr, bin->reg_size); regind = arena_run_regind(run, bin, ptr, bin->reg_size);
ret = *(prof_thr_cnt_t **)((uintptr_t)run + ret = *(prof_ctx_t **)((uintptr_t)run +
bin->cnt0_offset + (regind * bin->ctx0_offset + (regind *
sizeof(prof_thr_cnt_t *))); sizeof(prof_ctx_t *)));
} }
} else } else
ret = chunk->map[pageind].prof_cnt; ret = chunk->map[pageind].prof_ctx;
return (ret); return (ret);
} }
void void
arena_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt) arena_prof_ctx_set(const void *ptr, prof_ctx_t *ctx)
{ {
arena_chunk_t *chunk; arena_chunk_t *chunk;
size_t pageind, mapbits; size_t pageind, mapbits;
@@ -1699,12 +1704,12 @@ arena_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt)
assert(run->magic == ARENA_RUN_MAGIC); assert(run->magic == ARENA_RUN_MAGIC);
regind = arena_run_regind(run, bin, ptr, bin->reg_size); regind = arena_run_regind(run, bin, ptr, bin->reg_size);
*((prof_thr_cnt_t **)((uintptr_t)run + bin->cnt0_offset *((prof_ctx_t **)((uintptr_t)run + bin->ctx0_offset
+ (regind * sizeof(prof_thr_cnt_t *)))) = cnt; + (regind * sizeof(prof_ctx_t *)))) = ctx;
} else } else
assert((uintptr_t)cnt == (uintptr_t)1U); assert((uintptr_t)ctx == (uintptr_t)1U);
} else } else
chunk->map[pageind].prof_cnt = cnt; chunk->map[pageind].prof_ctx = ctx;
} }
#endif #endif

View File

@@ -23,14 +23,15 @@ static
/******************************************************************************/ /******************************************************************************/
/* Function prototypes for non-inline static functions. */ /* Function prototypes for non-inline static functions. */
static void *pages_map(void *addr, size_t size); static void *pages_map(void *addr, size_t size, bool noreserve);
static void pages_unmap(void *addr, size_t size); static void pages_unmap(void *addr, size_t size);
static void *chunk_alloc_mmap_slow(size_t size, bool unaligned); static void *chunk_alloc_mmap_slow(size_t size, bool unaligned, bool noreserve);
static void *chunk_alloc_mmap_internal(size_t size, bool noreserve);
/******************************************************************************/ /******************************************************************************/
static void * static void *
pages_map(void *addr, size_t size) pages_map(void *addr, size_t size, bool noreserve)
{ {
void *ret; void *ret;
@@ -38,8 +39,12 @@ pages_map(void *addr, size_t size)
* We don't use MAP_FIXED here, because it can cause the *replacement* * We don't use MAP_FIXED here, because it can cause the *replacement*
* of existing mappings, and we only want to create new mappings. * of existing mappings, and we only want to create new mappings.
*/ */
ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, int flags = MAP_PRIVATE | MAP_ANON;
-1, 0); #ifdef MAP_NORESERVE
if (noreserve)
flags |= MAP_NORESERVE;
#endif
ret = mmap(addr, size, PROT_READ | PROT_WRITE, flags, -1, 0);
assert(ret != NULL); assert(ret != NULL);
if (ret == MAP_FAILED) if (ret == MAP_FAILED)
@@ -83,7 +88,7 @@ pages_unmap(void *addr, size_t size)
} }
static void * static void *
chunk_alloc_mmap_slow(size_t size, bool unaligned) chunk_alloc_mmap_slow(size_t size, bool unaligned, bool noreserve)
{ {
void *ret; void *ret;
size_t offset; size_t offset;
@@ -92,7 +97,7 @@ chunk_alloc_mmap_slow(size_t size, bool unaligned)
if (size + chunksize <= size) if (size + chunksize <= size)
return (NULL); return (NULL);
ret = pages_map(NULL, size + chunksize); ret = pages_map(NULL, size + chunksize, noreserve);
if (ret == NULL) if (ret == NULL)
return (NULL); return (NULL);
@@ -128,8 +133,8 @@ chunk_alloc_mmap_slow(size_t size, bool unaligned)
return (ret); return (ret);
} }
void * static void *
chunk_alloc_mmap(size_t size) chunk_alloc_mmap_internal(size_t size, bool noreserve)
{ {
void *ret; void *ret;
@@ -164,7 +169,7 @@ chunk_alloc_mmap(size_t size)
if (mmap_unaligned == false) { if (mmap_unaligned == false) {
size_t offset; size_t offset;
ret = pages_map(NULL, size); ret = pages_map(NULL, size, noreserve);
if (ret == NULL) if (ret == NULL)
return (NULL); return (NULL);
@@ -173,13 +178,13 @@ chunk_alloc_mmap(size_t size)
mmap_unaligned = true; mmap_unaligned = true;
/* Try to extend chunk boundary. */ /* Try to extend chunk boundary. */
if (pages_map((void *)((uintptr_t)ret + size), if (pages_map((void *)((uintptr_t)ret + size),
chunksize - offset) == NULL) { chunksize - offset, noreserve) == NULL) {
/* /*
* Extension failed. Clean up, then revert to * Extension failed. Clean up, then revert to
* the reliable-but-expensive method. * the reliable-but-expensive method.
*/ */
pages_unmap(ret, size); pages_unmap(ret, size);
ret = chunk_alloc_mmap_slow(size, true); ret = chunk_alloc_mmap_slow(size, true, noreserve);
} else { } else {
/* Clean up unneeded leading space. */ /* Clean up unneeded leading space. */
pages_unmap(ret, chunksize - offset); pages_unmap(ret, chunksize - offset);
@@ -188,11 +193,23 @@ chunk_alloc_mmap(size_t size)
} }
} }
} else } else
ret = chunk_alloc_mmap_slow(size, false); ret = chunk_alloc_mmap_slow(size, false, noreserve);
return (ret); return (ret);
} }
void *
chunk_alloc_mmap(size_t size)
{
return chunk_alloc_mmap_internal(size, false);
}
void *
chunk_alloc_mmap_noreserve(size_t size)
{
return chunk_alloc_mmap_internal(size, true);
}
void void
chunk_dealloc_mmap(void *chunk, size_t size) chunk_dealloc_mmap(void *chunk, size_t size)
{ {

View File

@@ -283,7 +283,7 @@ chunk_swap_enable(const int *fds, unsigned nfds, bool prezeroed)
* Allocate a chunk-aligned region of anonymous memory, which will * Allocate a chunk-aligned region of anonymous memory, which will
* be the final location for the memory-mapped files. * be the final location for the memory-mapped files.
*/ */
vaddr = chunk_alloc_mmap(cumsize); vaddr = chunk_alloc_mmap_noreserve(cumsize);
if (vaddr == NULL) { if (vaddr == NULL) {
ret = true; ret = true;
goto RETURN; goto RETURN;

View File

@@ -241,10 +241,10 @@ huge_salloc(const void *ptr)
} }
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
prof_thr_cnt_t * prof_ctx_t *
huge_prof_cnt_get(const void *ptr) huge_prof_ctx_get(const void *ptr)
{ {
prof_thr_cnt_t *ret; prof_ctx_t *ret;
extent_node_t *node, key; extent_node_t *node, key;
malloc_mutex_lock(&huge_mtx); malloc_mutex_lock(&huge_mtx);
@@ -254,7 +254,7 @@ huge_prof_cnt_get(const void *ptr)
node = extent_tree_ad_search(&huge, &key); node = extent_tree_ad_search(&huge, &key);
assert(node != NULL); assert(node != NULL);
ret = node->prof_cnt; ret = node->prof_ctx;
malloc_mutex_unlock(&huge_mtx); malloc_mutex_unlock(&huge_mtx);
@@ -262,7 +262,7 @@ huge_prof_cnt_get(const void *ptr)
} }
void void
huge_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt) huge_prof_ctx_set(const void *ptr, prof_ctx_t *ctx)
{ {
extent_node_t *node, key; extent_node_t *node, key;
@@ -273,7 +273,7 @@ huge_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt)
node = extent_tree_ad_search(&huge, &key); node = extent_tree_ad_search(&huge, &key);
assert(node != NULL); assert(node != NULL);
node->prof_cnt = cnt; node->prof_ctx = ctx;
malloc_mutex_unlock(&huge_mtx); malloc_mutex_unlock(&huge_mtx);
} }

View File

@@ -324,6 +324,7 @@ malloc_init_hard(void)
CPU_SPINWAIT; CPU_SPINWAIT;
malloc_mutex_lock(&init_lock); malloc_mutex_lock(&init_lock);
} while (malloc_initialized == false); } while (malloc_initialized == false);
malloc_mutex_unlock(&init_lock);
return (false); return (false);
} }
@@ -775,7 +776,7 @@ MALLOC_OUT:
#endif #endif
#ifndef NO_TLS #ifndef NO_TLS
next_arena = 0; next_arena = (narenas > 0) ? 1 : 0;
#endif #endif
/* Allocate and initialize arenas. */ /* Allocate and initialize arenas. */
@@ -1060,7 +1061,8 @@ JEMALLOC_P(realloc)(void *ptr, size_t size)
void *ret; void *ret;
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
size_t old_size; size_t old_size;
prof_thr_cnt_t *cnt, *old_cnt; prof_thr_cnt_t *cnt;
prof_ctx_t *old_ctx;
#endif #endif
if (size == 0) { if (size == 0) {
@@ -1074,7 +1076,7 @@ JEMALLOC_P(realloc)(void *ptr, size_t size)
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
if (opt_prof) { if (opt_prof) {
old_size = isalloc(ptr); old_size = isalloc(ptr);
old_cnt = prof_cnt_get(ptr); old_ctx = prof_ctx_get(ptr);
cnt = NULL; cnt = NULL;
} }
#endif #endif
@@ -1083,7 +1085,7 @@ JEMALLOC_P(realloc)(void *ptr, size_t size)
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
else if (opt_prof) { else if (opt_prof) {
old_size = 0; old_size = 0;
old_cnt = NULL; old_ctx = NULL;
cnt = NULL; cnt = NULL;
} }
#endif #endif
@@ -1100,7 +1102,7 @@ JEMALLOC_P(realloc)(void *ptr, size_t size)
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
if (opt_prof) { if (opt_prof) {
old_size = isalloc(ptr); old_size = isalloc(ptr);
old_cnt = prof_cnt_get(ptr); old_ctx = prof_ctx_get(ptr);
if ((cnt = prof_alloc_prep(size)) == NULL) { if ((cnt = prof_alloc_prep(size)) == NULL) {
ret = NULL; ret = NULL;
goto OOM; goto OOM;
@@ -1133,7 +1135,7 @@ OOM:
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
if (opt_prof) { if (opt_prof) {
old_size = 0; old_size = 0;
old_cnt = NULL; old_ctx = NULL;
} }
#endif #endif
if (malloc_init()) { if (malloc_init()) {
@@ -1181,7 +1183,7 @@ RETURN:
#endif #endif
#ifdef JEMALLOC_PROF #ifdef JEMALLOC_PROF
if (opt_prof) if (opt_prof)
prof_realloc(ret, cnt, ptr, old_size, old_cnt); prof_realloc(ret, cnt, ptr, old_size, old_ctx);
#endif #endif
return (ret); return (ret);
} }

View File

@@ -48,7 +48,7 @@ static malloc_mutex_t bt2ctx_mtx;
static __thread ckh_t *bt2cnt_tls JEMALLOC_ATTR(tls_model("initial-exec")); static __thread ckh_t *bt2cnt_tls JEMALLOC_ATTR(tls_model("initial-exec"));
/* /*
* Same contents as b2cnt, but initialized such that the TSD destructor is * Same contents as b2cnt_tls, but initialized such that the TSD destructor is
* called when a thread exits, so that bt2cnt_tls contents can be merged, * called when a thread exits, so that bt2cnt_tls contents can be merged,
* unlinked, and deallocated. * unlinked, and deallocated.
*/ */
@@ -100,7 +100,7 @@ static _Unwind_Reason_Code prof_unwind_callback(
#endif #endif
static void prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max); static void prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max);
static prof_thr_cnt_t *prof_lookup(prof_bt_t *bt); static prof_thr_cnt_t *prof_lookup(prof_bt_t *bt);
static void prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt); static void prof_ctx_set(const void *ptr, prof_ctx_t *ctx);
static bool prof_flush(bool propagate_err); static bool prof_flush(bool propagate_err);
static bool prof_write(const char *s, bool propagate_err); static bool prof_write(const char *s, bool propagate_err);
static void prof_ctx_merge(prof_ctx_t *ctx, prof_cnt_t *cnt_all, static void prof_ctx_merge(prof_ctx_t *ctx, prof_cnt_t *cnt_all,
@@ -239,16 +239,15 @@ prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max)
} }
/* /*
* Iterate over stack frames until there are no more. Heap-allocate * Iterate over stack frames until there are no more, or until no space
* and iteratively grow a larger bt if necessary. * remains in bt.
*/ */
for (i = 0; i < max; i++) { for (i = 0; i < max; i++) {
unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t *)&bt->vec[i]); unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t *)&bt->vec[i]);
bt->len++;
err = unw_step(&cursor); err = unw_step(&cursor);
if (err <= 0) { if (err <= 0)
bt->len = i;
break; break;
}
} }
} }
#else #else
@@ -450,6 +449,7 @@ prof_lookup(prof_bt_t *bt)
return (NULL); return (NULL);
} }
bt2cnt_tls = bt2cnt; bt2cnt_tls = bt2cnt;
pthread_setspecific(bt2cnt_tsd, bt2cnt);
} }
if (ckh_search(bt2cnt, bt, NULL, (void **)&ret)) { if (ckh_search(bt2cnt, bt, NULL, (void **)&ret)) {
@@ -475,6 +475,7 @@ prof_lookup(prof_bt_t *bt)
idalloc(ctx); idalloc(ctx);
return (NULL); return (NULL);
} }
ctx->bt = btkey;
if (malloc_mutex_init(&ctx->lock)) { if (malloc_mutex_init(&ctx->lock)) {
prof_leave(); prof_leave();
idalloc(btkey); idalloc(btkey);
@@ -580,10 +581,10 @@ prof_alloc_prep(size_t size)
return (ret); return (ret);
} }
prof_thr_cnt_t * prof_ctx_t *
prof_cnt_get(const void *ptr) prof_ctx_get(const void *ptr)
{ {
prof_thr_cnt_t *ret; prof_ctx_t *ret;
arena_chunk_t *chunk; arena_chunk_t *chunk;
assert(ptr != NULL); assert(ptr != NULL);
@@ -593,15 +594,15 @@ prof_cnt_get(const void *ptr)
/* Region. */ /* Region. */
assert(chunk->arena->magic == ARENA_MAGIC); assert(chunk->arena->magic == ARENA_MAGIC);
ret = arena_prof_cnt_get(ptr); ret = arena_prof_ctx_get(ptr);
} else } else
ret = huge_prof_cnt_get(ptr); ret = huge_prof_ctx_get(ptr);
return (ret); return (ret);
} }
static void static void
prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt) prof_ctx_set(const void *ptr, prof_ctx_t *ctx)
{ {
arena_chunk_t *chunk; arena_chunk_t *chunk;
@@ -612,22 +613,17 @@ prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt)
/* Region. */ /* Region. */
assert(chunk->arena->magic == ARENA_MAGIC); assert(chunk->arena->magic == ARENA_MAGIC);
arena_prof_cnt_set(ptr, cnt); arena_prof_ctx_set(ptr, ctx);
} else } else
huge_prof_cnt_set(ptr, cnt); huge_prof_ctx_set(ptr, ctx);
} }
static inline void static inline void
prof_sample_accum_update(size_t size) prof_sample_accum_update(size_t size)
{ {
if (opt_lg_prof_sample == 0) { /* Sampling logic is unnecessary if the interval is 1. */
/* assert(opt_lg_prof_sample != 0);
* Don't bother with sampling logic, since sampling interval is
* 1.
*/
return;
}
/* Take care to avoid integer overflow. */ /* Take care to avoid integer overflow. */
if (size >= prof_sample_threshold - prof_sample_accum) { if (size >= prof_sample_threshold - prof_sample_accum) {
@@ -645,14 +641,19 @@ prof_sample_accum_update(size_t size)
void void
prof_malloc(const void *ptr, prof_thr_cnt_t *cnt) prof_malloc(const void *ptr, prof_thr_cnt_t *cnt)
{ {
size_t size = isalloc(ptr); size_t size;
assert(ptr != NULL); assert(ptr != NULL);
prof_cnt_set(ptr, cnt); if (opt_lg_prof_sample != 0) {
prof_sample_accum_update(size); size = isalloc(ptr);
prof_sample_accum_update(size);
} else if ((uintptr_t)cnt > (uintptr_t)1U)
size = isalloc(ptr);
if ((uintptr_t)cnt > (uintptr_t)1U) { if ((uintptr_t)cnt > (uintptr_t)1U) {
prof_ctx_set(ptr, cnt->ctx);
cnt->epoch++; cnt->epoch++;
/*********/ /*********/
mb_write(); mb_write();
@@ -668,30 +669,56 @@ prof_malloc(const void *ptr, prof_thr_cnt_t *cnt)
/*********/ /*********/
mb_write(); mb_write();
/*********/ /*********/
} } else
prof_ctx_set(ptr, (prof_ctx_t *)(uintptr_t)1U);
} }
void void
prof_realloc(const void *ptr, prof_thr_cnt_t *cnt, const void *old_ptr, prof_realloc(const void *ptr, prof_thr_cnt_t *cnt, const void *old_ptr,
size_t old_size, prof_thr_cnt_t *old_cnt) size_t old_size, prof_ctx_t *old_ctx)
{ {
size_t size = isalloc(ptr); size_t size;
prof_thr_cnt_t *told_cnt;
assert(ptr != NULL || (uintptr_t)cnt <= (uintptr_t)1U);
if (ptr != NULL) { if (ptr != NULL) {
prof_cnt_set(ptr, cnt); if (opt_lg_prof_sample != 0) {
prof_sample_accum_update(size); size = isalloc(ptr);
prof_sample_accum_update(size);
} else if ((uintptr_t)cnt > (uintptr_t)1U)
size = isalloc(ptr);
} }
if ((uintptr_t)old_cnt > (uintptr_t)1U) if ((uintptr_t)old_ctx > (uintptr_t)1U) {
old_cnt->epoch++; told_cnt = prof_lookup(old_ctx->bt);
if ((uintptr_t)cnt > (uintptr_t)1U) if (told_cnt == NULL) {
/*
* It's too late to propagate OOM for this realloc(),
* so operate directly on old_cnt->ctx->cnt_merged.
*/
malloc_mutex_lock(&old_ctx->lock);
old_ctx->cnt_merged.curobjs--;
old_ctx->cnt_merged.curbytes -= old_size;
malloc_mutex_unlock(&old_ctx->lock);
told_cnt = (prof_thr_cnt_t *)(uintptr_t)1U;
}
} else
told_cnt = (prof_thr_cnt_t *)(uintptr_t)1U;
if ((uintptr_t)told_cnt > (uintptr_t)1U)
told_cnt->epoch++;
if ((uintptr_t)cnt > (uintptr_t)1U) {
prof_ctx_set(ptr, cnt->ctx);
cnt->epoch++; cnt->epoch++;
} else
prof_ctx_set(ptr, (prof_ctx_t *)(uintptr_t)1U);
/*********/ /*********/
mb_write(); mb_write();
/*********/ /*********/
if ((uintptr_t)old_cnt > (uintptr_t)1U) { if ((uintptr_t)told_cnt > (uintptr_t)1U) {
old_cnt->cnts.curobjs--; told_cnt->cnts.curobjs--;
old_cnt->cnts.curbytes -= old_size; told_cnt->cnts.curbytes -= old_size;
} }
if ((uintptr_t)cnt > (uintptr_t)1U) { if ((uintptr_t)cnt > (uintptr_t)1U) {
cnt->cnts.curobjs++; cnt->cnts.curobjs++;
@@ -702,8 +729,8 @@ prof_realloc(const void *ptr, prof_thr_cnt_t *cnt, const void *old_ptr,
/*********/ /*********/
mb_write(); mb_write();
/*********/ /*********/
if ((uintptr_t)old_cnt > (uintptr_t)1U) if ((uintptr_t)told_cnt > (uintptr_t)1U)
old_cnt->epoch++; told_cnt->epoch++;
if ((uintptr_t)cnt > (uintptr_t)1U) if ((uintptr_t)cnt > (uintptr_t)1U)
cnt->epoch++; cnt->epoch++;
/*********/ /*********/
@@ -713,24 +740,36 @@ prof_realloc(const void *ptr, prof_thr_cnt_t *cnt, const void *old_ptr,
void void
prof_free(const void *ptr) prof_free(const void *ptr)
{ {
prof_thr_cnt_t *cnt = prof_cnt_get(ptr); prof_ctx_t *ctx = prof_ctx_get(ptr);
if ((uintptr_t)cnt > (uintptr_t)1) { if ((uintptr_t)ctx > (uintptr_t)1) {
size_t size = isalloc(ptr); size_t size = isalloc(ptr);
prof_thr_cnt_t *tcnt = prof_lookup(ctx->bt);
cnt->epoch++; if (tcnt != NULL) {
/*********/ tcnt->epoch++;
mb_write(); /*********/
/*********/ mb_write();
cnt->cnts.curobjs--; /*********/
cnt->cnts.curbytes -= size; tcnt->cnts.curobjs--;
/*********/ tcnt->cnts.curbytes -= size;
mb_write(); /*********/
/*********/ mb_write();
cnt->epoch++; /*********/
/*********/ tcnt->epoch++;
mb_write(); /*********/
/*********/ mb_write();
/*********/
} else {
/*
* OOM during free() cannot be propagated, so operate
* directly on cnt->ctx->cnt_merged.
*/
malloc_mutex_lock(&ctx->lock);
ctx->cnt_merged.curobjs--;
ctx->cnt_merged.curbytes -= size;
malloc_mutex_unlock(&ctx->lock);
}
} }
} }

View File

@@ -55,12 +55,14 @@ tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem
{ {
void *flush, *deferred, *ptr; void *flush, *deferred, *ptr;
unsigned i, nflush, ndeferred; unsigned i, nflush, ndeferred;
bool first_pass;
assert(binind < nbins); assert(binind < nbins);
assert(rem <= tbin->ncached); assert(rem <= tbin->ncached);
assert(tbin->ncached > 0 || tbin->avail == NULL);
for (flush = tbin->avail, nflush = tbin->ncached - rem; flush != NULL; for (flush = tbin->avail, nflush = tbin->ncached - rem, first_pass =
flush = deferred, nflush = ndeferred) { true; flush != NULL; flush = deferred, nflush = ndeferred) {
/* Lock the arena bin associated with the first object. */ /* Lock the arena bin associated with the first object. */
arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(flush); arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(flush);
arena_t *arena = chunk->arena; arena_t *arena = chunk->arena;
@@ -110,12 +112,9 @@ tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem
} }
malloc_mutex_unlock(&bin->lock); malloc_mutex_unlock(&bin->lock);
if (flush != NULL) { if (first_pass) {
/*
* This was the first pass, and rem cached objects
* remain.
*/
tbin->avail = flush; tbin->avail = flush;
first_pass = false;
} }
} }
@@ -133,12 +132,14 @@ tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem
{ {
void *flush, *deferred, *ptr; void *flush, *deferred, *ptr;
unsigned i, nflush, ndeferred; unsigned i, nflush, ndeferred;
bool first_pass;
assert(binind < nhbins); assert(binind < nhbins);
assert(rem <= tbin->ncached); assert(rem <= tbin->ncached);
assert(tbin->ncached > 0 || tbin->avail == NULL);
for (flush = tbin->avail, nflush = tbin->ncached - rem; flush != NULL; for (flush = tbin->avail, nflush = tbin->ncached - rem, first_pass =
flush = deferred, nflush = ndeferred) { true; flush != NULL; flush = deferred, nflush = ndeferred) {
/* Lock the arena associated with the first object. */ /* Lock the arena associated with the first object. */
arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(flush); arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(flush);
arena_t *arena = chunk->arena; arena_t *arena = chunk->arena;
@@ -183,12 +184,9 @@ tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem
} }
malloc_mutex_unlock(&arena->lock); malloc_mutex_unlock(&arena->lock);
if (flush != NULL) { if (first_pass) {
/*
* This was the first pass, and rem cached objects
* remain.
*/
tbin->avail = flush; tbin->avail = flush;
first_pass = false;
} }
} }