mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-30 18:36:12 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			961 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			961 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* -*- 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    (1U <<22)
 | |
| #define GRN_CTX_SEGMENT_MASK    (GRN_CTX_SEGMENT_SIZE - 1)
 | |
| 
 | |
| #define GRN_CTX_SEGMENT_WORD    (1U <<31)
 | |
| #define GRN_CTX_SEGMENT_VLEN    (1U <<30)
 | |
| #define GRN_CTX_SEGMENT_LIFO    (1U <<29)
 | |
| #define GRN_CTX_SEGMENT_DIRTY   (1U <<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 >= (1LL<<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 >= (1LL<<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 */
 | 
