Hooks: hook the realloc pathways that move/expand.
This commit is contained in:
parent
67270040a5
commit
cb0707c0fc
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "jemalloc/internal/bin.h"
|
#include "jemalloc/internal/bin.h"
|
||||||
#include "jemalloc/internal/extent_dss.h"
|
#include "jemalloc/internal/extent_dss.h"
|
||||||
|
#include "jemalloc/internal/hook.h"
|
||||||
#include "jemalloc/internal/pages.h"
|
#include "jemalloc/internal/pages.h"
|
||||||
#include "jemalloc/internal/size_classes.h"
|
#include "jemalloc/internal/size_classes.h"
|
||||||
#include "jemalloc/internal/stats.h"
|
#include "jemalloc/internal/stats.h"
|
||||||
@ -65,7 +66,8 @@ void arena_dalloc_small(tsdn_t *tsdn, void *ptr);
|
|||||||
bool arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
bool arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
||||||
size_t extra, bool zero);
|
size_t extra, bool zero);
|
||||||
void *arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
|
void *arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
|
||||||
size_t size, size_t alignment, bool zero, tcache_t *tcache);
|
size_t size, size_t alignment, bool zero, tcache_t *tcache,
|
||||||
|
hook_ralloc_args_t *hook_args);
|
||||||
dss_prec_t arena_dss_prec_get(arena_t *arena);
|
dss_prec_t arena_dss_prec_get(arena_t *arena);
|
||||||
bool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);
|
bool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);
|
||||||
ssize_t arena_dirty_decay_ms_default_get(void);
|
ssize_t arena_dirty_decay_ms_default_get(void);
|
||||||
|
@ -105,6 +105,37 @@ struct hooks_s {
|
|||||||
hook_expand expand_hook;
|
hook_expand expand_hook;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Begin implementation details; everything above this point might one day live
|
||||||
|
* in a public API. Everything below this point never will.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The realloc pathways haven't gotten any refactoring love in a while, and it's
|
||||||
|
* fairly difficult to pass information from the entry point to the hooks. We
|
||||||
|
* put the informaiton the hooks will need into a struct to encapsulate
|
||||||
|
* everything.
|
||||||
|
*
|
||||||
|
* Much of these pathways are force-inlined, so that the compiler can avoid
|
||||||
|
* materializing this struct until we hit an extern arena function. For fairly
|
||||||
|
* goofy reasons, *many* of the realloc paths hit an extern arena function.
|
||||||
|
* These paths are cold enough that it doesn't matter; eventually, we should
|
||||||
|
* rewrite the realloc code to make the expand-in-place and the
|
||||||
|
* free-then-realloc paths more orthogonal, at which point we don't need to
|
||||||
|
* spread the hook logic all over the place.
|
||||||
|
*/
|
||||||
|
typedef struct hook_ralloc_args_s hook_ralloc_args_t;
|
||||||
|
struct hook_ralloc_args_s {
|
||||||
|
/* I.e. as opposed to rallocx. */
|
||||||
|
bool is_realloc;
|
||||||
|
/*
|
||||||
|
* The expand hook takes 4 arguments, even if only 3 are actually used;
|
||||||
|
* we add an extra one in case the user decides to memcpy without
|
||||||
|
* looking too closely at the hooked function.
|
||||||
|
*/
|
||||||
|
uintptr_t args[4];
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns an opaque handle to be used when removing the hook. NULL means that
|
* Returns an opaque handle to be used when removing the hook. NULL means that
|
||||||
* we couldn't install the hook.
|
* we couldn't install the hook.
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef JEMALLOC_INTERNAL_INLINES_C_H
|
#ifndef JEMALLOC_INTERNAL_INLINES_C_H
|
||||||
#define JEMALLOC_INTERNAL_INLINES_C_H
|
#define JEMALLOC_INTERNAL_INLINES_C_H
|
||||||
|
|
||||||
|
#include "jemalloc/internal/hook.h"
|
||||||
#include "jemalloc/internal/jemalloc_internal_types.h"
|
#include "jemalloc/internal/jemalloc_internal_types.h"
|
||||||
#include "jemalloc/internal/sz.h"
|
#include "jemalloc/internal/sz.h"
|
||||||
#include "jemalloc/internal/witness.h"
|
#include "jemalloc/internal/witness.h"
|
||||||
@ -133,31 +134,20 @@ isdalloct(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
|
|||||||
|
|
||||||
JEMALLOC_ALWAYS_INLINE void *
|
JEMALLOC_ALWAYS_INLINE void *
|
||||||
iralloct_realign(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
iralloct_realign(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
||||||
size_t extra, size_t alignment, bool zero, tcache_t *tcache,
|
size_t alignment, bool zero, tcache_t *tcache, arena_t *arena,
|
||||||
arena_t *arena) {
|
hook_ralloc_args_t *hook_args) {
|
||||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||||
WITNESS_RANK_CORE, 0);
|
WITNESS_RANK_CORE, 0);
|
||||||
void *p;
|
void *p;
|
||||||
size_t usize, copysize;
|
size_t usize, copysize;
|
||||||
|
|
||||||
usize = sz_sa2u(size + extra, alignment);
|
usize = sz_sa2u(size, alignment);
|
||||||
if (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {
|
if (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
p = ipalloct(tsdn, usize, alignment, zero, tcache, arena);
|
p = ipalloct(tsdn, usize, alignment, zero, tcache, arena);
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
if (extra == 0) {
|
return NULL;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* Try again, without extra this time. */
|
|
||||||
usize = sz_sa2u(size, alignment);
|
|
||||||
if (unlikely(usize == 0 || usize > LARGE_MAXCLASS)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
p = ipalloct(tsdn, usize, alignment, zero, tcache, arena);
|
|
||||||
if (p == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Copy at most size bytes (not size+extra), since the caller has no
|
* Copy at most size bytes (not size+extra), since the caller has no
|
||||||
@ -165,13 +155,26 @@ iralloct_realign(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
|
|||||||
*/
|
*/
|
||||||
copysize = (size < oldsize) ? size : oldsize;
|
copysize = (size < oldsize) ? size : oldsize;
|
||||||
memcpy(p, ptr, copysize);
|
memcpy(p, ptr, copysize);
|
||||||
|
hook_invoke_alloc(hook_args->is_realloc
|
||||||
|
? hook_alloc_realloc : hook_alloc_rallocx, p, (uintptr_t)p,
|
||||||
|
hook_args->args);
|
||||||
|
hook_invoke_dalloc(hook_args->is_realloc
|
||||||
|
? hook_dalloc_realloc : hook_dalloc_rallocx, ptr, hook_args->args);
|
||||||
isdalloct(tsdn, ptr, oldsize, tcache, NULL, true);
|
isdalloct(tsdn, ptr, oldsize, tcache, NULL, true);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* is_realloc threads through the knowledge of whether or not this call comes
|
||||||
|
* from je_realloc (as opposed to je_rallocx); this ensures that we pass the
|
||||||
|
* correct entry point into any hooks.
|
||||||
|
* Note that these functions are all force-inlined, so no actual bool gets
|
||||||
|
* passed-around anywhere.
|
||||||
|
*/
|
||||||
JEMALLOC_ALWAYS_INLINE void *
|
JEMALLOC_ALWAYS_INLINE void *
|
||||||
iralloct(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t alignment,
|
iralloct(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t alignment,
|
||||||
bool zero, tcache_t *tcache, arena_t *arena) {
|
bool zero, tcache_t *tcache, arena_t *arena, hook_ralloc_args_t *hook_args)
|
||||||
|
{
|
||||||
assert(ptr != NULL);
|
assert(ptr != NULL);
|
||||||
assert(size != 0);
|
assert(size != 0);
|
||||||
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
||||||
@ -183,19 +186,19 @@ iralloct(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t alignment,
|
|||||||
* Existing object alignment is inadequate; allocate new space
|
* Existing object alignment is inadequate; allocate new space
|
||||||
* and copy.
|
* and copy.
|
||||||
*/
|
*/
|
||||||
return iralloct_realign(tsdn, ptr, oldsize, size, 0, alignment,
|
return iralloct_realign(tsdn, ptr, oldsize, size, alignment,
|
||||||
zero, tcache, arena);
|
zero, tcache, arena, hook_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return arena_ralloc(tsdn, arena, ptr, oldsize, size, alignment, zero,
|
return arena_ralloc(tsdn, arena, ptr, oldsize, size, alignment, zero,
|
||||||
tcache);
|
tcache, hook_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
JEMALLOC_ALWAYS_INLINE void *
|
JEMALLOC_ALWAYS_INLINE void *
|
||||||
iralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment,
|
iralloc(tsd_t *tsd, void *ptr, size_t oldsize, size_t size, size_t alignment,
|
||||||
bool zero) {
|
bool zero, hook_ralloc_args_t *hook_args) {
|
||||||
return iralloct(tsd_tsdn(tsd), ptr, oldsize, size, alignment, zero,
|
return iralloct(tsd_tsdn(tsd), ptr, oldsize, size, alignment, zero,
|
||||||
tcache_get(tsd), NULL);
|
tcache_get(tsd), NULL, hook_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
JEMALLOC_ALWAYS_INLINE bool
|
JEMALLOC_ALWAYS_INLINE bool
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
#ifndef JEMALLOC_INTERNAL_LARGE_EXTERNS_H
|
#ifndef JEMALLOC_INTERNAL_LARGE_EXTERNS_H
|
||||||
#define JEMALLOC_INTERNAL_LARGE_EXTERNS_H
|
#define JEMALLOC_INTERNAL_LARGE_EXTERNS_H
|
||||||
|
|
||||||
|
#include "jemalloc/internal/hook.h"
|
||||||
|
|
||||||
void *large_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero);
|
void *large_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero);
|
||||||
void *large_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
|
void *large_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
|
||||||
bool zero);
|
bool zero);
|
||||||
bool large_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,
|
bool large_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,
|
||||||
size_t usize_max, bool zero);
|
size_t usize_max, bool zero);
|
||||||
void *large_ralloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent, size_t usize,
|
void *large_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t usize,
|
||||||
size_t alignment, bool zero, tcache_t *tcache);
|
size_t alignment, bool zero, tcache_t *tcache,
|
||||||
|
hook_ralloc_args_t *hook_args);
|
||||||
|
|
||||||
typedef void (large_dalloc_junk_t)(void *, size_t);
|
typedef void (large_dalloc_junk_t)(void *, size_t);
|
||||||
extern large_dalloc_junk_t *JET_MUTABLE large_dalloc_junk;
|
extern large_dalloc_junk_t *JET_MUTABLE large_dalloc_junk;
|
||||||
|
18
src/arena.c
18
src/arena.c
@ -1630,7 +1630,8 @@ arena_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
|||||||
|
|
||||||
void *
|
void *
|
||||||
arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
|
arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
|
||||||
size_t size, size_t alignment, bool zero, tcache_t *tcache) {
|
size_t size, size_t alignment, bool zero, tcache_t *tcache,
|
||||||
|
hook_ralloc_args_t *hook_args) {
|
||||||
size_t usize = sz_s2u(size);
|
size_t usize = sz_s2u(size);
|
||||||
if (unlikely(usize == 0 || size > LARGE_MAXCLASS)) {
|
if (unlikely(usize == 0 || size > LARGE_MAXCLASS)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1639,13 +1640,17 @@ arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
|
|||||||
if (likely(usize <= SMALL_MAXCLASS)) {
|
if (likely(usize <= SMALL_MAXCLASS)) {
|
||||||
/* Try to avoid moving the allocation. */
|
/* Try to avoid moving the allocation. */
|
||||||
if (!arena_ralloc_no_move(tsdn, ptr, oldsize, usize, 0, zero)) {
|
if (!arena_ralloc_no_move(tsdn, ptr, oldsize, usize, 0, zero)) {
|
||||||
|
hook_invoke_expand(hook_args->is_realloc
|
||||||
|
? hook_expand_realloc : hook_expand_rallocx,
|
||||||
|
ptr, oldsize, usize, (uintptr_t)ptr,
|
||||||
|
hook_args->args);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldsize >= LARGE_MINCLASS && usize >= LARGE_MINCLASS) {
|
if (oldsize >= LARGE_MINCLASS && usize >= LARGE_MINCLASS) {
|
||||||
return large_ralloc(tsdn, arena, iealloc(tsdn, ptr), usize,
|
return large_ralloc(tsdn, arena, ptr, usize,
|
||||||
alignment, zero, tcache);
|
alignment, zero, tcache, hook_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1658,11 +1663,16 @@ arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hook_invoke_alloc(hook_args->is_realloc
|
||||||
|
? hook_alloc_realloc : hook_alloc_rallocx, ret, (uintptr_t)ret,
|
||||||
|
hook_args->args);
|
||||||
|
hook_invoke_dalloc(hook_args->is_realloc
|
||||||
|
? hook_dalloc_realloc : hook_dalloc_rallocx, ptr, hook_args->args);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Junk/zero-filling were already done by
|
* Junk/zero-filling were already done by
|
||||||
* ipalloc()/arena_malloc().
|
* ipalloc()/arena_malloc().
|
||||||
*/
|
*/
|
||||||
|
|
||||||
size_t copysize = (usize < oldsize) ? usize : oldsize;
|
size_t copysize = (usize < oldsize) ? usize : oldsize;
|
||||||
memcpy(ret, ptr, copysize);
|
memcpy(ret, ptr, copysize);
|
||||||
isdalloct(tsdn, ptr, oldsize, tcache, NULL, true);
|
isdalloct(tsdn, ptr, oldsize, tcache, NULL, true);
|
||||||
|
@ -2169,20 +2169,22 @@ je_calloc(size_t num, size_t size) {
|
|||||||
|
|
||||||
static void *
|
static void *
|
||||||
irealloc_prof_sample(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,
|
irealloc_prof_sample(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,
|
||||||
prof_tctx_t *tctx) {
|
prof_tctx_t *tctx, hook_ralloc_args_t *hook_args) {
|
||||||
void *p;
|
void *p;
|
||||||
|
|
||||||
if (tctx == NULL) {
|
if (tctx == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (usize <= SMALL_MAXCLASS) {
|
if (usize <= SMALL_MAXCLASS) {
|
||||||
p = iralloc(tsd, old_ptr, old_usize, LARGE_MINCLASS, 0, false);
|
p = iralloc(tsd, old_ptr, old_usize, LARGE_MINCLASS, 0, false,
|
||||||
|
hook_args);
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
arena_prof_promote(tsd_tsdn(tsd), p, usize);
|
arena_prof_promote(tsd_tsdn(tsd), p, usize);
|
||||||
} else {
|
} else {
|
||||||
p = iralloc(tsd, old_ptr, old_usize, usize, 0, false);
|
p = iralloc(tsd, old_ptr, old_usize, usize, 0, false,
|
||||||
|
hook_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
@ -2190,7 +2192,7 @@ irealloc_prof_sample(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,
|
|||||||
|
|
||||||
JEMALLOC_ALWAYS_INLINE void *
|
JEMALLOC_ALWAYS_INLINE void *
|
||||||
irealloc_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,
|
irealloc_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,
|
||||||
alloc_ctx_t *alloc_ctx) {
|
alloc_ctx_t *alloc_ctx, hook_ralloc_args_t *hook_args) {
|
||||||
void *p;
|
void *p;
|
||||||
bool prof_active;
|
bool prof_active;
|
||||||
prof_tctx_t *old_tctx, *tctx;
|
prof_tctx_t *old_tctx, *tctx;
|
||||||
@ -2199,9 +2201,11 @@ irealloc_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,
|
|||||||
old_tctx = prof_tctx_get(tsd_tsdn(tsd), old_ptr, alloc_ctx);
|
old_tctx = prof_tctx_get(tsd_tsdn(tsd), old_ptr, alloc_ctx);
|
||||||
tctx = prof_alloc_prep(tsd, usize, prof_active, true);
|
tctx = prof_alloc_prep(tsd, usize, prof_active, true);
|
||||||
if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
|
if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
|
||||||
p = irealloc_prof_sample(tsd, old_ptr, old_usize, usize, tctx);
|
p = irealloc_prof_sample(tsd, old_ptr, old_usize, usize, tctx,
|
||||||
|
hook_args);
|
||||||
} else {
|
} else {
|
||||||
p = iralloc(tsd, old_ptr, old_usize, usize, 0, false);
|
p = iralloc(tsd, old_ptr, old_usize, usize, 0, false,
|
||||||
|
hook_args);
|
||||||
}
|
}
|
||||||
if (unlikely(p == NULL)) {
|
if (unlikely(p == NULL)) {
|
||||||
prof_alloc_rollback(tsd, tctx, true);
|
prof_alloc_rollback(tsd, tctx, true);
|
||||||
@ -2349,6 +2353,10 @@ je_realloc(void *ptr, size_t arg_size) {
|
|||||||
|
|
||||||
check_entry_exit_locking(tsd_tsdn(tsd));
|
check_entry_exit_locking(tsd_tsdn(tsd));
|
||||||
|
|
||||||
|
|
||||||
|
hook_ralloc_args_t hook_args = {true, {(uintptr_t)ptr,
|
||||||
|
(uintptr_t)arg_size, 0, 0}};
|
||||||
|
|
||||||
alloc_ctx_t alloc_ctx;
|
alloc_ctx_t alloc_ctx;
|
||||||
rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);
|
rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);
|
||||||
rtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,
|
rtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,
|
||||||
@ -2362,13 +2370,14 @@ je_realloc(void *ptr, size_t arg_size) {
|
|||||||
ret = NULL;
|
ret = NULL;
|
||||||
} else {
|
} else {
|
||||||
ret = irealloc_prof(tsd, ptr, old_usize, usize,
|
ret = irealloc_prof(tsd, ptr, old_usize, usize,
|
||||||
&alloc_ctx);
|
&alloc_ctx, &hook_args);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (config_stats) {
|
if (config_stats) {
|
||||||
usize = sz_s2u(size);
|
usize = sz_s2u(size);
|
||||||
}
|
}
|
||||||
ret = iralloc(tsd, ptr, old_usize, size, 0, false);
|
ret = iralloc(tsd, ptr, old_usize, size, 0, false,
|
||||||
|
&hook_args);
|
||||||
}
|
}
|
||||||
tsdn = tsd_tsdn(tsd);
|
tsdn = tsd_tsdn(tsd);
|
||||||
} else {
|
} else {
|
||||||
@ -2664,7 +2673,7 @@ je_mallocx(size_t size, int flags) {
|
|||||||
static void *
|
static void *
|
||||||
irallocx_prof_sample(tsdn_t *tsdn, void *old_ptr, size_t old_usize,
|
irallocx_prof_sample(tsdn_t *tsdn, void *old_ptr, size_t old_usize,
|
||||||
size_t usize, size_t alignment, bool zero, tcache_t *tcache, arena_t *arena,
|
size_t usize, size_t alignment, bool zero, tcache_t *tcache, arena_t *arena,
|
||||||
prof_tctx_t *tctx) {
|
prof_tctx_t *tctx, hook_ralloc_args_t *hook_args) {
|
||||||
void *p;
|
void *p;
|
||||||
|
|
||||||
if (tctx == NULL) {
|
if (tctx == NULL) {
|
||||||
@ -2672,14 +2681,14 @@ irallocx_prof_sample(tsdn_t *tsdn, void *old_ptr, size_t old_usize,
|
|||||||
}
|
}
|
||||||
if (usize <= SMALL_MAXCLASS) {
|
if (usize <= SMALL_MAXCLASS) {
|
||||||
p = iralloct(tsdn, old_ptr, old_usize, LARGE_MINCLASS,
|
p = iralloct(tsdn, old_ptr, old_usize, LARGE_MINCLASS,
|
||||||
alignment, zero, tcache, arena);
|
alignment, zero, tcache, arena, hook_args);
|
||||||
if (p == NULL) {
|
if (p == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
arena_prof_promote(tsdn, p, usize);
|
arena_prof_promote(tsdn, p, usize);
|
||||||
} else {
|
} else {
|
||||||
p = iralloct(tsdn, old_ptr, old_usize, usize, alignment, zero,
|
p = iralloct(tsdn, old_ptr, old_usize, usize, alignment, zero,
|
||||||
tcache, arena);
|
tcache, arena, hook_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
@ -2688,7 +2697,7 @@ irallocx_prof_sample(tsdn_t *tsdn, void *old_ptr, size_t old_usize,
|
|||||||
JEMALLOC_ALWAYS_INLINE void *
|
JEMALLOC_ALWAYS_INLINE void *
|
||||||
irallocx_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t size,
|
irallocx_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t size,
|
||||||
size_t alignment, size_t *usize, bool zero, tcache_t *tcache,
|
size_t alignment, size_t *usize, bool zero, tcache_t *tcache,
|
||||||
arena_t *arena, alloc_ctx_t *alloc_ctx) {
|
arena_t *arena, alloc_ctx_t *alloc_ctx, hook_ralloc_args_t *hook_args) {
|
||||||
void *p;
|
void *p;
|
||||||
bool prof_active;
|
bool prof_active;
|
||||||
prof_tctx_t *old_tctx, *tctx;
|
prof_tctx_t *old_tctx, *tctx;
|
||||||
@ -2698,10 +2707,10 @@ irallocx_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t size,
|
|||||||
tctx = prof_alloc_prep(tsd, *usize, prof_active, false);
|
tctx = prof_alloc_prep(tsd, *usize, prof_active, false);
|
||||||
if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
|
if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
|
||||||
p = irallocx_prof_sample(tsd_tsdn(tsd), old_ptr, old_usize,
|
p = irallocx_prof_sample(tsd_tsdn(tsd), old_ptr, old_usize,
|
||||||
*usize, alignment, zero, tcache, arena, tctx);
|
*usize, alignment, zero, tcache, arena, tctx, hook_args);
|
||||||
} else {
|
} else {
|
||||||
p = iralloct(tsd_tsdn(tsd), old_ptr, old_usize, size, alignment,
|
p = iralloct(tsd_tsdn(tsd), old_ptr, old_usize, size, alignment,
|
||||||
zero, tcache, arena);
|
zero, tcache, arena, hook_args);
|
||||||
}
|
}
|
||||||
if (unlikely(p == NULL)) {
|
if (unlikely(p == NULL)) {
|
||||||
prof_alloc_rollback(tsd, tctx, false);
|
prof_alloc_rollback(tsd, tctx, false);
|
||||||
@ -2775,6 +2784,9 @@ je_rallocx(void *ptr, size_t size, int flags) {
|
|||||||
assert(alloc_ctx.szind != NSIZES);
|
assert(alloc_ctx.szind != NSIZES);
|
||||||
old_usize = sz_index2size(alloc_ctx.szind);
|
old_usize = sz_index2size(alloc_ctx.szind);
|
||||||
assert(old_usize == isalloc(tsd_tsdn(tsd), ptr));
|
assert(old_usize == isalloc(tsd_tsdn(tsd), ptr));
|
||||||
|
|
||||||
|
hook_ralloc_args_t hook_args = {false, {(uintptr_t)ptr, size, flags,
|
||||||
|
0}};
|
||||||
if (config_prof && opt_prof) {
|
if (config_prof && opt_prof) {
|
||||||
usize = (alignment == 0) ?
|
usize = (alignment == 0) ?
|
||||||
sz_s2u(size) : sz_sa2u(size, alignment);
|
sz_s2u(size) : sz_sa2u(size, alignment);
|
||||||
@ -2782,13 +2794,13 @@ je_rallocx(void *ptr, size_t size, int flags) {
|
|||||||
goto label_oom;
|
goto label_oom;
|
||||||
}
|
}
|
||||||
p = irallocx_prof(tsd, ptr, old_usize, size, alignment, &usize,
|
p = irallocx_prof(tsd, ptr, old_usize, size, alignment, &usize,
|
||||||
zero, tcache, arena, &alloc_ctx);
|
zero, tcache, arena, &alloc_ctx, &hook_args);
|
||||||
if (unlikely(p == NULL)) {
|
if (unlikely(p == NULL)) {
|
||||||
goto label_oom;
|
goto label_oom;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
p = iralloct(tsd_tsdn(tsd), ptr, old_usize, size, alignment,
|
p = iralloct(tsd_tsdn(tsd), ptr, old_usize, size, alignment,
|
||||||
zero, tcache, arena);
|
zero, tcache, arena, &hook_args);
|
||||||
if (unlikely(p == NULL)) {
|
if (unlikely(p == NULL)) {
|
||||||
goto label_oom;
|
goto label_oom;
|
||||||
}
|
}
|
||||||
|
17
src/large.c
17
src/large.c
@ -270,10 +270,12 @@ large_ralloc_move_helper(tsdn_t *tsdn, arena_t *arena, size_t usize,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
large_ralloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent, size_t usize,
|
large_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t usize,
|
||||||
size_t alignment, bool zero, tcache_t *tcache) {
|
size_t alignment, bool zero, tcache_t *tcache,
|
||||||
size_t oldusize = extent_usize_get(extent);
|
hook_ralloc_args_t *hook_args) {
|
||||||
|
extent_t *extent = iealloc(tsdn, ptr);
|
||||||
|
|
||||||
|
size_t oldusize = extent_usize_get(extent);
|
||||||
/* The following should have been caught by callers. */
|
/* The following should have been caught by callers. */
|
||||||
assert(usize > 0 && usize <= LARGE_MAXCLASS);
|
assert(usize > 0 && usize <= LARGE_MAXCLASS);
|
||||||
/* Both allocation sizes must be large to avoid a move. */
|
/* Both allocation sizes must be large to avoid a move. */
|
||||||
@ -281,6 +283,9 @@ large_ralloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent, size_t usize,
|
|||||||
|
|
||||||
/* Try to avoid moving the allocation. */
|
/* Try to avoid moving the allocation. */
|
||||||
if (!large_ralloc_no_move(tsdn, extent, usize, usize, zero)) {
|
if (!large_ralloc_no_move(tsdn, extent, usize, usize, zero)) {
|
||||||
|
hook_invoke_expand(hook_args->is_realloc
|
||||||
|
? hook_expand_realloc : hook_expand_rallocx, ptr, oldusize,
|
||||||
|
usize, (uintptr_t)ptr, hook_args->args);
|
||||||
return extent_addr_get(extent);
|
return extent_addr_get(extent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,6 +300,12 @@ large_ralloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent, size_t usize,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hook_invoke_alloc(hook_args->is_realloc
|
||||||
|
? hook_alloc_realloc : hook_alloc_rallocx, ret, (uintptr_t)ret,
|
||||||
|
hook_args->args);
|
||||||
|
hook_invoke_dalloc(hook_args->is_realloc
|
||||||
|
? hook_dalloc_realloc : hook_dalloc_rallocx, ptr, hook_args->args);
|
||||||
|
|
||||||
size_t copysize = (usize < oldusize) ? usize : oldusize;
|
size_t copysize = (usize < oldusize) ? usize : oldusize;
|
||||||
memcpy(ret, extent_addr_get(extent), copysize);
|
memcpy(ret, extent_addr_get(extent), copysize);
|
||||||
isdalloct(tsdn, extent_addr_get(extent), oldusize, tcache, NULL, true);
|
isdalloct(tsdn, extent_addr_get(extent), oldusize, tcache, NULL, true);
|
||||||
|
113
test/unit/hook.c
113
test/unit/hook.c
@ -412,6 +412,115 @@ TEST_BEGIN(test_hooks_realloc_as_malloc_or_free) {
|
|||||||
}
|
}
|
||||||
TEST_END
|
TEST_END
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_realloc_test(void *(*ralloc)(void *, size_t, int), int flags,
|
||||||
|
int expand_type, int dalloc_type) {
|
||||||
|
hooks_t hooks = {&test_alloc_hook, &test_dalloc_hook,
|
||||||
|
&test_expand_hook};
|
||||||
|
void *handle = hook_install(TSDN_NULL, &hooks, (void *)123);
|
||||||
|
assert_ptr_ne(handle, NULL, "Hook installation failed");
|
||||||
|
|
||||||
|
void *volatile ptr;
|
||||||
|
void *volatile ptr2;
|
||||||
|
|
||||||
|
/* Realloc in-place, small. */
|
||||||
|
ptr = malloc(129);
|
||||||
|
reset();
|
||||||
|
ptr2 = ralloc(ptr, 130, flags);
|
||||||
|
assert_ptr_eq(ptr, ptr2, "Small realloc moved");
|
||||||
|
|
||||||
|
assert_d_eq(call_count, 1, "Hook not called");
|
||||||
|
assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
|
||||||
|
assert_d_eq(arg_type, expand_type, "Wrong hook type");
|
||||||
|
assert_ptr_eq(ptr, arg_address, "Wrong address");
|
||||||
|
assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
|
||||||
|
"Wrong raw result");
|
||||||
|
assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument");
|
||||||
|
assert_u64_eq((uintptr_t)130, arg_args_raw[1], "Wrong argument");
|
||||||
|
free(ptr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Realloc in-place, large. Since we can't guarantee the large case
|
||||||
|
* across all platforms, we stay resilient to moving results.
|
||||||
|
*/
|
||||||
|
ptr = malloc(2 * 1024 * 1024);
|
||||||
|
free(ptr);
|
||||||
|
ptr2 = malloc(1 * 1024 * 1024);
|
||||||
|
reset();
|
||||||
|
ptr = ralloc(ptr2, 2 * 1024 * 1024, flags);
|
||||||
|
/* ptr is the new address, ptr2 is the old address. */
|
||||||
|
if (ptr == ptr2) {
|
||||||
|
assert_d_eq(call_count, 1, "Hook not called");
|
||||||
|
assert_d_eq(arg_type, expand_type, "Wrong hook type");
|
||||||
|
} else {
|
||||||
|
assert_d_eq(call_count, 2, "Wrong hooks called");
|
||||||
|
assert_ptr_eq(ptr, arg_result, "Wrong address");
|
||||||
|
assert_d_eq(arg_type, dalloc_type, "Wrong hook type");
|
||||||
|
}
|
||||||
|
assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
|
||||||
|
assert_ptr_eq(ptr2, arg_address, "Wrong address");
|
||||||
|
assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
|
||||||
|
"Wrong raw result");
|
||||||
|
assert_u64_eq((uintptr_t)ptr2, arg_args_raw[0], "Wrong argument");
|
||||||
|
assert_u64_eq((uintptr_t)2 * 1024 * 1024, arg_args_raw[1],
|
||||||
|
"Wrong argument");
|
||||||
|
free(ptr);
|
||||||
|
|
||||||
|
/* Realloc with move, small. */
|
||||||
|
ptr = malloc(8);
|
||||||
|
reset();
|
||||||
|
ptr2 = ralloc(ptr, 128, flags);
|
||||||
|
assert_ptr_ne(ptr, ptr2, "Small realloc didn't move");
|
||||||
|
|
||||||
|
assert_d_eq(call_count, 2, "Hook not called");
|
||||||
|
assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
|
||||||
|
assert_d_eq(arg_type, dalloc_type, "Wrong hook type");
|
||||||
|
assert_ptr_eq(ptr, arg_address, "Wrong address");
|
||||||
|
assert_ptr_eq(ptr2, arg_result, "Wrong address");
|
||||||
|
assert_u64_eq((uintptr_t)ptr2, (uintptr_t)arg_result_raw,
|
||||||
|
"Wrong raw result");
|
||||||
|
assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument");
|
||||||
|
assert_u64_eq((uintptr_t)128, arg_args_raw[1], "Wrong argument");
|
||||||
|
free(ptr2);
|
||||||
|
|
||||||
|
/* Realloc with move, large. */
|
||||||
|
ptr = malloc(1);
|
||||||
|
reset();
|
||||||
|
ptr2 = ralloc(ptr, 2 * 1024 * 1024, flags);
|
||||||
|
assert_ptr_ne(ptr, ptr2, "Large realloc didn't move");
|
||||||
|
|
||||||
|
assert_d_eq(call_count, 2, "Hook not called");
|
||||||
|
assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
|
||||||
|
assert_d_eq(arg_type, dalloc_type, "Wrong hook type");
|
||||||
|
assert_ptr_eq(ptr, arg_address, "Wrong address");
|
||||||
|
assert_ptr_eq(ptr2, arg_result, "Wrong address");
|
||||||
|
assert_u64_eq((uintptr_t)ptr2, (uintptr_t)arg_result_raw,
|
||||||
|
"Wrong raw result");
|
||||||
|
assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument");
|
||||||
|
assert_u64_eq((uintptr_t)2 * 1024 * 1024, arg_args_raw[1],
|
||||||
|
"Wrong argument");
|
||||||
|
free(ptr2);
|
||||||
|
|
||||||
|
hook_remove(TSDN_NULL, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
realloc_wrapper(void *ptr, size_t size, UNUSED int flags) {
|
||||||
|
return realloc(ptr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_BEGIN(test_hooks_realloc) {
|
||||||
|
do_realloc_test(&realloc_wrapper, 0, hook_expand_realloc,
|
||||||
|
hook_dalloc_realloc);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
TEST_BEGIN(test_hooks_rallocx) {
|
||||||
|
do_realloc_test(&rallocx, MALLOCX_TCACHE_NONE, hook_expand_rallocx,
|
||||||
|
hook_dalloc_rallocx);
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
int
|
int
|
||||||
main(void) {
|
main(void) {
|
||||||
/* We assert on call counts. */
|
/* We assert on call counts. */
|
||||||
@ -422,5 +531,7 @@ main(void) {
|
|||||||
test_hooks_alloc_simple,
|
test_hooks_alloc_simple,
|
||||||
test_hooks_dalloc_simple,
|
test_hooks_dalloc_simple,
|
||||||
test_hooks_expand_simple,
|
test_hooks_expand_simple,
|
||||||
test_hooks_realloc_as_malloc_or_free);
|
test_hooks_realloc_as_malloc_or_free,
|
||||||
|
test_hooks_realloc,
|
||||||
|
test_hooks_rallocx);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user