/* -*- c-basic-offset: 2 -*- */
/*
Copyright ( C ) 2009 - 2016 Brazil
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License version 2 . 1 as published by the Free Software Foundation .
This library is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
License along with this library ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1335 USA
*/
#include "grn.h"
#include "grn_alloc.h"
#include "grn_ctx_impl.h"
static int alloc_count = 0 ;
#ifdef USE_FAIL_MALLOC
static int grn_fmalloc_prob = 0 ;
static char *grn_fmalloc_func = NULL;
static char *grn_fmalloc_file = NULL;
static int grn_fmalloc_line = 0 ;
#endif /* USE_FAIL_MALLOC */
#ifdef USE_EXACT_ALLOC_COUNT
# define GRN_ADD_ALLOC_COUNT(count) do { \
uint32_t alloced; \
GRN_ATOMIC_ADD_EX(&alloc_count, count, alloced); \
} while (0 )
#else /* USE_EXACT_ALLOC_COUNT */
# define GRN_ADD_ALLOC_COUNT(count) do { \
alloc_count += count; \
} while (0 )
#endif
void
grn_alloc_init_from_env(void )
{
#ifdef USE_FAIL_MALLOC
{
char grn_fmalloc_prob_env[GRN_ENV_BUFFER_SIZE];
grn_getenv("GRN_FMALLOC_PROB" ,
grn_fmalloc_prob_env,
GRN_ENV_BUFFER_SIZE);
if (grn_fmalloc_prob_env[0 ]) {
char grn_fmalloc_seed_env[GRN_ENV_BUFFER_SIZE];
grn_fmalloc_prob = strtod(grn_fmalloc_prob_env, 0 ) * RAND_MAX;
grn_getenv("GRN_FMALLOC_SEED" ,
grn_fmalloc_seed_env,
GRN_ENV_BUFFER_SIZE);
if (grn_fmalloc_seed_env[0 ]) {
srand((unsigned int )atoi(grn_fmalloc_seed_env));
} else {
srand((unsigned int )time(NULL));
}
}
}
{
static char grn_fmalloc_func_env[GRN_ENV_BUFFER_SIZE];
grn_getenv("GRN_FMALLOC_FUNC" ,
grn_fmalloc_func_env,
GRN_ENV_BUFFER_SIZE);
if (grn_fmalloc_func_env[0 ]) {
grn_fmalloc_func = grn_fmalloc_func_env;
}
}
{
static char grn_fmalloc_file_env[GRN_ENV_BUFFER_SIZE];
grn_getenv("GRN_FMALLOC_FILE" ,
grn_fmalloc_file_env,
GRN_ENV_BUFFER_SIZE);
if (grn_fmalloc_file_env[0 ]) {
grn_fmalloc_file = grn_fmalloc_file_env;
}
}
{
char grn_fmalloc_line_env[GRN_ENV_BUFFER_SIZE];
grn_getenv("GRN_FMALLOC_LINE" ,
grn_fmalloc_line_env,
GRN_ENV_BUFFER_SIZE);
if (grn_fmalloc_line_env[0 ]) {
grn_fmalloc_line = atoi(grn_fmalloc_line_env);
}
}
#endif /* USE_FAIL_MALLOC */
}
#ifdef USE_MEMORY_DEBUG
static grn_critical_section grn_alloc_info_lock;
void
grn_alloc_info_init(void )
{
CRITICAL_SECTION_INIT(grn_alloc_info_lock);
}
void
grn_alloc_info_fin(void )
{
CRITICAL_SECTION_FIN(grn_alloc_info_lock);
}
inline static void
grn_alloc_info_set_backtrace(char *buffer, size_t size)
{
# ifdef HAVE_BACKTRACE
# define N_TRACE_LEVEL 100
static void *trace[N_TRACE_LEVEL];
char **symbols;
int i, n, rest;
rest = size;
n = backtrace(trace, N_TRACE_LEVEL);
symbols = backtrace_symbols(trace, n);
if (symbols) {
for (i = 0 ; i < n; i++) {
int symbol_length;
symbol_length = strlen(symbols[i]);
if (symbol_length + 2 > rest) {
break ;
}
grn_memcpy(buffer, symbols[i], symbol_length);
buffer += symbol_length;
rest -= symbol_length;
buffer[0 ] = '\n' ;
buffer++;
rest--;
buffer[0 ] = '\0' ;
rest--;
}
free(symbols);
} else {
buffer[0 ] = '\0' ;
}
# undef N_TRACE_LEVEL
# else /* HAVE_BACKTRACE */
buffer[0 ] = '\0' ;
# endif /* HAVE_BACKTRACE */
}
inline static void
grn_alloc_info_add(void *address, size_t size,
const char *file, int line, const char *func)
{
grn_ctx *ctx;
grn_alloc_info *new_alloc_info;
ctx = &grn_gctx;
if (!ctx->impl) { return ; }
CRITICAL_SECTION_ENTER(grn_alloc_info_lock);
new_alloc_info = malloc(sizeof (grn_alloc_info));
if (new_alloc_info) {
new_alloc_info->address = address;
new_alloc_info->size = size;
new_alloc_info->freed = GRN_FALSE;
grn_alloc_info_set_backtrace(new_alloc_info->alloc_backtrace,
sizeof (new_alloc_info->alloc_backtrace));
if (file) {
new_alloc_info->file = strdup(file);
} else {
new_alloc_info->file = NULL;
}
new_alloc_info->line = line;
if (func) {
new_alloc_info->func = strdup(func);
} else {
new_alloc_info->func = NULL;
}
new_alloc_info->next = ctx->impl->alloc_info;
ctx->impl->alloc_info = new_alloc_info;
}
CRITICAL_SECTION_LEAVE(grn_alloc_info_lock);
}
inline static void
grn_alloc_info_change(void *old_address, void *new_address, size_t size)
{
grn_ctx *ctx;
grn_alloc_info *alloc_info;
ctx = &grn_gctx;
if (!ctx->impl) { return ; }
CRITICAL_SECTION_ENTER(grn_alloc_info_lock);
alloc_info = ctx->impl->alloc_info;
for (; alloc_info; alloc_info = alloc_info->next) {
if (alloc_info->address == old_address) {
alloc_info->address = new_address;
alloc_info->size = size;
grn_alloc_info_set_backtrace(alloc_info->alloc_backtrace,
sizeof (alloc_info->alloc_backtrace));
}
}
CRITICAL_SECTION_LEAVE(grn_alloc_info_lock);
}
void
grn_alloc_info_dump(grn_ctx *ctx)
{
int i = 0 ;
grn_alloc_info *alloc_info;
if (!ctx) { return ; }
if (!ctx->impl) { return ; }
alloc_info = ctx->impl->alloc_info;
for (; alloc_info; alloc_info = alloc_info->next) {
if (alloc_info->freed) {
printf("address[%d][freed]: %p(%" GRN_FMT_SIZE ")\n" ,
i, alloc_info->address, alloc_info->size);
} else {
printf("address[%d][not-freed]: %p(%" GRN_FMT_SIZE "): %s:%d: %s()\n%s" ,
i,
alloc_info->address,
alloc_info->size,
alloc_info->file ? alloc_info->file : "(unknown)" ,
alloc_info->line,
alloc_info->func ? alloc_info->func : "(unknown)" ,
alloc_info->alloc_backtrace);
}
i++;
}
}
inline static void
grn_alloc_info_check(grn_ctx *ctx, void *address)
{
grn_alloc_info *alloc_info;
if (!grn_gctx.impl) { return ; }
/* grn_alloc_info_dump(ctx); */
CRITICAL_SECTION_ENTER(grn_alloc_info_lock);
alloc_info = grn_gctx.impl->alloc_info;
for (; alloc_info; alloc_info = alloc_info->next) {
if (alloc_info->address == address) {
if (alloc_info->freed) {
GRN_LOG(ctx, GRN_LOG_WARNING,
"double free: %p(%" GRN_FMT_SIZE "):\n"
"alloc backtrace:\n"
"%sfree backtrace:\n"
"%s" ,
alloc_info->address,
alloc_info->size,
alloc_info->alloc_backtrace,
alloc_info->free_backtrace);
} else {
alloc_info->freed = GRN_TRUE;
grn_alloc_info_set_backtrace(alloc_info->free_backtrace,
sizeof (alloc_info->free_backtrace));
}
break ;
}
}
CRITICAL_SECTION_LEAVE(grn_alloc_info_lock);
}
void
grn_alloc_info_free(grn_ctx *ctx)
{
grn_alloc_info *alloc_info;
if (!ctx) { return ; }
if (!ctx->impl) { return ; }
alloc_info = ctx->impl->alloc_info;
while (alloc_info) {
grn_alloc_info *current_alloc_info = alloc_info;
alloc_info = alloc_info->next;
current_alloc_info->next = NULL;
free(current_alloc_info->file);
free(current_alloc_info->func);
free(current_alloc_info);
}
ctx->impl->alloc_info = NULL;
}
#else /* USE_MEMORY_DEBUG */
void
grn_alloc_info_init(void )
{
}
void
grn_alloc_info_fin(void )
{
}
# define grn_alloc_info_add(address, size, file, line, func)
# define grn_alloc_info_change(old_address, new_address, size)
# define grn_alloc_info_check(ctx, address)
void
grn_alloc_info_dump(grn_ctx *ctx)
{
}
void
grn_alloc_info_free(grn_ctx *ctx)
{
}
#endif /* USE_MEMORY_DEBUG */
#define GRN_CTX_SEGMENT_SIZE (1 U <<22 )
#define GRN_CTX_SEGMENT_MASK (GRN_CTX_SEGMENT_SIZE - 1 )
#define GRN_CTX_SEGMENT_WORD (1 U <<31 )
#define GRN_CTX_SEGMENT_VLEN (1 U <<30 )
#define GRN_CTX_SEGMENT_LIFO (1 U <<29 )
#define GRN_CTX_SEGMENT_DIRTY (1 U <<28 )
void
grn_alloc_init_ctx_impl(grn_ctx *ctx)
{
#ifdef USE_DYNAMIC_MALLOC_CHANGE
# ifdef USE_FAIL_MALLOC
ctx->impl->malloc_func = grn_malloc_fail;
ctx->impl->calloc_func = grn_calloc_fail;
ctx->impl->realloc_func = grn_realloc_fail;
ctx->impl->strdup_func = grn_strdup_fail;
# else
ctx->impl->malloc_func = grn_malloc_default;
ctx->impl->calloc_func = grn_calloc_default;
ctx->impl->realloc_func = grn_realloc_default;
ctx->impl->strdup_func = grn_strdup_default;
# endif
#endif
#ifdef USE_MEMORY_DEBUG
ctx->impl->alloc_info = NULL;
#endif
}
void
grn_alloc_fin_ctx_impl(grn_ctx *ctx)
{
int i;
grn_io_mapinfo *mi;
for (i = 0 , mi = ctx->impl->segs; i < GRN_CTX_N_SEGMENTS; i++, mi++) {
if (mi->map) {
//GRN_LOG(ctx, GRN_LOG_NOTICE, "unmap in ctx_fin(%d,%d,%d)", i, (mi->count & GRN_CTX_SEGMENT_MASK), mi->nref);
if (mi->count & GRN_CTX_SEGMENT_VLEN) {
grn_io_anon_unmap(ctx, mi, mi->nref * grn_pagesize);
} else {
grn_io_anon_unmap(ctx, mi, GRN_CTX_SEGMENT_SIZE);
}
}
}
}
#define ALIGN_SIZE (1 <<3 )
#define ALIGN_MASK (ALIGN_SIZE-1 )
#define GRN_CTX_ALLOC_CLEAR 1
static void *
grn_ctx_alloc(grn_ctx *ctx, size_t size, int flags,
const char * file, int line, const char *func)
{
void *res = NULL;
if (!ctx) { return res; }
if (!ctx->impl) {
if (ERRP(ctx, GRN_ERROR)) { return res; }
}
CRITICAL_SECTION_ENTER(ctx->impl->lock);
{
int32_t i;
int32_t *header;
grn_io_mapinfo *mi;
size = ((size + ALIGN_MASK) & ~ALIGN_MASK) + ALIGN_SIZE;
if (size > GRN_CTX_SEGMENT_SIZE) {
uint64_t npages = (size + (grn_pagesize - 1 )) / grn_pagesize;
size_t aligned_size;
if (npages >= (1 LL<<32 )) {
MERR("too long request size=%" GRN_FMT_SIZE, size);
goto exit ;
}
for (i = 0 , mi = ctx->impl->segs;; i++, mi++) {
if (i >= GRN_CTX_N_SEGMENTS) {
MERR("all segments are full" );
goto exit ;
}
if (!mi->map) { break ; }
}
aligned_size = grn_pagesize * ((size_t)npages);
if (!grn_io_anon_map(ctx, mi, aligned_size)) { goto exit ; }
/* GRN_LOG(ctx, GRN_LOG_NOTICE, "map i=%d (%d)", i, npages * grn_pagesize); */
mi->nref = (uint32_t) npages;
mi->count = GRN_CTX_SEGMENT_VLEN;
ctx->impl->currseg = -1 ;
header = mi->map;
header[0 ] = i;
header[1 ] = (int32_t) size;
} else {
if ((i = ctx->impl->currseg) >= 0 )
mi = &ctx->impl->segs[i];
if (i < 0 || size + mi->nref > GRN_CTX_SEGMENT_SIZE) {
for (i = 0 , mi = ctx->impl->segs;; i++, mi++) {
if (i >= GRN_CTX_N_SEGMENTS) {
MERR("all segments are full" );
goto exit ;
}
if (!mi->map) { break ; }
}
if (!grn_io_anon_map(ctx, mi, GRN_CTX_SEGMENT_SIZE)) { goto exit ; }
/* GRN_LOG(ctx, GRN_LOG_NOTICE, "map i=%d", i); */
mi->nref = 0 ;
mi->count = GRN_CTX_SEGMENT_WORD;
ctx->impl->currseg = i;
}
header = (int32_t *)((byte *)mi->map + mi->nref);
mi->nref += size;
mi->count++;
header[0 ] = i;
header[1 ] = (int32_t) size;
if ((flags & GRN_CTX_ALLOC_CLEAR) &&
(mi->count & GRN_CTX_SEGMENT_DIRTY) && (size > ALIGN_SIZE)) {
memset(&header[2 ], 0 , size - ALIGN_SIZE);
}
}
/*
{
char g = ( ctx = = & grn_gctx ) ? ' g ' : ' ' ;
GRN_LOG ( ctx , GRN_LOG_NOTICE , " + % c ( % p ) % s : % d ( % s ) ( % d : % d ) % p mi ( % d : % d ) " , g , ctx , file , line , func , header [ 0 ] , header [ 1 ] , & header [ 2 ] , mi - > nref , ( mi - > count & GRN_CTX_SEGMENT_MASK ) ) ;
}
*/
res = &header[2 ];
}
exit :
CRITICAL_SECTION_LEAVE(ctx->impl->lock);
return res;
}
void *
grn_ctx_malloc(grn_ctx *ctx, size_t size,
const char * file, int line, const char *func)
{
return grn_ctx_alloc(ctx, size, 0 , file, line, func);
}
void *
grn_ctx_calloc(grn_ctx *ctx, size_t size,
const char * file, int line, const char *func)
{
return grn_ctx_alloc(ctx, size, GRN_CTX_ALLOC_CLEAR, file, line, func);
}
void *
grn_ctx_realloc(grn_ctx *ctx, void *ptr, size_t size,
const char * file, int line, const char *func)
{
void *res = NULL;
if (size) {
/* todo : expand if possible */
res = grn_ctx_alloc(ctx, size, 0 , file, line, func);
if (res && ptr) {
int32_t *header = &((int32_t *)ptr)[-2 ];
size_t size_ = header[1 ];
grn_memcpy(res, ptr, size_ > size ? size : size_);
grn_ctx_free(ctx, ptr, file, line, func);
}
} else {
grn_ctx_free(ctx, ptr, file, line, func);
}
return res;
}
char *
grn_ctx_strdup(grn_ctx *ctx, const char *s,
const char * file, int line, const char *func)
{
void *res = NULL;
if (s) {
size_t size = strlen(s) + 1 ;
if ((res = grn_ctx_alloc(ctx, size, 0 , file, line, func))) {
grn_memcpy(res, s, size);
}
}
return res;
}
void
grn_ctx_free(grn_ctx *ctx, void *ptr,
const char * file, int line, const char *func)
{
if (!ctx) { return ; }
if (!ctx->impl) {
ERR(GRN_INVALID_ARGUMENT,"ctx without impl passed." );
return ;
}
CRITICAL_SECTION_ENTER(ctx->impl->lock);
if (ptr) {
int32_t *header = &((int32_t *)ptr)[-2 ];
if (header[0 ] >= GRN_CTX_N_SEGMENTS) {
ERR(GRN_INVALID_ARGUMENT,"invalid ptr passed. ptr=%p seg=%d" , ptr, *header);
goto exit ;
}
/*
{
int32_t i = header [ 0 ] ;
char c = ' X ' , g = ( ctx = = & grn_gctx ) ? ' g ' : ' ' ;
grn_io_mapinfo * mi = & ctx - > impl - > segs [ i ] ;
if ( ! ( mi - > count & GRN_CTX_SEGMENT_VLEN ) & &
mi - > map < = ( void * ) header & & ( char * ) header < ( ( char * ) mi - > map + GRN_CTX_SEGMENT_SIZE ) ) { c = ' - ' ; }
GRN_LOG ( ctx , GRN_LOG_NOTICE , " % c % c ( % p ) % s : % d ( % s ) ( % d : % d ) % p mi ( % d : % d ) " , c , g , ctx , file , line , func , header [ 0 ] , header [ 1 ] , & header [ 2 ] , mi - > nref , ( mi - > count & GRN_CTX_SEGMENT_MASK ) ) ;
}
*/
{
int32_t i = header[0 ];
grn_io_mapinfo *mi = &ctx->impl->segs[i];
if (mi->count & GRN_CTX_SEGMENT_VLEN) {
if (mi->map != header) {
ERR(GRN_INVALID_ARGUMENT,"invalid ptr passed.. ptr=%p seg=%d" , ptr, i);
goto exit ;
}
//GRN_LOG(ctx, GRN_LOG_NOTICE, "umap i=%d (%d)", i, mi->nref * grn_pagesize);
grn_io_anon_unmap(ctx, mi, mi->nref * grn_pagesize);
mi->map = NULL;
} else {
if (!mi->map) {
ERR(GRN_INVALID_ARGUMENT,"invalid ptr passed... ptr=%p seg=%d" , ptr, i);
goto exit ;
}
mi->count--;
if (!(mi->count & GRN_CTX_SEGMENT_MASK)) {
//GRN_LOG(ctx, GRN_LOG_NOTICE, "umap i=%d", i);
if (i == ctx->impl->currseg) {
mi->count |= GRN_CTX_SEGMENT_DIRTY;
mi->nref = 0 ;
} else {
grn_io_anon_unmap(ctx, mi, GRN_CTX_SEGMENT_SIZE);
mi->map = NULL;
}
}
}
}
}
exit :
CRITICAL_SECTION_LEAVE(ctx->impl->lock);
}
void *
grn_ctx_alloc_lifo(grn_ctx *ctx, size_t size,
const char * file, int line, const char *func)
{
if (!ctx) { return NULL; }
if (!ctx->impl) {
if (ERRP(ctx, GRN_ERROR)) { return NULL; }
}
{
int32_t i = ctx->impl->lifoseg;
grn_io_mapinfo *mi = &ctx->impl->segs[i];
if (size > GRN_CTX_SEGMENT_SIZE) {
uint64_t npages = (size + (grn_pagesize - 1 )) / grn_pagesize;
size_t aligned_size;
if (npages >= (1 LL<<32 )) {
MERR("too long request size=%" GRN_FMT_SIZE, size);
return NULL;
}
for (;;) {
if (++i >= GRN_CTX_N_SEGMENTS) {
MERR("all segments are full" );
return NULL;
}
mi++;
if (!mi->map) { break ; }
}
aligned_size = grn_pagesize * ((size_t)npages);
if (!grn_io_anon_map(ctx, mi, aligned_size)) { return NULL; }
mi->nref = (uint32_t) npages;
mi->count = GRN_CTX_SEGMENT_VLEN|GRN_CTX_SEGMENT_LIFO;
ctx->impl->lifoseg = i;
return mi->map;
} else {
size = (size + ALIGN_MASK) & ~ALIGN_MASK;
if (i < 0 || (mi->count & GRN_CTX_SEGMENT_VLEN) || size + mi->nref > GRN_CTX_SEGMENT_SIZE) {
for (;;) {
if (++i >= GRN_CTX_N_SEGMENTS) {
MERR("all segments are full" );
return NULL;
}
if (!(++mi)->map) { break ; }
}
if (!grn_io_anon_map(ctx, mi, GRN_CTX_SEGMENT_SIZE)) { return NULL; }
mi->nref = 0 ;
mi->count = GRN_CTX_SEGMENT_WORD|GRN_CTX_SEGMENT_LIFO;
ctx->impl->lifoseg = i;
}
{
uint32_t u = mi->nref;
mi->nref += size;
return (byte *)mi->map + u;
}
}
}
}
void
grn_ctx_free_lifo(grn_ctx *ctx, void *ptr,
const char * file, int line, const char *func)
{
if (!ctx) { return ; }
if (!ctx->impl) {
ERR(GRN_INVALID_ARGUMENT,"ctx without impl passed." );
return ;
}
{
int32_t i = ctx->impl->lifoseg, done = 0 ;
grn_io_mapinfo *mi = &ctx->impl->segs[i];
if (i < 0 ) {
ERR(GRN_INVALID_ARGUMENT, "lifo buffer is void" );
return ;
}
for (; i >= 0 ; i--, mi--) {
if (!(mi->count & GRN_CTX_SEGMENT_LIFO)) { continue ; }
if (done) { break ; }
if (mi->count & GRN_CTX_SEGMENT_VLEN) {
if (mi->map == ptr) { done = 1 ; }
grn_io_anon_unmap(ctx, mi, mi->nref * grn_pagesize);
mi->map = NULL;
} else {
if (mi->map == ptr) {
done = 1 ;
} else {
if (mi->map < ptr && ptr < (void *)((byte*)mi->map + mi->nref)) {
mi->nref = (uint32_t) ((uintptr_t)ptr - (uintptr_t)mi->map);
break ;
}
}
grn_io_anon_unmap(ctx, mi, GRN_CTX_SEGMENT_SIZE);
mi->map = NULL;
}
}
ctx->impl->lifoseg = i;
}
}
#if defined (USE_DYNAMIC_MALLOC_CHANGE)
grn_malloc_func
grn_ctx_get_malloc(grn_ctx *ctx)
{
if (!ctx || !ctx->impl) { return NULL; }
return ctx->impl->malloc_func;
}
void
grn_ctx_set_malloc(grn_ctx *ctx, grn_malloc_func malloc_func)
{
if (!ctx || !ctx->impl) { return ; }
ctx->impl->malloc_func = malloc_func;
}
grn_calloc_func
grn_ctx_get_calloc(grn_ctx *ctx)
{
if (!ctx || !ctx->impl) { return NULL; }
return ctx->impl->calloc_func;
}
void
grn_ctx_set_calloc(grn_ctx *ctx, grn_calloc_func calloc_func)
{
if (!ctx || !ctx->impl) { return ; }
ctx->impl->calloc_func = calloc_func;
}
grn_realloc_func
grn_ctx_get_realloc(grn_ctx *ctx)
{
if (!ctx || !ctx->impl) { return NULL; }
return ctx->impl->realloc_func;
}
void
grn_ctx_set_realloc(grn_ctx *ctx, grn_realloc_func realloc_func)
{
if (!ctx || !ctx->impl) { return ; }
ctx->impl->realloc_func = realloc_func;
}
grn_strdup_func
grn_ctx_get_strdup(grn_ctx *ctx)
{
if (!ctx || !ctx->impl) { return NULL; }
return ctx->impl->strdup_func;
}
void
grn_ctx_set_strdup(grn_ctx *ctx, grn_strdup_func strdup_func)
{
if (!ctx || !ctx->impl) { return ; }
ctx->impl->strdup_func = strdup_func;
}
grn_free_func
grn_ctx_get_free(grn_ctx *ctx)
{
if (!ctx || !ctx->impl) { return NULL; }
return ctx->impl->free_func;
}
void
grn_ctx_set_free(grn_ctx *ctx, grn_free_func free_func)
{
if (!ctx || !ctx->impl) { return ; }
ctx->impl->free_func = free_func;
}
void *
grn_malloc(grn_ctx *ctx, size_t size,
const char * file, int line, const char *func)
{
if (ctx && ctx->impl && ctx->impl->malloc_func) {
return ctx->impl->malloc_func(ctx, size, file, line, func);
} else {
return grn_malloc_default(ctx, size, file, line, func);
}
}
void *
grn_calloc(grn_ctx *ctx, size_t size,
const char * file, int line, const char *func)
{
if (ctx && ctx->impl && ctx->impl->calloc_func) {
return ctx->impl->calloc_func(ctx, size, file, line, func);
} else {
return grn_calloc_default(ctx, size, file, line, func);
}
}
void *
grn_realloc(grn_ctx *ctx, void *ptr, size_t size,
const char * file, int line, const char *func)
{
if (ctx && ctx->impl && ctx->impl->realloc_func) {
return ctx->impl->realloc_func(ctx, ptr, size, file, line, func);
} else {
return grn_realloc_default(ctx, ptr, size, file, line, func);
}
}
char *
grn_strdup(grn_ctx *ctx, const char *string,
const char * file, int line, const char *func)
{
if (ctx && ctx->impl && ctx->impl->strdup_func) {
return ctx->impl->strdup_func(ctx, string, file, line, func);
} else {
return grn_strdup_default(ctx, string, file, line, func);
}
}
void
grn_free(grn_ctx *ctx, void *ptr,
const char * file, int line, const char *func)
{
if (ctx && ctx->impl && ctx->impl->free_func) {
return ctx->impl->free_func(ctx, ptr, file, line, func);
} else {
return grn_free_default(ctx, ptr, file, line, func);
}
}
#endif
void *
grn_malloc_default(grn_ctx *ctx, size_t size,
const char * file, int line, const char *func)
{
if (!ctx) { return NULL; }
{
void *res = malloc(size);
if (res) {
GRN_ADD_ALLOC_COUNT(1 );
grn_alloc_info_add(res, size, file, line, func);
} else {
if (!(res = malloc(size))) {
MERR("malloc fail (%" GRN_FMT_SIZE ")=%p (%s:%d) <%d>" ,
size, res, file, line, alloc_count);
} else {
GRN_ADD_ALLOC_COUNT(1 );
grn_alloc_info_add(res, size, file, line, func);
}
}
return res;
}
}
void *
grn_calloc_default(grn_ctx *ctx, size_t size,
const char * file, int line, const char *func)
{
if (!ctx) { return NULL; }
{
void *res = calloc(size, 1 );
if (res) {
GRN_ADD_ALLOC_COUNT(1 );
grn_alloc_info_add(res, size, file, line, func);
} else {
if (!(res = calloc(size, 1 ))) {
MERR("calloc fail (%" GRN_FMT_SIZE ")=%p (%s:%d) <%d>" ,
size, res, file, line, alloc_count);
} else {
GRN_ADD_ALLOC_COUNT(1 );
grn_alloc_info_add(res, size, file, line, func);
}
}
return res;
}
}
void
grn_free_default(grn_ctx *ctx, void *ptr,
const char * file, int line, const char *func)
{
if (!ctx) { return ; }
grn_alloc_info_check(ctx, ptr);
{
free(ptr);
if (ptr) {
GRN_ADD_ALLOC_COUNT(-1 );
} else {
GRN_LOG(ctx, GRN_LOG_ALERT, "free fail (nullptr) (%s:%d) <%d>" ,
file, line, alloc_count);
}
}
}
void *
grn_realloc_default(grn_ctx *ctx, void *ptr, size_t size,
const char * file, int line, const char *func)
{
void *res;
if (!ctx) { return NULL; }
if (size) {
if (!(res = realloc(ptr, size))) {
if (!(res = realloc(ptr, size))) {
MERR("realloc fail (%p,%" GRN_FMT_SIZE ")=%p (%s:%d) <%d>" ,
ptr, size, res, file, line, alloc_count);
return NULL;
}
}
if (ptr) {
grn_alloc_info_change(ptr, res, size);
} else {
GRN_ADD_ALLOC_COUNT(1 );
grn_alloc_info_add(res, size, file, line, func);
}
} else {
if (!ptr) { return NULL; }
grn_alloc_info_check(ctx, ptr);
GRN_ADD_ALLOC_COUNT(-1 );
free(ptr);
res = NULL;
}
return res;
}
int
grn_alloc_count(void )
{
return alloc_count;
}
char *
grn_strdup_default(grn_ctx *ctx, const char *s,
const char * file, int line, const char *func)
{
if (!ctx) { return NULL; }
{
char *res = grn_strdup_raw(s);
if (res) {
GRN_ADD_ALLOC_COUNT(1 );
grn_alloc_info_add(res, strlen(res) + 1 , file, line, func);
} else {
if (!(res = grn_strdup_raw(s))) {
MERR("strdup(%p)=%p (%s:%d) <%d>" , s, res, file, line, alloc_count);
} else {
GRN_ADD_ALLOC_COUNT(1 );
grn_alloc_info_add(res, strlen(res) + 1 , file, line, func);
}
}
return res;
}
}
#ifdef USE_FAIL_MALLOC
int
grn_fail_malloc_check(size_t size,
const char *file, int line, const char *func)
{
if ((grn_fmalloc_file && strcmp(file, grn_fmalloc_file)) ||
(grn_fmalloc_line && line != grn_fmalloc_line) ||
(grn_fmalloc_func && strcmp(func, grn_fmalloc_func))) {
return 1 ;
}
if (grn_fmalloc_prob && grn_fmalloc_prob >= rand()) {
return 0 ;
}
return 1 ;
}
void *
grn_malloc_fail(grn_ctx *ctx, size_t size,
const char * file, int line, const char *func)
{
if (grn_fail_malloc_check(size, file, line, func)) {
return grn_malloc_default(ctx, size, file, line, func);
} else {
MERR("fail_malloc (%" GRN_FMT_SIZE ") (%s:%d@%s) <%d>" ,
size, file, line, func, alloc_count);
return NULL;
}
}
void *
grn_calloc_fail(grn_ctx *ctx, size_t size,
const char * file, int line, const char *func)
{
if (grn_fail_malloc_check(size, file, line, func)) {
return grn_calloc_default(ctx, size, file, line, func);
} else {
MERR("fail_calloc (%" GRN_FMT_SIZE ") (%s:%d@%s) <%d>" ,
size, file, line, func, alloc_count);
return NULL;
}
}
void *
grn_realloc_fail(grn_ctx *ctx, void *ptr, size_t size,
const char * file, int line, const char *func)
{
if (grn_fail_malloc_check(size, file, line, func)) {
return grn_realloc_default(ctx, ptr, size, file, line, func);
} else {
MERR("fail_realloc (%p,%" GRN_FMT_SIZE ") (%s:%d@%s) <%d>" ,
ptr, size, file, line, func, alloc_count);
return NULL;
}
}
char *
grn_strdup_fail(grn_ctx *ctx, const char *s,
const char * file, int line, const char *func)
{
if (grn_fail_malloc_check(strlen(s), file, line, func)) {
return grn_strdup_default(ctx, s, file, line, func);
} else {
MERR("fail_strdup(%p) (%s:%d@%s) <%d>" , s, file, line, func, alloc_count);
return NULL;
}
}
#endif /* USE_FAIL_MALLOC */
Messung V0.5 in Prozent C=99 H=91 G=94
¤ Dauer der Verarbeitung: 0.4 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland