1 | /* -*- c-basic-offset: 2 -*- */ |
2 | /* |
3 | Copyright(C) 2009-2016 Brazil |
4 | |
5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License version 2.1 as published by the Free Software Foundation. |
8 | |
9 | This library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with this library; if not, write to the Free Software |
16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
17 | */ |
18 | |
19 | #include "grn.h" |
20 | #include "grn_alloc.h" |
21 | #include "grn_ctx_impl.h" |
22 | |
23 | static int alloc_count = 0; |
24 | |
25 | #ifdef USE_FAIL_MALLOC |
26 | static int grn_fmalloc_prob = 0; |
27 | static char *grn_fmalloc_func = NULL; |
28 | static char *grn_fmalloc_file = NULL; |
29 | static int grn_fmalloc_line = 0; |
30 | #endif /* USE_FAIL_MALLOC */ |
31 | |
32 | #ifdef USE_EXACT_ALLOC_COUNT |
33 | # define GRN_ADD_ALLOC_COUNT(count) do { \ |
34 | uint32_t alloced; \ |
35 | GRN_ATOMIC_ADD_EX(&alloc_count, count, alloced); \ |
36 | } while (0) |
37 | #else /* USE_EXACT_ALLOC_COUNT */ |
38 | # define GRN_ADD_ALLOC_COUNT(count) do { \ |
39 | alloc_count += count; \ |
40 | } while (0) |
41 | #endif |
42 | |
43 | void |
44 | grn_alloc_init_from_env(void) |
45 | { |
46 | #ifdef USE_FAIL_MALLOC |
47 | { |
48 | char grn_fmalloc_prob_env[GRN_ENV_BUFFER_SIZE]; |
49 | grn_getenv("GRN_FMALLOC_PROB" , |
50 | grn_fmalloc_prob_env, |
51 | GRN_ENV_BUFFER_SIZE); |
52 | if (grn_fmalloc_prob_env[0]) { |
53 | char grn_fmalloc_seed_env[GRN_ENV_BUFFER_SIZE]; |
54 | grn_fmalloc_prob = strtod(grn_fmalloc_prob_env, 0) * RAND_MAX; |
55 | grn_getenv("GRN_FMALLOC_SEED" , |
56 | grn_fmalloc_seed_env, |
57 | GRN_ENV_BUFFER_SIZE); |
58 | if (grn_fmalloc_seed_env[0]) { |
59 | srand((unsigned int)atoi(grn_fmalloc_seed_env)); |
60 | } else { |
61 | srand((unsigned int)time(NULL)); |
62 | } |
63 | } |
64 | } |
65 | { |
66 | static char grn_fmalloc_func_env[GRN_ENV_BUFFER_SIZE]; |
67 | grn_getenv("GRN_FMALLOC_FUNC" , |
68 | grn_fmalloc_func_env, |
69 | GRN_ENV_BUFFER_SIZE); |
70 | if (grn_fmalloc_func_env[0]) { |
71 | grn_fmalloc_func = grn_fmalloc_func_env; |
72 | } |
73 | } |
74 | { |
75 | static char grn_fmalloc_file_env[GRN_ENV_BUFFER_SIZE]; |
76 | grn_getenv("GRN_FMALLOC_FILE" , |
77 | grn_fmalloc_file_env, |
78 | GRN_ENV_BUFFER_SIZE); |
79 | if (grn_fmalloc_file_env[0]) { |
80 | grn_fmalloc_file = grn_fmalloc_file_env; |
81 | } |
82 | } |
83 | { |
84 | char grn_fmalloc_line_env[GRN_ENV_BUFFER_SIZE]; |
85 | grn_getenv("GRN_FMALLOC_LINE" , |
86 | grn_fmalloc_line_env, |
87 | GRN_ENV_BUFFER_SIZE); |
88 | if (grn_fmalloc_line_env[0]) { |
89 | grn_fmalloc_line = atoi(grn_fmalloc_line_env); |
90 | } |
91 | } |
92 | #endif /* USE_FAIL_MALLOC */ |
93 | } |
94 | |
95 | #ifdef USE_MEMORY_DEBUG |
96 | static grn_critical_section grn_alloc_info_lock; |
97 | |
98 | void |
99 | grn_alloc_info_init(void) |
100 | { |
101 | CRITICAL_SECTION_INIT(grn_alloc_info_lock); |
102 | } |
103 | |
104 | void |
105 | grn_alloc_info_fin(void) |
106 | { |
107 | CRITICAL_SECTION_FIN(grn_alloc_info_lock); |
108 | } |
109 | |
110 | inline static void |
111 | grn_alloc_info_set_backtrace(char *buffer, size_t size) |
112 | { |
113 | # ifdef HAVE_BACKTRACE |
114 | # define N_TRACE_LEVEL 100 |
115 | static void *trace[N_TRACE_LEVEL]; |
116 | char **symbols; |
117 | int i, n, rest; |
118 | |
119 | rest = size; |
120 | n = backtrace(trace, N_TRACE_LEVEL); |
121 | symbols = backtrace_symbols(trace, n); |
122 | if (symbols) { |
123 | for (i = 0; i < n; i++) { |
124 | int symbol_length; |
125 | |
126 | symbol_length = strlen(symbols[i]); |
127 | if (symbol_length + 2 > rest) { |
128 | break; |
129 | } |
130 | grn_memcpy(buffer, symbols[i], symbol_length); |
131 | buffer += symbol_length; |
132 | rest -= symbol_length; |
133 | buffer[0] = '\n'; |
134 | buffer++; |
135 | rest--; |
136 | buffer[0] = '\0'; |
137 | rest--; |
138 | } |
139 | free(symbols); |
140 | } else { |
141 | buffer[0] = '\0'; |
142 | } |
143 | # undef N_TRACE_LEVEL |
144 | # else /* HAVE_BACKTRACE */ |
145 | buffer[0] = '\0'; |
146 | # endif /* HAVE_BACKTRACE */ |
147 | } |
148 | |
149 | inline static void |
150 | grn_alloc_info_add(void *address, size_t size, |
151 | const char *file, int line, const char *func) |
152 | { |
153 | grn_ctx *ctx; |
154 | grn_alloc_info *new_alloc_info; |
155 | |
156 | ctx = &grn_gctx; |
157 | if (!ctx->impl) { return; } |
158 | |
159 | CRITICAL_SECTION_ENTER(grn_alloc_info_lock); |
160 | new_alloc_info = malloc(sizeof(grn_alloc_info)); |
161 | if (new_alloc_info) { |
162 | new_alloc_info->address = address; |
163 | new_alloc_info->size = size; |
164 | new_alloc_info->freed = GRN_FALSE; |
165 | grn_alloc_info_set_backtrace(new_alloc_info->alloc_backtrace, |
166 | sizeof(new_alloc_info->alloc_backtrace)); |
167 | if (file) { |
168 | new_alloc_info->file = strdup(file); |
169 | } else { |
170 | new_alloc_info->file = NULL; |
171 | } |
172 | new_alloc_info->line = line; |
173 | if (func) { |
174 | new_alloc_info->func = strdup(func); |
175 | } else { |
176 | new_alloc_info->func = NULL; |
177 | } |
178 | new_alloc_info->next = ctx->impl->alloc_info; |
179 | ctx->impl->alloc_info = new_alloc_info; |
180 | } |
181 | CRITICAL_SECTION_LEAVE(grn_alloc_info_lock); |
182 | } |
183 | |
184 | inline static void |
185 | grn_alloc_info_change(void *old_address, void *new_address, size_t size) |
186 | { |
187 | grn_ctx *ctx; |
188 | grn_alloc_info *alloc_info; |
189 | |
190 | ctx = &grn_gctx; |
191 | if (!ctx->impl) { return; } |
192 | |
193 | CRITICAL_SECTION_ENTER(grn_alloc_info_lock); |
194 | alloc_info = ctx->impl->alloc_info; |
195 | for (; alloc_info; alloc_info = alloc_info->next) { |
196 | if (alloc_info->address == old_address) { |
197 | alloc_info->address = new_address; |
198 | alloc_info->size = size; |
199 | grn_alloc_info_set_backtrace(alloc_info->alloc_backtrace, |
200 | sizeof(alloc_info->alloc_backtrace)); |
201 | } |
202 | } |
203 | CRITICAL_SECTION_LEAVE(grn_alloc_info_lock); |
204 | } |
205 | |
206 | void |
207 | grn_alloc_info_dump(grn_ctx *ctx) |
208 | { |
209 | int i = 0; |
210 | grn_alloc_info *alloc_info; |
211 | |
212 | if (!ctx) { return; } |
213 | if (!ctx->impl) { return; } |
214 | |
215 | alloc_info = ctx->impl->alloc_info; |
216 | for (; alloc_info; alloc_info = alloc_info->next) { |
217 | if (alloc_info->freed) { |
218 | printf("address[%d][freed]: %p(%" GRN_FMT_SIZE ")\n" , |
219 | i, alloc_info->address, alloc_info->size); |
220 | } else { |
221 | printf("address[%d][not-freed]: %p(%" GRN_FMT_SIZE "): %s:%d: %s()\n%s" , |
222 | i, |
223 | alloc_info->address, |
224 | alloc_info->size, |
225 | alloc_info->file ? alloc_info->file : "(unknown)" , |
226 | alloc_info->line, |
227 | alloc_info->func ? alloc_info->func : "(unknown)" , |
228 | alloc_info->alloc_backtrace); |
229 | } |
230 | i++; |
231 | } |
232 | } |
233 | |
234 | inline static void |
235 | grn_alloc_info_check(grn_ctx *ctx, void *address) |
236 | { |
237 | grn_alloc_info *alloc_info; |
238 | |
239 | if (!grn_gctx.impl) { return; } |
240 | /* grn_alloc_info_dump(ctx); */ |
241 | |
242 | CRITICAL_SECTION_ENTER(grn_alloc_info_lock); |
243 | alloc_info = grn_gctx.impl->alloc_info; |
244 | for (; alloc_info; alloc_info = alloc_info->next) { |
245 | if (alloc_info->address == address) { |
246 | if (alloc_info->freed) { |
247 | GRN_LOG(ctx, GRN_LOG_WARNING, |
248 | "double free: %p(%" GRN_FMT_SIZE "):\n" |
249 | "alloc backtrace:\n" |
250 | "%sfree backtrace:\n" |
251 | "%s" , |
252 | alloc_info->address, |
253 | alloc_info->size, |
254 | alloc_info->alloc_backtrace, |
255 | alloc_info->free_backtrace); |
256 | } else { |
257 | alloc_info->freed = GRN_TRUE; |
258 | grn_alloc_info_set_backtrace(alloc_info->free_backtrace, |
259 | sizeof(alloc_info->free_backtrace)); |
260 | } |
261 | break; |
262 | } |
263 | } |
264 | CRITICAL_SECTION_LEAVE(grn_alloc_info_lock); |
265 | } |
266 | |
267 | void |
268 | grn_alloc_info_free(grn_ctx *ctx) |
269 | { |
270 | grn_alloc_info *alloc_info; |
271 | |
272 | if (!ctx) { return; } |
273 | if (!ctx->impl) { return; } |
274 | |
275 | alloc_info = ctx->impl->alloc_info; |
276 | while (alloc_info) { |
277 | grn_alloc_info *current_alloc_info = alloc_info; |
278 | alloc_info = alloc_info->next; |
279 | current_alloc_info->next = NULL; |
280 | free(current_alloc_info->file); |
281 | free(current_alloc_info->func); |
282 | free(current_alloc_info); |
283 | } |
284 | ctx->impl->alloc_info = NULL; |
285 | } |
286 | |
287 | #else /* USE_MEMORY_DEBUG */ |
288 | void |
289 | grn_alloc_info_init(void) |
290 | { |
291 | } |
292 | |
293 | void |
294 | grn_alloc_info_fin(void) |
295 | { |
296 | } |
297 | |
298 | # define grn_alloc_info_add(address, size, file, line, func) |
299 | # define grn_alloc_info_change(old_address, new_address, size) |
300 | # define grn_alloc_info_check(ctx, address) |
301 | |
302 | void |
303 | grn_alloc_info_dump(grn_ctx *ctx) |
304 | { |
305 | } |
306 | |
307 | void |
308 | grn_alloc_info_free(grn_ctx *ctx) |
309 | { |
310 | } |
311 | #endif /* USE_MEMORY_DEBUG */ |
312 | |
313 | #define GRN_CTX_SEGMENT_SIZE (1<<22) |
314 | #define GRN_CTX_SEGMENT_MASK (GRN_CTX_SEGMENT_SIZE - 1) |
315 | |
316 | #define GRN_CTX_SEGMENT_WORD (1<<31) |
317 | #define GRN_CTX_SEGMENT_VLEN (1<<30) |
318 | #define GRN_CTX_SEGMENT_LIFO (1<<29) |
319 | #define GRN_CTX_SEGMENT_DIRTY (1<<28) |
320 | |
321 | void |
322 | grn_alloc_init_ctx_impl(grn_ctx *ctx) |
323 | { |
324 | #ifdef USE_DYNAMIC_MALLOC_CHANGE |
325 | # ifdef USE_FAIL_MALLOC |
326 | ctx->impl->malloc_func = grn_malloc_fail; |
327 | ctx->impl->calloc_func = grn_calloc_fail; |
328 | ctx->impl->realloc_func = grn_realloc_fail; |
329 | ctx->impl->strdup_func = grn_strdup_fail; |
330 | # else |
331 | ctx->impl->malloc_func = grn_malloc_default; |
332 | ctx->impl->calloc_func = grn_calloc_default; |
333 | ctx->impl->realloc_func = grn_realloc_default; |
334 | ctx->impl->strdup_func = grn_strdup_default; |
335 | # endif |
336 | #endif |
337 | |
338 | #ifdef USE_MEMORY_DEBUG |
339 | ctx->impl->alloc_info = NULL; |
340 | #endif |
341 | } |
342 | |
343 | void |
344 | grn_alloc_fin_ctx_impl(grn_ctx *ctx) |
345 | { |
346 | int i; |
347 | grn_io_mapinfo *mi; |
348 | for (i = 0, mi = ctx->impl->segs; i < GRN_CTX_N_SEGMENTS; i++, mi++) { |
349 | if (mi->map) { |
350 | //GRN_LOG(ctx, GRN_LOG_NOTICE, "unmap in ctx_fin(%d,%d,%d)", i, (mi->count & GRN_CTX_SEGMENT_MASK), mi->nref); |
351 | if (mi->count & GRN_CTX_SEGMENT_VLEN) { |
352 | grn_io_anon_unmap(ctx, mi, mi->nref * grn_pagesize); |
353 | } else { |
354 | grn_io_anon_unmap(ctx, mi, GRN_CTX_SEGMENT_SIZE); |
355 | } |
356 | } |
357 | } |
358 | } |
359 | |
360 | #define ALIGN_SIZE (1<<3) |
361 | #define ALIGN_MASK (ALIGN_SIZE-1) |
362 | #define GRN_CTX_ALLOC_CLEAR 1 |
363 | |
364 | static void * |
365 | grn_ctx_alloc(grn_ctx *ctx, size_t size, int flags, |
366 | const char* file, int line, const char *func) |
367 | { |
368 | void *res = NULL; |
369 | if (!ctx) { return res; } |
370 | if (!ctx->impl) { |
371 | if (ERRP(ctx, GRN_ERROR)) { return res; } |
372 | } |
373 | CRITICAL_SECTION_ENTER(ctx->impl->lock); |
374 | { |
375 | int32_t i; |
376 | int32_t *; |
377 | grn_io_mapinfo *mi; |
378 | size = ((size + ALIGN_MASK) & ~ALIGN_MASK) + ALIGN_SIZE; |
379 | if (size > GRN_CTX_SEGMENT_SIZE) { |
380 | uint64_t npages = (size + (grn_pagesize - 1)) / grn_pagesize; |
381 | size_t aligned_size; |
382 | if (npages >= (1LL<<32)) { |
383 | MERR("too long request size=%" GRN_FMT_SIZE, size); |
384 | goto exit; |
385 | } |
386 | for (i = 0, mi = ctx->impl->segs;; i++, mi++) { |
387 | if (i >= GRN_CTX_N_SEGMENTS) { |
388 | MERR("all segments are full" ); |
389 | goto exit; |
390 | } |
391 | if (!mi->map) { break; } |
392 | } |
393 | aligned_size = grn_pagesize * ((size_t)npages); |
394 | if (!grn_io_anon_map(ctx, mi, aligned_size)) { goto exit; } |
395 | /* GRN_LOG(ctx, GRN_LOG_NOTICE, "map i=%d (%d)", i, npages * grn_pagesize); */ |
396 | mi->nref = (uint32_t) npages; |
397 | mi->count = GRN_CTX_SEGMENT_VLEN; |
398 | ctx->impl->currseg = -1; |
399 | header = mi->map; |
400 | header[0] = i; |
401 | header[1] = (int32_t) size; |
402 | } else { |
403 | i = ctx->impl->currseg; |
404 | mi = &ctx->impl->segs[i]; |
405 | if (i < 0 || size + mi->nref > GRN_CTX_SEGMENT_SIZE) { |
406 | for (i = 0, mi = ctx->impl->segs;; i++, mi++) { |
407 | if (i >= GRN_CTX_N_SEGMENTS) { |
408 | MERR("all segments are full" ); |
409 | goto exit; |
410 | } |
411 | if (!mi->map) { break; } |
412 | } |
413 | if (!grn_io_anon_map(ctx, mi, GRN_CTX_SEGMENT_SIZE)) { goto exit; } |
414 | /* GRN_LOG(ctx, GRN_LOG_NOTICE, "map i=%d", i); */ |
415 | mi->nref = 0; |
416 | mi->count = GRN_CTX_SEGMENT_WORD; |
417 | ctx->impl->currseg = i; |
418 | } |
419 | header = (int32_t *)((byte *)mi->map + mi->nref); |
420 | mi->nref += size; |
421 | mi->count++; |
422 | header[0] = i; |
423 | header[1] = (int32_t) size; |
424 | if ((flags & GRN_CTX_ALLOC_CLEAR) && |
425 | (mi->count & GRN_CTX_SEGMENT_DIRTY) && (size > ALIGN_SIZE)) { |
426 | memset(&header[2], 0, size - ALIGN_SIZE); |
427 | } |
428 | } |
429 | /* |
430 | { |
431 | char g = (ctx == &grn_gctx) ? 'g' : ' '; |
432 | 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)); |
433 | } |
434 | */ |
435 | res = &header[2]; |
436 | } |
437 | exit : |
438 | CRITICAL_SECTION_LEAVE(ctx->impl->lock); |
439 | return res; |
440 | } |
441 | |
442 | void * |
443 | grn_ctx_malloc(grn_ctx *ctx, size_t size, |
444 | const char* file, int line, const char *func) |
445 | { |
446 | return grn_ctx_alloc(ctx, size, 0, file, line, func); |
447 | } |
448 | |
449 | void * |
450 | grn_ctx_calloc(grn_ctx *ctx, size_t size, |
451 | const char* file, int line, const char *func) |
452 | { |
453 | return grn_ctx_alloc(ctx, size, GRN_CTX_ALLOC_CLEAR, file, line, func); |
454 | } |
455 | |
456 | void * |
457 | grn_ctx_realloc(grn_ctx *ctx, void *ptr, size_t size, |
458 | const char* file, int line, const char *func) |
459 | { |
460 | void *res = NULL; |
461 | if (size) { |
462 | /* todo : expand if possible */ |
463 | res = grn_ctx_alloc(ctx, size, 0, file, line, func); |
464 | if (res && ptr) { |
465 | int32_t * = &((int32_t *)ptr)[-2]; |
466 | size_t size_ = header[1]; |
467 | grn_memcpy(res, ptr, size_ > size ? size : size_); |
468 | grn_ctx_free(ctx, ptr, file, line, func); |
469 | } |
470 | } else { |
471 | grn_ctx_free(ctx, ptr, file, line, func); |
472 | } |
473 | return res; |
474 | } |
475 | |
476 | char * |
477 | grn_ctx_strdup(grn_ctx *ctx, const char *s, |
478 | const char* file, int line, const char *func) |
479 | { |
480 | void *res = NULL; |
481 | if (s) { |
482 | size_t size = strlen(s) + 1; |
483 | if ((res = grn_ctx_alloc(ctx, size, 0, file, line, func))) { |
484 | grn_memcpy(res, s, size); |
485 | } |
486 | } |
487 | return res; |
488 | } |
489 | |
490 | void |
491 | grn_ctx_free(grn_ctx *ctx, void *ptr, |
492 | const char* file, int line, const char *func) |
493 | { |
494 | if (!ctx) { return; } |
495 | if (!ctx->impl) { |
496 | ERR(GRN_INVALID_ARGUMENT,"ctx without impl passed." ); |
497 | return; |
498 | } |
499 | CRITICAL_SECTION_ENTER(ctx->impl->lock); |
500 | if (ptr) { |
501 | int32_t * = &((int32_t *)ptr)[-2]; |
502 | |
503 | if (header[0] >= GRN_CTX_N_SEGMENTS) { |
504 | ERR(GRN_INVALID_ARGUMENT,"invalid ptr passed. ptr=%p seg=%d" , ptr, *header); |
505 | goto exit; |
506 | } |
507 | /* |
508 | { |
509 | int32_t i = header[0]; |
510 | char c = 'X', g = (ctx == &grn_gctx) ? 'g' : ' '; |
511 | grn_io_mapinfo *mi = &ctx->impl->segs[i]; |
512 | if (!(mi->count & GRN_CTX_SEGMENT_VLEN) && |
513 | mi->map <= (void *)header && (char *)header < ((char *)mi->map + GRN_CTX_SEGMENT_SIZE)) { c = '-'; } |
514 | 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)); |
515 | } |
516 | */ |
517 | { |
518 | int32_t i = header[0]; |
519 | grn_io_mapinfo *mi = &ctx->impl->segs[i]; |
520 | if (mi->count & GRN_CTX_SEGMENT_VLEN) { |
521 | if (mi->map != header) { |
522 | ERR(GRN_INVALID_ARGUMENT,"invalid ptr passed.. ptr=%p seg=%d" , ptr, i); |
523 | goto exit; |
524 | } |
525 | //GRN_LOG(ctx, GRN_LOG_NOTICE, "umap i=%d (%d)", i, mi->nref * grn_pagesize); |
526 | grn_io_anon_unmap(ctx, mi, mi->nref * grn_pagesize); |
527 | mi->map = NULL; |
528 | } else { |
529 | if (!mi->map) { |
530 | ERR(GRN_INVALID_ARGUMENT,"invalid ptr passed... ptr=%p seg=%d" , ptr, i); |
531 | goto exit; |
532 | } |
533 | mi->count--; |
534 | if (!(mi->count & GRN_CTX_SEGMENT_MASK)) { |
535 | //GRN_LOG(ctx, GRN_LOG_NOTICE, "umap i=%d", i); |
536 | if (i == ctx->impl->currseg) { |
537 | mi->count |= GRN_CTX_SEGMENT_DIRTY; |
538 | mi->nref = 0; |
539 | } else { |
540 | grn_io_anon_unmap(ctx, mi, GRN_CTX_SEGMENT_SIZE); |
541 | mi->map = NULL; |
542 | } |
543 | } |
544 | } |
545 | } |
546 | } |
547 | exit : |
548 | CRITICAL_SECTION_LEAVE(ctx->impl->lock); |
549 | } |
550 | |
551 | void * |
552 | grn_ctx_alloc_lifo(grn_ctx *ctx, size_t size, |
553 | const char* file, int line, const char *func) |
554 | { |
555 | if (!ctx) { return NULL; } |
556 | if (!ctx->impl) { |
557 | if (ERRP(ctx, GRN_ERROR)) { return NULL; } |
558 | } |
559 | { |
560 | int32_t i = ctx->impl->lifoseg; |
561 | grn_io_mapinfo *mi = &ctx->impl->segs[i]; |
562 | if (size > GRN_CTX_SEGMENT_SIZE) { |
563 | uint64_t npages = (size + (grn_pagesize - 1)) / grn_pagesize; |
564 | size_t aligned_size; |
565 | if (npages >= (1LL<<32)) { |
566 | MERR("too long request size=%" GRN_FMT_SIZE, size); |
567 | return NULL; |
568 | } |
569 | for (;;) { |
570 | if (++i >= GRN_CTX_N_SEGMENTS) { |
571 | MERR("all segments are full" ); |
572 | return NULL; |
573 | } |
574 | mi++; |
575 | if (!mi->map) { break; } |
576 | } |
577 | aligned_size = grn_pagesize * ((size_t)npages); |
578 | if (!grn_io_anon_map(ctx, mi, aligned_size)) { return NULL; } |
579 | mi->nref = (uint32_t) npages; |
580 | mi->count = GRN_CTX_SEGMENT_VLEN|GRN_CTX_SEGMENT_LIFO; |
581 | ctx->impl->lifoseg = i; |
582 | return mi->map; |
583 | } else { |
584 | size = (size + ALIGN_MASK) & ~ALIGN_MASK; |
585 | if (i < 0 || (mi->count & GRN_CTX_SEGMENT_VLEN) || size + mi->nref > GRN_CTX_SEGMENT_SIZE) { |
586 | for (;;) { |
587 | if (++i >= GRN_CTX_N_SEGMENTS) { |
588 | MERR("all segments are full" ); |
589 | return NULL; |
590 | } |
591 | if (!(++mi)->map) { break; } |
592 | } |
593 | if (!grn_io_anon_map(ctx, mi, GRN_CTX_SEGMENT_SIZE)) { return NULL; } |
594 | mi->nref = 0; |
595 | mi->count = GRN_CTX_SEGMENT_WORD|GRN_CTX_SEGMENT_LIFO; |
596 | ctx->impl->lifoseg = i; |
597 | } |
598 | { |
599 | uint32_t u = mi->nref; |
600 | mi->nref += size; |
601 | return (byte *)mi->map + u; |
602 | } |
603 | } |
604 | } |
605 | } |
606 | |
607 | void |
608 | grn_ctx_free_lifo(grn_ctx *ctx, void *ptr, |
609 | const char* file, int line, const char *func) |
610 | { |
611 | if (!ctx) { return; } |
612 | if (!ctx->impl) { |
613 | ERR(GRN_INVALID_ARGUMENT,"ctx without impl passed." ); |
614 | return; |
615 | } |
616 | { |
617 | int32_t i = ctx->impl->lifoseg, done = 0; |
618 | grn_io_mapinfo *mi = &ctx->impl->segs[i]; |
619 | if (i < 0) { |
620 | ERR(GRN_INVALID_ARGUMENT, "lifo buffer is void" ); |
621 | return; |
622 | } |
623 | for (; i >= 0; i--, mi--) { |
624 | if (!(mi->count & GRN_CTX_SEGMENT_LIFO)) { continue; } |
625 | if (done) { break; } |
626 | if (mi->count & GRN_CTX_SEGMENT_VLEN) { |
627 | if (mi->map == ptr) { done = 1; } |
628 | grn_io_anon_unmap(ctx, mi, mi->nref * grn_pagesize); |
629 | mi->map = NULL; |
630 | } else { |
631 | if (mi->map == ptr) { |
632 | done = 1; |
633 | } else { |
634 | if (mi->map < ptr && ptr < (void *)((byte*)mi->map + mi->nref)) { |
635 | mi->nref = (uint32_t) ((uintptr_t)ptr - (uintptr_t)mi->map); |
636 | break; |
637 | } |
638 | } |
639 | grn_io_anon_unmap(ctx, mi, GRN_CTX_SEGMENT_SIZE); |
640 | mi->map = NULL; |
641 | } |
642 | } |
643 | ctx->impl->lifoseg = i; |
644 | } |
645 | } |
646 | |
647 | #if defined(USE_DYNAMIC_MALLOC_CHANGE) |
648 | grn_malloc_func |
649 | grn_ctx_get_malloc(grn_ctx *ctx) |
650 | { |
651 | if (!ctx || !ctx->impl) { return NULL; } |
652 | return ctx->impl->malloc_func; |
653 | } |
654 | |
655 | void |
656 | grn_ctx_set_malloc(grn_ctx *ctx, grn_malloc_func malloc_func) |
657 | { |
658 | if (!ctx || !ctx->impl) { return; } |
659 | ctx->impl->malloc_func = malloc_func; |
660 | } |
661 | |
662 | grn_calloc_func |
663 | grn_ctx_get_calloc(grn_ctx *ctx) |
664 | { |
665 | if (!ctx || !ctx->impl) { return NULL; } |
666 | return ctx->impl->calloc_func; |
667 | } |
668 | |
669 | void |
670 | grn_ctx_set_calloc(grn_ctx *ctx, grn_calloc_func calloc_func) |
671 | { |
672 | if (!ctx || !ctx->impl) { return; } |
673 | ctx->impl->calloc_func = calloc_func; |
674 | } |
675 | |
676 | grn_realloc_func |
677 | grn_ctx_get_realloc(grn_ctx *ctx) |
678 | { |
679 | if (!ctx || !ctx->impl) { return NULL; } |
680 | return ctx->impl->realloc_func; |
681 | } |
682 | |
683 | void |
684 | grn_ctx_set_realloc(grn_ctx *ctx, grn_realloc_func realloc_func) |
685 | { |
686 | if (!ctx || !ctx->impl) { return; } |
687 | ctx->impl->realloc_func = realloc_func; |
688 | } |
689 | |
690 | grn_strdup_func |
691 | grn_ctx_get_strdup(grn_ctx *ctx) |
692 | { |
693 | if (!ctx || !ctx->impl) { return NULL; } |
694 | return ctx->impl->strdup_func; |
695 | } |
696 | |
697 | void |
698 | grn_ctx_set_strdup(grn_ctx *ctx, grn_strdup_func strdup_func) |
699 | { |
700 | if (!ctx || !ctx->impl) { return; } |
701 | ctx->impl->strdup_func = strdup_func; |
702 | } |
703 | |
704 | grn_free_func |
705 | grn_ctx_get_free(grn_ctx *ctx) |
706 | { |
707 | if (!ctx || !ctx->impl) { return NULL; } |
708 | return ctx->impl->free_func; |
709 | } |
710 | |
711 | void |
712 | grn_ctx_set_free(grn_ctx *ctx, grn_free_func free_func) |
713 | { |
714 | if (!ctx || !ctx->impl) { return; } |
715 | ctx->impl->free_func = free_func; |
716 | } |
717 | |
718 | void * |
719 | grn_malloc(grn_ctx *ctx, size_t size, |
720 | const char* file, int line, const char *func) |
721 | { |
722 | if (ctx && ctx->impl && ctx->impl->malloc_func) { |
723 | return ctx->impl->malloc_func(ctx, size, file, line, func); |
724 | } else { |
725 | return grn_malloc_default(ctx, size, file, line, func); |
726 | } |
727 | } |
728 | |
729 | void * |
730 | grn_calloc(grn_ctx *ctx, size_t size, |
731 | const char* file, int line, const char *func) |
732 | { |
733 | if (ctx && ctx->impl && ctx->impl->calloc_func) { |
734 | return ctx->impl->calloc_func(ctx, size, file, line, func); |
735 | } else { |
736 | return grn_calloc_default(ctx, size, file, line, func); |
737 | } |
738 | } |
739 | |
740 | void * |
741 | grn_realloc(grn_ctx *ctx, void *ptr, size_t size, |
742 | const char* file, int line, const char *func) |
743 | { |
744 | if (ctx && ctx->impl && ctx->impl->realloc_func) { |
745 | return ctx->impl->realloc_func(ctx, ptr, size, file, line, func); |
746 | } else { |
747 | return grn_realloc_default(ctx, ptr, size, file, line, func); |
748 | } |
749 | } |
750 | |
751 | char * |
752 | grn_strdup(grn_ctx *ctx, const char *string, |
753 | const char* file, int line, const char *func) |
754 | { |
755 | if (ctx && ctx->impl && ctx->impl->strdup_func) { |
756 | return ctx->impl->strdup_func(ctx, string, file, line, func); |
757 | } else { |
758 | return grn_strdup_default(ctx, string, file, line, func); |
759 | } |
760 | } |
761 | |
762 | void |
763 | grn_free(grn_ctx *ctx, void *ptr, |
764 | const char* file, int line, const char *func) |
765 | { |
766 | if (ctx && ctx->impl && ctx->impl->free_func) { |
767 | return ctx->impl->free_func(ctx, ptr, file, line, func); |
768 | } else { |
769 | return grn_free_default(ctx, ptr, file, line, func); |
770 | } |
771 | } |
772 | #endif |
773 | |
774 | void * |
775 | grn_malloc_default(grn_ctx *ctx, size_t size, |
776 | const char* file, int line, const char *func) |
777 | { |
778 | if (!ctx) { return NULL; } |
779 | { |
780 | void *res = malloc(size); |
781 | if (res) { |
782 | GRN_ADD_ALLOC_COUNT(1); |
783 | grn_alloc_info_add(res, size, file, line, func); |
784 | } else { |
785 | if (!(res = malloc(size))) { |
786 | MERR("malloc fail (%" GRN_FMT_SIZE ")=%p (%s:%d) <%d>" , |
787 | size, res, file, line, alloc_count); |
788 | } else { |
789 | GRN_ADD_ALLOC_COUNT(1); |
790 | grn_alloc_info_add(res, size, file, line, func); |
791 | } |
792 | } |
793 | return res; |
794 | } |
795 | } |
796 | |
797 | void * |
798 | grn_calloc_default(grn_ctx *ctx, size_t size, |
799 | const char* file, int line, const char *func) |
800 | { |
801 | if (!ctx) { return NULL; } |
802 | { |
803 | void *res = calloc(size, 1); |
804 | if (res) { |
805 | GRN_ADD_ALLOC_COUNT(1); |
806 | grn_alloc_info_add(res, size, file, line, func); |
807 | } else { |
808 | if (!(res = calloc(size, 1))) { |
809 | MERR("calloc fail (%" GRN_FMT_SIZE ")=%p (%s:%d) <%d>" , |
810 | size, res, file, line, alloc_count); |
811 | } else { |
812 | GRN_ADD_ALLOC_COUNT(1); |
813 | grn_alloc_info_add(res, size, file, line, func); |
814 | } |
815 | } |
816 | return res; |
817 | } |
818 | } |
819 | |
820 | void |
821 | grn_free_default(grn_ctx *ctx, void *ptr, |
822 | const char* file, int line, const char *func) |
823 | { |
824 | if (!ctx) { return; } |
825 | grn_alloc_info_check(ctx, ptr); |
826 | { |
827 | free(ptr); |
828 | if (ptr) { |
829 | GRN_ADD_ALLOC_COUNT(-1); |
830 | } else { |
831 | GRN_LOG(ctx, GRN_LOG_ALERT, "free fail (%p) (%s:%d) <%d>" , |
832 | ptr, file, line, alloc_count); |
833 | } |
834 | } |
835 | } |
836 | |
837 | void * |
838 | grn_realloc_default(grn_ctx *ctx, void *ptr, size_t size, |
839 | const char* file, int line, const char *func) |
840 | { |
841 | void *res; |
842 | if (!ctx) { return NULL; } |
843 | if (size) { |
844 | if (!(res = realloc(ptr, size))) { |
845 | if (!(res = realloc(ptr, size))) { |
846 | MERR("realloc fail (%p,%" GRN_FMT_SIZE ")=%p (%s:%d) <%d>" , |
847 | ptr, size, res, file, line, alloc_count); |
848 | return NULL; |
849 | } |
850 | } |
851 | if (ptr) { |
852 | grn_alloc_info_change(ptr, res, size); |
853 | } else { |
854 | GRN_ADD_ALLOC_COUNT(1); |
855 | grn_alloc_info_add(res, size, file, line, func); |
856 | } |
857 | } else { |
858 | if (!ptr) { return NULL; } |
859 | grn_alloc_info_check(ctx, ptr); |
860 | GRN_ADD_ALLOC_COUNT(-1); |
861 | free(ptr); |
862 | res = NULL; |
863 | } |
864 | return res; |
865 | } |
866 | |
867 | int |
868 | grn_alloc_count(void) |
869 | { |
870 | return alloc_count; |
871 | } |
872 | |
873 | char * |
874 | grn_strdup_default(grn_ctx *ctx, const char *s, |
875 | const char* file, int line, const char *func) |
876 | { |
877 | if (!ctx) { return NULL; } |
878 | { |
879 | char *res = grn_strdup_raw(s); |
880 | if (res) { |
881 | GRN_ADD_ALLOC_COUNT(1); |
882 | grn_alloc_info_add(res, strlen(res) + 1, file, line, func); |
883 | } else { |
884 | if (!(res = grn_strdup_raw(s))) { |
885 | MERR("strdup(%p)=%p (%s:%d) <%d>" , s, res, file, line, alloc_count); |
886 | } else { |
887 | GRN_ADD_ALLOC_COUNT(1); |
888 | grn_alloc_info_add(res, strlen(res) + 1, file, line, func); |
889 | } |
890 | } |
891 | return res; |
892 | } |
893 | } |
894 | |
895 | #ifdef USE_FAIL_MALLOC |
896 | int |
897 | grn_fail_malloc_check(size_t size, |
898 | const char *file, int line, const char *func) |
899 | { |
900 | if ((grn_fmalloc_file && strcmp(file, grn_fmalloc_file)) || |
901 | (grn_fmalloc_line && line != grn_fmalloc_line) || |
902 | (grn_fmalloc_func && strcmp(func, grn_fmalloc_func))) { |
903 | return 1; |
904 | } |
905 | if (grn_fmalloc_prob && grn_fmalloc_prob >= rand()) { |
906 | return 0; |
907 | } |
908 | return 1; |
909 | } |
910 | |
911 | void * |
912 | grn_malloc_fail(grn_ctx *ctx, size_t size, |
913 | const char* file, int line, const char *func) |
914 | { |
915 | if (grn_fail_malloc_check(size, file, line, func)) { |
916 | return grn_malloc_default(ctx, size, file, line, func); |
917 | } else { |
918 | MERR("fail_malloc (%" GRN_FMT_SIZE ") (%s:%d@%s) <%d>" , |
919 | size, file, line, func, alloc_count); |
920 | return NULL; |
921 | } |
922 | } |
923 | |
924 | void * |
925 | grn_calloc_fail(grn_ctx *ctx, size_t size, |
926 | const char* file, int line, const char *func) |
927 | { |
928 | if (grn_fail_malloc_check(size, file, line, func)) { |
929 | return grn_calloc_default(ctx, size, file, line, func); |
930 | } else { |
931 | MERR("fail_calloc (%" GRN_FMT_SIZE ") (%s:%d@%s) <%d>" , |
932 | size, file, line, func, alloc_count); |
933 | return NULL; |
934 | } |
935 | } |
936 | |
937 | void * |
938 | grn_realloc_fail(grn_ctx *ctx, void *ptr, size_t size, |
939 | const char* file, int line, const char *func) |
940 | { |
941 | if (grn_fail_malloc_check(size, file, line, func)) { |
942 | return grn_realloc_default(ctx, ptr, size, file, line, func); |
943 | } else { |
944 | MERR("fail_realloc (%p,%" GRN_FMT_SIZE ") (%s:%d@%s) <%d>" , |
945 | ptr, size, file, line, func, alloc_count); |
946 | return NULL; |
947 | } |
948 | } |
949 | |
950 | char * |
951 | grn_strdup_fail(grn_ctx *ctx, const char *s, |
952 | const char* file, int line, const char *func) |
953 | { |
954 | if (grn_fail_malloc_check(strlen(s), file, line, func)) { |
955 | return grn_strdup_default(ctx, s, file, line, func); |
956 | } else { |
957 | MERR("fail_strdup(%p) (%s:%d@%s) <%d>" , s, file, line, func, alloc_count); |
958 | return NULL; |
959 | } |
960 | } |
961 | #endif /* USE_FAIL_MALLOC */ |
962 | |