/src/gpac/src/utils/alloc.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Romain Bouqueau - Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2010-2023 |
6 | | * All rights reserved |
7 | | * |
8 | | * This file is part of GPAC / common tools sub-project |
9 | | * |
10 | | * GPAC is free software; you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2, or (at your option) |
13 | | * any later version. |
14 | | * |
15 | | * GPAC is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public |
21 | | * License along with this library; see the file COPYING. If not, write to |
22 | | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
23 | | * |
24 | | */ |
25 | | |
26 | | #if defined(__GNUC__) && __GNUC__ >= 4 |
27 | | #define _GNU_SOURCE |
28 | | #endif |
29 | | #include <stdio.h> |
30 | | #include <stdarg.h> |
31 | | #include <string.h> |
32 | | |
33 | | |
34 | | #define STD_MALLOC 0 |
35 | | #define GOOGLE_MALLOC 1 |
36 | | #define INTEL_MALLOC 2 |
37 | | #define DL_MALLOC 3 |
38 | | |
39 | | #ifdef WIN32 |
40 | | #define USE_MALLOC STD_MALLOC |
41 | | #else |
42 | | #define USE_MALLOC STD_MALLOC |
43 | | #endif |
44 | | |
45 | | |
46 | | #if defined(_WIN32_WCE) && !defined(strdup) |
47 | | #define strdup _strdup |
48 | | #endif |
49 | | |
50 | | /* |
51 | | WARNING - you must enable C++ style compilation of this file (error.c) to be able to compile |
52 | | with google malloc. This is not set by default in the project settings. |
53 | | */ |
54 | | #if (USE_MALLOC==GOOGLE_MALLOC) |
55 | | #include <config.h> |
56 | | #include <base/commandlineflags.h> |
57 | | #include <google/malloc_extension.h> |
58 | | |
59 | | #ifdef WIN32 |
60 | | #pragma comment(lib, "libtcmalloc_minimal") |
61 | | #endif |
62 | | |
63 | | #define MALLOC malloc |
64 | | #define CALLOC calloc |
65 | | #define REALLOC realloc |
66 | | #define FREE free |
67 | | #define STRDUP(a) return strdup(a); |
68 | | |
69 | | /*we must use c++ compiler for google malloc :( */ |
70 | | #define CDECL extern "C" |
71 | | #endif |
72 | | |
73 | | #if (USE_MALLOC==INTEL_MALLOC) |
74 | | #define CDECL |
75 | | CDECL void * scalable_malloc(size_t size); |
76 | | CDECL void * scalable_realloc(void* ptr, size_t size); |
77 | | CDECL void * scalable_calloc(size_t num, size_t size); |
78 | | CDECL void scalable_free(void* ptr); |
79 | | |
80 | | #ifdef WIN32 |
81 | | #pragma comment(lib, "tbbmalloc.lib") |
82 | | #endif |
83 | | |
84 | | #define MALLOC scalable_malloc |
85 | | #define CALLOC scalable_calloc |
86 | | #define REALLOC scalable_realloc |
87 | | #define FREE scalable_free |
88 | | #define STRDUP(_a) if (_a) { unsigned int len = strlen(_a)+1; char *ptr = (char *) scalable_malloc(len); strcpy(ptr, _a); return ptr; } else { return NULL; } |
89 | | |
90 | | #endif |
91 | | |
92 | | #ifndef CDECL |
93 | | #define CDECL |
94 | | #endif |
95 | | |
96 | | #ifndef SYMBOL_EXPORT |
97 | | #if defined(__GNUC__) && __GNUC__ >= 4 |
98 | | #define SYMBOL_EXPORT __attribute__((visibility("default"))) |
99 | | #endif |
100 | | #endif |
101 | | |
102 | | #if (USE_MALLOC==DL_MALLOC) |
103 | | |
104 | | CDECL void * dlmalloc(size_t size); |
105 | | CDECL void * dlrealloc(void* ptr, size_t size); |
106 | | CDECL void * dlcalloc(size_t num, size_t size); |
107 | | CDECL void dlfree(void* ptr); |
108 | | |
109 | | #define MALLOC dlmalloc |
110 | | #define CALLOC dlcalloc |
111 | | #define REALLOC dlrealloc |
112 | | #define FREE dlfree |
113 | | #define STRDUP(_a) if (_a) { unsigned int len = strlen(_a)+1; char *ptr = (char *) dlmalloc(len); strcpy(ptr, _a); return ptr; } else { return NULL; } |
114 | | |
115 | | #endif |
116 | | |
117 | | #if (USE_MALLOC==STD_MALLOC) |
118 | | |
119 | | #include <stdlib.h> |
120 | | |
121 | 91.4M | #define MALLOC malloc |
122 | 107k | #define CALLOC calloc |
123 | 19.8M | #define REALLOC realloc |
124 | 107M | #define FREE free |
125 | 1.11M | #define STRDUP(a) return strdup(a); |
126 | | |
127 | | #endif |
128 | | |
129 | | |
130 | | |
131 | | #ifndef _WIN32_WCE |
132 | | #include <assert.h> |
133 | | #endif |
134 | | |
135 | | /*This is to handle cases where config.h is generated at the root of the gpac build tree (./configure) |
136 | | This is only needed when building libgpac and modules when libgpac is not installed*/ |
137 | | #ifdef GPAC_HAVE_CONFIG_H |
138 | | # include "config.h" |
139 | | #else |
140 | | # include <gpac/configuration.h> |
141 | | #endif |
142 | | |
143 | | /*GPAC memory tracking*/ |
144 | | #ifndef GPAC_MEMORY_TRACKING |
145 | | |
146 | | #include <gpac/setup.h> |
147 | | GF_EXPORT |
148 | | void *gf_malloc(size_t size) |
149 | 91.4M | { |
150 | 91.4M | return MALLOC(size); |
151 | 91.4M | } |
152 | | GF_EXPORT |
153 | | void *gf_calloc(size_t num, size_t size_of) |
154 | 107k | { |
155 | 107k | return CALLOC(num, size_of); |
156 | 107k | } |
157 | | GF_EXPORT |
158 | | void *gf_realloc(void *ptr, size_t size) |
159 | 19.8M | { |
160 | 19.8M | return REALLOC(ptr, size); |
161 | 19.8M | } |
162 | | GF_EXPORT |
163 | | void gf_free(void *ptr) |
164 | 107M | { |
165 | 107M | FREE(ptr); |
166 | 107M | } |
167 | | GF_EXPORT |
168 | | char *gf_strdup(const char *str) |
169 | 1.11M | { |
170 | 1.11M | STRDUP(str); |
171 | 0 | } |
172 | | |
173 | | #else /*GPAC_MEMORY_TRACKING**/ |
174 | | |
175 | | |
176 | | static void gf_memory_log(unsigned int level, const char *fmt, ...); |
177 | | enum |
178 | | { |
179 | | /*! Disable all Log message*/ |
180 | | GF_MEMORY_QUIET = 0, |
181 | | /*! Log message describes an error*/ |
182 | | GF_MEMORY_ERROR = 1, |
183 | | /*! Log message describes a warning*/ |
184 | | GF_MEMORY_WARNING, |
185 | | /*! Log message is informational (state, etc..)*/ |
186 | | GF_MEMORY_INFO, |
187 | | /*! Log message is a debug info*/ |
188 | | GF_MEMORY_DEBUG, |
189 | | }; |
190 | | |
191 | | |
192 | | size_t gpac_allocated_memory = 0; |
193 | | size_t gpac_nb_alloc_blocs = 0; |
194 | | |
195 | | //defs for gf_assert |
196 | | #include <gpac/setup.h> |
197 | | |
198 | | //backtrace not supported on these platforms |
199 | | #if defined(GPAC_CONFIG_IOS) || defined(GPAC_CONFIG_ANDROID) |
200 | | #define GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE |
201 | | #endif |
202 | | |
203 | | int gf_mem_track_enabled = 0; |
204 | | |
205 | | #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE |
206 | | static int gf_mem_backtrace_enabled = 0; |
207 | | |
208 | | /*malloc dynamic storage needed for each alloc is STACK_PRINT_SIZE*SYMBOL_MAX_SIZE+1, keep them small!*/ |
209 | | #define STACK_FIRST_IDX 5 //remove the gpac memory allocator self trace |
210 | | #define STACK_PRINT_SIZE 10 |
211 | | |
212 | | #ifdef WIN32 |
213 | | #define SYMBOL_MAX_SIZE 50 |
214 | | #include <windows.h> |
215 | | /* on visual studio 2015 windows sdk 8.1 dbghelp has a typedef enum with no name that throws a warning */ |
216 | | #if !defined(__GNUC__) |
217 | | #pragma warning(disable: 4091) |
218 | | #endif |
219 | | #include <dbghelp.h> |
220 | | #if !defined(__GNUC__) |
221 | | #pragma comment(lib, "dbghelp.lib") |
222 | | #endif |
223 | | /*memory ownership to the caller*/ |
224 | | static void store_backtrace(char *s_backtrace) |
225 | | { |
226 | | void *stack[STACK_PRINT_SIZE]; |
227 | | size_t i, frames, bt_idx = 0; |
228 | | SYMBOL_INFO *symbol; |
229 | | HANDLE process; |
230 | | |
231 | | process = GetCurrentProcess(); |
232 | | SymInitialize(process, NULL, TRUE); |
233 | | |
234 | | symbol = (SYMBOL_INFO*)_alloca(sizeof(SYMBOL_INFO) + SYMBOL_MAX_SIZE); |
235 | | symbol->MaxNameLen = SYMBOL_MAX_SIZE-1; |
236 | | symbol->SizeOfStruct = sizeof(SYMBOL_INFO); |
237 | | |
238 | | frames = CaptureStackBackTrace(STACK_FIRST_IDX, STACK_PRINT_SIZE, stack, NULL); |
239 | | |
240 | | for (i=0; i<frames; i++) { |
241 | | int len; |
242 | | int bt_len; |
243 | | char *symbol_name = "unresolved"; |
244 | | SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol); |
245 | | if (symbol->Name) symbol_name = (char*)symbol->Name; |
246 | | |
247 | | bt_len = (int) strlen(symbol_name) + 10; |
248 | | if (bt_idx + bt_len > STACK_PRINT_SIZE*SYMBOL_MAX_SIZE) { |
249 | | gf_memory_log(GF_MEMORY_WARNING, "[MemoryInfo] Not enough space to hold backtrace - truncating\n"); |
250 | | break; |
251 | | } |
252 | | |
253 | | len = _snprintf(s_backtrace+bt_idx, SYMBOL_MAX_SIZE-1, "\t%02u 0x%I64X %s", (unsigned int) (frames-i-1), symbol->Address, symbol_name); |
254 | | if (len<0) len = SYMBOL_MAX_SIZE-1; |
255 | | s_backtrace[bt_idx+len]='\n'; |
256 | | bt_idx += (len+1); |
257 | | } |
258 | | gf_assert(bt_idx < STACK_PRINT_SIZE*SYMBOL_MAX_SIZE); |
259 | | s_backtrace[bt_idx-1] = '\0'; |
260 | | } |
261 | | |
262 | | #else /*WIN32*/ |
263 | | |
264 | | #define SYMBOL_MAX_SIZE 100 |
265 | | |
266 | | #ifndef GPAC_CONFIG_EMSCRIPTEN |
267 | | #include <execinfo.h> |
268 | | #endif |
269 | | |
270 | | /*memory ownership to the caller*/ |
271 | | static void store_backtrace(char *s_backtrace) |
272 | | { |
273 | | #ifndef GPAC_CONFIG_EMSCRIPTEN |
274 | | size_t i, size, bt_idx=0; |
275 | | void *stack[STACK_PRINT_SIZE+STACK_FIRST_IDX]; |
276 | | char **messages; |
277 | | |
278 | | size = backtrace(stack, STACK_PRINT_SIZE+STACK_FIRST_IDX); |
279 | | messages = backtrace_symbols(stack, size); |
280 | | |
281 | | for (i=STACK_FIRST_IDX; i<size && messages!=NULL; ++i) { |
282 | | int bt_len = strlen(messages[i]) + 10; |
283 | | int len; |
284 | | |
285 | | if (bt_idx + bt_len > STACK_PRINT_SIZE*SYMBOL_MAX_SIZE) { |
286 | | gf_memory_log(GF_MEMORY_WARNING, "[MemoryInfo] Not enough space to hold backtrace - truncating\n"); |
287 | | break; |
288 | | } |
289 | | |
290 | | len = snprintf(s_backtrace+bt_idx, SYMBOL_MAX_SIZE-1, "\t%02zu %s", i, messages[i]); |
291 | | if (len<0) len = SYMBOL_MAX_SIZE-1; |
292 | | s_backtrace[bt_idx+len]='\n'; |
293 | | bt_idx += (len+1); |
294 | | |
295 | | } |
296 | | gf_assert(bt_idx < STACK_PRINT_SIZE*SYMBOL_MAX_SIZE); |
297 | | s_backtrace[bt_idx-1] = '\0'; |
298 | | free(messages); |
299 | | #endif |
300 | | } |
301 | | #endif /*WIN32*/ |
302 | | |
303 | | |
304 | | #endif /*GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE*/ |
305 | | |
306 | | |
307 | | static void register_address(void *ptr, size_t size, const char *filename, int line); |
308 | | static int unregister_address(void *ptr, const char *filename, int line); |
309 | | |
310 | | |
311 | | static void *gf_mem_malloc_basic(size_t size, const char *filename, int line) |
312 | | { |
313 | | return MALLOC(size); |
314 | | } |
315 | | static void *gf_mem_calloc_basic(size_t num, size_t size_of, const char *filename, int line) |
316 | | { |
317 | | return CALLOC(num, size_of); |
318 | | } |
319 | | static void *gf_mem_realloc_basic(void *ptr, size_t size, const char *filename, int line) |
320 | | { |
321 | | return REALLOC(ptr, size); |
322 | | } |
323 | | static void gf_mem_free_basic(void *ptr, const char *filename, int line) |
324 | | { |
325 | | FREE(ptr); |
326 | | } |
327 | | static char *gf_mem_strdup_basic(const char *str, const char *filename, int line) |
328 | | { |
329 | | STRDUP(str); |
330 | | } |
331 | | |
332 | | static unsigned int nb_calls_alloc = 0; |
333 | | static unsigned int nb_calls_calloc = 0; |
334 | | static unsigned int nb_calls_realloc = 0; |
335 | | static unsigned int nb_calls_free = 0; |
336 | | |
337 | | void *gf_mem_malloc_tracker(size_t size, const char *filename, int line) |
338 | | { |
339 | | void *ptr = MALLOC(size); |
340 | | if (!ptr) { |
341 | | gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] malloc() has returned a NULL pointer\n"); |
342 | | gf_assert(0); |
343 | | } else { |
344 | | register_address(ptr, size, filename, line); |
345 | | } |
346 | | gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] malloc %3d bytes at %p in:\n", size, ptr); |
347 | | gf_memory_log(GF_MEMORY_DEBUG, " file %s at line %d\n" , filename, line); |
348 | | nb_calls_alloc++; |
349 | | return ptr; |
350 | | } |
351 | | |
352 | | void *gf_mem_calloc_tracker(size_t num, size_t size_of, const char *filename, int line) |
353 | | { |
354 | | size_t size = num*size_of; |
355 | | void *ptr = CALLOC(num, size_of); |
356 | | if (!ptr) { |
357 | | gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] calloc() has returned a NULL pointer\n"); |
358 | | gf_assert(0); |
359 | | } else { |
360 | | register_address(ptr, size, filename, line); |
361 | | } |
362 | | gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] calloc %3d bytes at %p in:\n", ptr, size); |
363 | | gf_memory_log(GF_MEMORY_DEBUG, " file %s at line %d\n" , filename, line); |
364 | | nb_calls_calloc++; |
365 | | return ptr; |
366 | | } |
367 | | |
368 | | void gf_mem_free_tracker(void *ptr, const char *filename, int line) |
369 | | { |
370 | | int size_prev; |
371 | | if (ptr && (size_prev=unregister_address(ptr, filename, line))) { |
372 | | gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] free %3d bytes at %p in:\n", size_prev, ptr); |
373 | | gf_memory_log(GF_MEMORY_DEBUG, " file %s at line %d\n" , filename, line); |
374 | | FREE(ptr); |
375 | | } |
376 | | nb_calls_free++; |
377 | | } |
378 | | |
379 | | void *gf_mem_realloc_tracker(void *ptr, size_t size, const char *filename, int line) |
380 | | { |
381 | | void *ptr_g; |
382 | | int size_prev; |
383 | | if (!ptr) { |
384 | | gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] realloc() from a null pointer: calling malloc() instead\n"); |
385 | | return gf_mem_malloc_tracker(size, filename, line); |
386 | | } |
387 | | /*a) The return value is NULL if the size is zero and the buffer argument is not NULL. In this case, the original block is freed.*/ |
388 | | if (!size) { |
389 | | gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] realloc() with a null size: calling free() instead\n"); |
390 | | gf_mem_free_tracker(ptr, filename, line); |
391 | | return NULL; |
392 | | } |
393 | | size_prev = unregister_address(ptr, filename, line); |
394 | | ptr_g = REALLOC(ptr, size); |
395 | | if (!ptr_g) { |
396 | | /*b) The return value is NULL if there is not enough available memory to expand the block to the given size. In this case, the original block is unchanged.*/ |
397 | | gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] realloc() has returned a NULL pointer\n"); |
398 | | register_address(ptr, size_prev, filename, line); |
399 | | gf_assert(0); |
400 | | } else { |
401 | | register_address(ptr_g, size, filename, line); |
402 | | // gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] realloc %3d (instead of %3d) bytes at %p (instead of %p)\n", size, size_prev, ptr_g, ptr); |
403 | | gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] realloc %3d (instead of %3d) bytes at %p\n", size, size_prev, ptr_g); |
404 | | gf_memory_log(GF_MEMORY_DEBUG, " file %s at line %d\n" , filename, line); |
405 | | } |
406 | | nb_calls_realloc++; |
407 | | return ptr_g; |
408 | | } |
409 | | |
410 | | char *gf_mem_strdup_tracker(const char *str, const char *filename, int line) |
411 | | { |
412 | | char *ptr; |
413 | | if (!str) return NULL; |
414 | | ptr = (char*)gf_mem_malloc_tracker(strlen(str)+1, filename, line); |
415 | | strcpy(ptr, str); |
416 | | return ptr; |
417 | | } |
418 | | |
419 | | |
420 | | static void *(*gf_mem_malloc_proto)(size_t size, const char *filename, int line) = gf_mem_malloc_basic; |
421 | | static void *(*gf_mem_calloc_proto)(size_t num, size_t size_of, const char *filename, int line) = gf_mem_calloc_basic; |
422 | | static void *(*gf_mem_realloc_proto)(void *ptr, size_t size, const char *filename, int line) = gf_mem_realloc_basic; |
423 | | static void (*gf_mem_free_proto)(void *ptr, const char *filename, int line) = gf_mem_free_basic; |
424 | | static char *(*gf_mem_strdup_proto)(const char *str, const char *filename, int line) = gf_mem_strdup_basic; |
425 | | |
426 | | #ifndef MY_GF_EXPORT |
427 | | #if defined(__GNUC__) && __GNUC__ >= 4 |
428 | | #define MY_GF_EXPORT __attribute__((visibility("default"))) |
429 | | #else |
430 | | /*use def files for windows or let compiler decide*/ |
431 | | #define MY_GF_EXPORT |
432 | | #endif |
433 | | #endif |
434 | | |
435 | | MY_GF_EXPORT void *gf_mem_malloc(size_t size, const char *filename, int line) |
436 | | { |
437 | | return gf_mem_malloc_proto(size, filename, line); |
438 | | } |
439 | | |
440 | | MY_GF_EXPORT void *gf_mem_calloc(size_t num, size_t size_of, const char *filename, int line) |
441 | | { |
442 | | return gf_mem_calloc_proto(num, size_of, filename, line); |
443 | | } |
444 | | |
445 | | MY_GF_EXPORT |
446 | | void *gf_mem_realloc(void *ptr, size_t size, const char *filename, int line) |
447 | | { |
448 | | return gf_mem_realloc_proto(ptr, size, filename, line); |
449 | | } |
450 | | |
451 | | MY_GF_EXPORT |
452 | | void gf_mem_free(void *ptr, const char *filename, int line) |
453 | | { |
454 | | gf_mem_free_proto(ptr, filename, line); |
455 | | } |
456 | | |
457 | | MY_GF_EXPORT |
458 | | char *gf_mem_strdup(const char *str, const char *filename, int line) |
459 | | { |
460 | | return gf_mem_strdup_proto(str, filename, line); |
461 | | } |
462 | | |
463 | | void gf_mem_enable_tracker(unsigned int mem_track_type) |
464 | | { |
465 | | if (mem_track_type) { |
466 | | #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE |
467 | | gf_mem_backtrace_enabled = (mem_track_type==2) ? 1 : 0; |
468 | | #endif |
469 | | gf_mem_track_enabled = 1; |
470 | | gf_mem_malloc_proto = gf_mem_malloc_tracker; |
471 | | gf_mem_calloc_proto = gf_mem_calloc_tracker; |
472 | | gf_mem_realloc_proto = gf_mem_realloc_tracker; |
473 | | gf_mem_free_proto = gf_mem_free_tracker; |
474 | | gf_mem_strdup_proto = gf_mem_strdup_tracker; |
475 | | } else { |
476 | | #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE |
477 | | gf_mem_backtrace_enabled = 0; |
478 | | #endif |
479 | | gf_mem_track_enabled = 0; |
480 | | gf_mem_malloc_proto = gf_mem_malloc_basic; |
481 | | gf_mem_calloc_proto = gf_mem_calloc_basic; |
482 | | gf_mem_realloc_proto = gf_mem_realloc_basic; |
483 | | gf_mem_free_proto = gf_mem_free_basic; |
484 | | gf_mem_strdup_proto = gf_mem_strdup_basic; |
485 | | } |
486 | | } |
487 | | |
488 | | size_t gf_mem_get_stats(unsigned int *nb_allocs, unsigned int *nb_callocs, unsigned int *nb_reallocs, unsigned int *nb_free) |
489 | | { |
490 | | if (nb_allocs) (*nb_allocs) = nb_calls_alloc; |
491 | | if (nb_callocs) (*nb_callocs) = nb_calls_calloc; |
492 | | if (nb_reallocs) (*nb_reallocs) = nb_calls_realloc; |
493 | | if (nb_free) (*nb_free) = nb_calls_free; |
494 | | return gpac_allocated_memory; |
495 | | } |
496 | | |
497 | | typedef struct s_memory_element |
498 | | { |
499 | | void *ptr; |
500 | | unsigned int size; |
501 | | struct s_memory_element *next; |
502 | | #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE |
503 | | char *backtrace_stack; |
504 | | #endif |
505 | | int line; |
506 | | char *filename; |
507 | | } memory_element; |
508 | | |
509 | | /*pointer to the first element of the list*/ |
510 | | typedef memory_element** memory_list; |
511 | | |
512 | | |
513 | | #define HASH_ENTRIES 4096 |
514 | | |
515 | | #if !defined(WIN32) |
516 | | #include <stdint.h> |
517 | | #endif |
518 | | |
519 | | static unsigned int gf_memory_hash(void *ptr) |
520 | | { |
521 | | #if defined(WIN32) |
522 | | return (unsigned int) ( (((unsigned __int64)ptr>>4)+(unsigned __int64)ptr) % HASH_ENTRIES ); |
523 | | #else |
524 | | return (unsigned int) ( (((uint64_t) ((intptr_t) ptr)>>4) + (uint64_t) ((intptr_t)ptr) ) % HASH_ENTRIES ); |
525 | | #endif |
526 | | } |
527 | | |
528 | | |
529 | | /*base functions (add, find, del_item, del) are implemented upon a stack model*/ |
530 | | static void gf_memory_add_stack(memory_element **p, void *ptr, unsigned int size, const char *filename, int line) |
531 | | { |
532 | | memory_element *element = (memory_element*)MALLOC(sizeof(memory_element)); |
533 | | if (!element) { |
534 | | gf_memory_log(GF_MEMORY_ERROR, ("[Mem] Fail to register stack for allocation\n")); |
535 | | return; |
536 | | } |
537 | | element->ptr = ptr; |
538 | | element->size = size; |
539 | | element->line = line; |
540 | | |
541 | | #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE |
542 | | if (gf_mem_backtrace_enabled) { |
543 | | element->backtrace_stack = MALLOC(sizeof(char) * STACK_PRINT_SIZE * SYMBOL_MAX_SIZE); |
544 | | if (!element->backtrace_stack) { |
545 | | gf_memory_log(GF_MEMORY_WARNING, ("[Mem] Fail to register backtrace of allocation\n")); |
546 | | element->backtrace_stack = NULL; |
547 | | } else { |
548 | | store_backtrace(element->backtrace_stack); |
549 | | } |
550 | | } else { |
551 | | element->backtrace_stack = NULL; |
552 | | } |
553 | | #endif |
554 | | |
555 | | element->filename = MALLOC(strlen(filename) + 1); |
556 | | if (element->filename) |
557 | | strcpy(element->filename, filename); |
558 | | |
559 | | element->next = *p; |
560 | | *p = element; |
561 | | } |
562 | | |
563 | | /*returns the position of a ptr from a memory_element, 0 if not found*/ |
564 | | static int gf_memory_find_stack(memory_element *p, void *ptr) |
565 | | { |
566 | | int i = 1; |
567 | | memory_element *element = p; |
568 | | while (element) { |
569 | | if (element->ptr == ptr) { |
570 | | return i; |
571 | | } |
572 | | element = element->next; |
573 | | i++; |
574 | | } |
575 | | return 0; |
576 | | } |
577 | | |
578 | | /*returns the size of the deleted item*/ |
579 | | static unsigned int gf_memory_del_item_stack(memory_element **p, void *ptr) |
580 | | { |
581 | | unsigned int size; |
582 | | memory_element *curr_element=*p, *prev_element=NULL; |
583 | | while (curr_element) { |
584 | | if (curr_element->ptr == ptr) { |
585 | | if (prev_element) prev_element->next = curr_element->next; |
586 | | else *p = curr_element->next; |
587 | | size = curr_element->size; |
588 | | #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE |
589 | | if (curr_element->backtrace_stack) { |
590 | | FREE(curr_element->backtrace_stack); |
591 | | } |
592 | | #endif |
593 | | FREE(curr_element); |
594 | | return size; |
595 | | } |
596 | | prev_element = curr_element; |
597 | | curr_element = curr_element->next; |
598 | | } |
599 | | return 0; |
600 | | } |
601 | | |
602 | | /*this list is implemented as a stack to minimise the cost of freeing recent allocations*/ |
603 | | static void gf_memory_add(memory_list *p, void *ptr, unsigned int size, const char *filename, int line) |
604 | | { |
605 | | unsigned int hash; |
606 | | if (!*p) *p = (memory_list) CALLOC(HASH_ENTRIES, sizeof(memory_element*)); |
607 | | gf_fatal_assert(*p); |
608 | | |
609 | | hash = gf_memory_hash(ptr); |
610 | | gf_memory_add_stack(&((*p)[hash]), ptr, size, filename, line); |
611 | | } |
612 | | |
613 | | |
614 | | static int gf_memory_find(memory_list p, void *ptr) |
615 | | { |
616 | | unsigned int hash; |
617 | | gf_assert(p); |
618 | | if (!p) return 0; |
619 | | hash = gf_memory_hash(ptr); |
620 | | return gf_memory_find_stack(p[hash], ptr); |
621 | | } |
622 | | |
623 | | static unsigned int gf_memory_del_item(memory_list *p, void *ptr) |
624 | | { |
625 | | unsigned int hash; |
626 | | unsigned int ret; |
627 | | memory_element **sub_list; |
628 | | if (!*p) *p = (memory_list) CALLOC(HASH_ENTRIES, sizeof(memory_element*)); |
629 | | gf_fatal_assert(*p); |
630 | | hash = gf_memory_hash(ptr); |
631 | | sub_list = &((*p)[hash]); |
632 | | //code does nothing as &((*p)[hash]) always evaluates to true - commenting it |
633 | | // if (!sub_list) return 0; |
634 | | ret = gf_memory_del_item_stack(sub_list, ptr); |
635 | | |
636 | | //code does nothing as &((*p)[i]) always evaluates to true - commenting it |
637 | | #if 0 |
638 | | if (ret && !((*p)[hash])) { |
639 | | /*check for deletion*/ |
640 | | int i; |
641 | | for (i=0; i<HASH_ENTRIES; i++) |
642 | | if (&((*p)[i])) break; |
643 | | if (i==HASH_ENTRIES) { |
644 | | FREE(*p); |
645 | | } |
646 | | } |
647 | | #endif |
648 | | return ret; |
649 | | } |
650 | | |
651 | | |
652 | | |
653 | | #endif /*GPAC_MEMORY_TRACKING*/ |
654 | | |
655 | | |
656 | | #include <gpac/tools.h> |
657 | | |
658 | | |
659 | | /*GPAC memory tracking*/ |
660 | | #ifdef GPAC_MEMORY_TRACKING |
661 | | |
662 | | #include <gpac/thread.h> |
663 | | |
664 | | /*global lists of allocations and deallocations*/ |
665 | | memory_list memory_add = NULL, memory_rem = NULL; |
666 | | GF_Mutex *gpac_allocations_lock = NULL; |
667 | | |
668 | | static void register_address(void *ptr, size_t size, const char *filename, int line) |
669 | | { |
670 | | /*mutex initialization*/ |
671 | | if (gpac_allocations_lock == 0) { |
672 | | gf_assert(!memory_add); |
673 | | gf_assert(!memory_rem); |
674 | | gpac_allocations_lock = (GF_Mutex*)1; /*must be non-null to avoid a recursive infinite call*/ |
675 | | gpac_allocations_lock = gf_mx_new("gpac_allocations_lock"); |
676 | | } |
677 | | else if (gpac_allocations_lock == (void*)1) { |
678 | | /*we're initializing the mutex (ie called by the gf_mx_new() above)*/ |
679 | | return; |
680 | | } |
681 | | |
682 | | /*lock*/ |
683 | | gf_mx_p(gpac_allocations_lock); |
684 | | |
685 | | gf_memory_add(&memory_add, ptr, (unsigned int)size, filename, line); |
686 | | gf_memory_del_item(&memory_rem, ptr); /*the same block can be reallocated, so remove it from the deallocation list*/ |
687 | | |
688 | | /*update stats*/ |
689 | | gpac_allocated_memory += size; |
690 | | gpac_nb_alloc_blocs++; |
691 | | |
692 | | /*gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] register %6d bytes at %p (%8d Bytes in %4d Blocks allocated)\n", size, ptr, gpac_allocated_memory, gpac_nb_alloc_blocs);*/ |
693 | | |
694 | | /*unlock*/ |
695 | | gf_mx_v(gpac_allocations_lock); |
696 | | } |
697 | | |
698 | | void log_backtrace(unsigned int log_level, memory_element *element) |
699 | | { |
700 | | #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE |
701 | | if (gf_mem_backtrace_enabled) { |
702 | | gf_memory_log(log_level, "file %s at line %d\n%s\n", element->filename, element->line, element->backtrace_stack); |
703 | | } else |
704 | | #endif |
705 | | { |
706 | | gf_memory_log(log_level, "file %s at line %d\n", element->filename, element->line); |
707 | | } |
708 | | } |
709 | | |
710 | | |
711 | | #if 0 //unused |
712 | | Bool gf_mem_check_address(void *ptr) |
713 | | { |
714 | | Bool res = GF_TRUE; |
715 | | int pos; |
716 | | |
717 | | if (!gpac_allocations_lock) return res; |
718 | | |
719 | | /*lock*/ |
720 | | gf_mx_p(gpac_allocations_lock); |
721 | | |
722 | | if ( (pos=gf_memory_find(memory_rem, ptr)) ) { |
723 | | int i; |
724 | | unsigned int hash = gf_memory_hash(ptr); |
725 | | memory_element *element = memory_rem[hash]; |
726 | | gf_assert(element); |
727 | | for (i=1; i<pos; i++) |
728 | | element = element->next; |
729 | | gf_assert(element); |
730 | | gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] the block %p was already freed in:\n", ptr); |
731 | | res = GF_FALSE; |
732 | | log_backtrace(GF_MEMORY_ERROR, element); |
733 | | } |
734 | | /*unlock*/ |
735 | | gf_mx_v(gpac_allocations_lock); |
736 | | return res; |
737 | | } |
738 | | #endif |
739 | | |
740 | | /*returns the size of the unregistered block*/ |
741 | | static int unregister_address(void *ptr, const char *filename, int line) |
742 | | { |
743 | | unsigned int size = 0; /*default: failure*/ |
744 | | |
745 | | /*lock*/ |
746 | | gf_mx_p(gpac_allocations_lock); |
747 | | |
748 | | if (!memory_add) { |
749 | | if (!memory_rem) { |
750 | | /*assume we're rather destroying the mutex (ie calling the gf_mx_del() below) |
751 | | than being called by free() before the first allocation occured*/ |
752 | | return 1; |
753 | | /*gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] calling free() before the first allocation occured\n"); |
754 | | gf_assert(0); */ |
755 | | } |
756 | | } else { |
757 | | if (!gf_memory_find(memory_add, ptr)) { |
758 | | int pos; |
759 | | if (!(pos=gf_memory_find(memory_rem, ptr))) { |
760 | | gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] trying to free a never allocated block (%p)\n", ptr); |
761 | | /* gf_assert(0); */ /*don't assert since this is often due to allocations that occured out of gpac (fonts, etc.)*/ |
762 | | } else { |
763 | | int i; |
764 | | unsigned int hash = gf_memory_hash(ptr); |
765 | | memory_element *element = memory_rem[hash]; |
766 | | |
767 | | gf_assert(element); |
768 | | for (i=1; i<pos; i++) |
769 | | element = element->next; |
770 | | gf_assert(element); |
771 | | gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] the block %p trying to be deleted in:\n", ptr); |
772 | | gf_memory_log(GF_MEMORY_ERROR, " file %s at line %d\n", filename, line); |
773 | | gf_memory_log(GF_MEMORY_ERROR, " was already freed in:\n"); |
774 | | log_backtrace(GF_MEMORY_ERROR, element); |
775 | | gf_fatal_assert(0); |
776 | | } |
777 | | } else { |
778 | | size = gf_memory_del_item(&memory_add, ptr); |
779 | | |
780 | | /*update stats*/ |
781 | | gpac_allocated_memory -= size; |
782 | | gpac_nb_alloc_blocs--; |
783 | | |
784 | | /*gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] unregister %6d bytes at %p (%8d bytes in %4d blocks remaining)\n", size, ptr, gpac_allocated_memory, gpac_nb_alloc_blocs); */ |
785 | | |
786 | | /*the allocation list is empty: free the lists to avoid a leak (we should be exiting)*/ |
787 | | if (!memory_add) { |
788 | | gf_assert(!gpac_allocated_memory); |
789 | | gf_assert(!gpac_nb_alloc_blocs); |
790 | | |
791 | | /*we destroy the mutex we own, then we return*/ |
792 | | gf_mx_del(gpac_allocations_lock); |
793 | | gpac_allocations_lock = NULL; |
794 | | |
795 | | gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] the allocated-blocks-list is empty: the freed-blocks-list will be emptied too.\n"); |
796 | | |
797 | | //reset the freed block list |
798 | | memory_list *m_list = &memory_rem; |
799 | | int i; |
800 | | for (i=0; i<HASH_ENTRIES; i++) { |
801 | | memory_element **m_elt = &((*m_list)[i]) ; |
802 | | |
803 | | memory_element *curr_element=*m_elt, *next_element; |
804 | | while (curr_element) { |
805 | | next_element = curr_element->next; |
806 | | #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE |
807 | | if (curr_element->backtrace_stack) { |
808 | | FREE(curr_element->backtrace_stack); |
809 | | } |
810 | | #endif |
811 | | FREE(curr_element); |
812 | | curr_element = next_element; |
813 | | } |
814 | | *m_elt = NULL; |
815 | | } |
816 | | |
817 | | FREE(*m_list); |
818 | | |
819 | | return size; |
820 | | } else { |
821 | | gf_memory_add(&memory_rem, ptr, size, filename, line); |
822 | | } |
823 | | } |
824 | | } |
825 | | |
826 | | /*unlock*/ |
827 | | gf_mx_v(gpac_allocations_lock); |
828 | | |
829 | | return size; |
830 | | } |
831 | | |
832 | | static void gf_memory_log(unsigned int level, const char *fmt, ...) |
833 | | { |
834 | | va_list vl; |
835 | | char msg[1025]; |
836 | | va_start(vl, fmt); |
837 | | vsnprintf(msg, 1024, fmt, vl); |
838 | | msg[1024] = 0; |
839 | | GF_LOG(level, GF_LOG_MEMORY, (msg)); |
840 | | va_end(vl); |
841 | | } |
842 | | |
843 | | /*prints allocations sum-up*/ |
844 | | static void print_memory_size() |
845 | | { |
846 | | GF_LOG(gpac_nb_alloc_blocs ? GF_MEMORY_ERROR : GF_MEMORY_INFO, GF_LOG_MEMORY, ("[MemTracker] Total: %d bytes allocated in %d blocks\n", (u32) gpac_allocated_memory, (u32) gpac_nb_alloc_blocs )); |
847 | | } |
848 | | |
849 | | GF_EXPORT |
850 | | u64 gf_memory_size() |
851 | | { |
852 | | return (u64) gpac_allocated_memory; |
853 | | } |
854 | | |
855 | | /*prints the state of current allocations*/ |
856 | | GF_EXPORT |
857 | | void gf_memory_print() |
858 | | { |
859 | | /*if lists are empty, the mutex is also NULL*/ |
860 | | if (!memory_add) { |
861 | | gf_assert(!gpac_allocations_lock); |
862 | | gf_memory_log(GF_MEMORY_INFO, "[MemTracker] gf_memory_print(): the memory tracker is not initialized, some file handles are not closed.\n"); |
863 | | } else { |
864 | | int i=0; |
865 | | gf_assert(gpac_allocations_lock); |
866 | | const char *enum_open_handles(u32 *idx); |
867 | | u32 nb_handles = gf_file_handles_count(); |
868 | | |
869 | | |
870 | | gf_memory_log(GF_MEMORY_INFO, "\n[MemTracker] Printing the current state of allocations (%d open file handles) :\n", nb_handles); |
871 | | |
872 | | /*lock*/ |
873 | | gf_mx_p(gpac_allocations_lock); |
874 | | for (i=0; i<HASH_ENTRIES; i++) { |
875 | | memory_element *curr_element = memory_add[i], *next_element; |
876 | | while (curr_element) { |
877 | | char szVal[51], *sep; |
878 | | char szHexVal[101]; |
879 | | u32 size, j; |
880 | | next_element = curr_element->next; |
881 | | size = curr_element->size>=50 ? 50 : curr_element->size; |
882 | | for (j=0 ; j<size ; j++) { |
883 | | unsigned char byte = *((unsigned char*)(curr_element->ptr) + j); |
884 | | szVal[j] = (byte > 31 && byte < 127) ? byte : '.'; |
885 | | sprintf(szHexVal+2*j, "%02X", byte); |
886 | | } |
887 | | szVal[size] = 0; |
888 | | sep = strchr(szVal, '%'); |
889 | | if (sep) sep[0] = 0; |
890 | | szHexVal[2*size] = 0; |
891 | | gf_memory_log(GF_MEMORY_INFO, "[MemTracker] Memory Block %p (size %d) allocated in:\n", curr_element->ptr, curr_element->size); |
892 | | log_backtrace(GF_MEMORY_INFO, curr_element); |
893 | | gf_memory_log(GF_MEMORY_INFO, " string dump: %s\n", szVal); |
894 | | gf_memory_log(GF_MEMORY_INFO, " hex dump: %s\n", szHexVal); |
895 | | curr_element = next_element; |
896 | | } |
897 | | } |
898 | | print_memory_size(); |
899 | | /*unlock*/ |
900 | | gf_mx_v(gpac_allocations_lock); |
901 | | |
902 | | i=0; |
903 | | while (1) { |
904 | | const char *n = enum_open_handles(&i); |
905 | | if (!n) break; |
906 | | gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] File %s was not closed\n", n); |
907 | | } |
908 | | } |
909 | | } |
910 | | |
911 | | #endif /*GPAC_MEMORY_TRACKING*/ |
912 | | |
913 | | #if 0 //unused |
914 | | |
915 | | /*gf_asprintf(): as_printf portable implementation*/ |
916 | | #if defined(WIN32) || defined(_WIN32_WCE) || (defined (__SVR4) && defined (__sun)) |
917 | | static GFINLINE int gf_vasprintf (char **strp, const char *fmt, va_list ap) |
918 | | { |
919 | | int vsn_ret, size; |
920 | | char *buffer, *realloc_buffer; |
921 | | |
922 | | size = 2 * (u32) strlen(fmt); /*first guess for the size*/ |
923 | | buffer = (char*)gf_malloc(size); |
924 | | if (buffer == NULL) |
925 | | return -1; |
926 | | |
927 | | while (1) { |
928 | | #if !defined(WIN32) && !defined(_WIN32_WCE) |
929 | | #define _vsnprintf vsnprintf |
930 | | #endif |
931 | | vsn_ret = _vsnprintf(buffer, size, fmt, ap); |
932 | | |
933 | | /* If that worked, return the string. */ |
934 | | if (vsn_ret>-1 && vsn_ret<size) { |
935 | | *strp = buffer; |
936 | | return vsn_ret; |
937 | | } |
938 | | |
939 | | /*else double the allocated size*/ |
940 | | size *= 2; |
941 | | realloc_buffer = (char*)gf_realloc(buffer, size); |
942 | | if (!realloc_buffer) { |
943 | | gf_free(buffer); |
944 | | return -1; |
945 | | } else { |
946 | | buffer = realloc_buffer; |
947 | | } |
948 | | |
949 | | } |
950 | | } |
951 | | #endif |
952 | | |
953 | | GF_EXPORT |
954 | | int gf_asprintf(char **strp, const char *fmt, ...) |
955 | | { |
956 | | s32 size; |
957 | | va_list args; |
958 | | va_start(args, fmt); |
959 | | #if defined(WIN32) || defined(_WIN32_WCE) || (defined (__SVR4) && defined (__sun)) |
960 | | size = gf_vasprintf(strp, fmt, args); |
961 | | #else |
962 | | size = asprintf(strp, fmt, args); |
963 | | #endif |
964 | | va_end(args); |
965 | | return size; |
966 | | } |
967 | | |
968 | | #endif //unused |
969 | | |
970 | | /* |
971 | | * FROM: https://github.com/freebsd/freebsd-src/blob/master/sys/libkern/strlcpy.c |
972 | | * |
973 | | * Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com> |
974 | | * |
975 | | * Permission to use, copy, modify, and distribute this software for any |
976 | | * purpose with or without fee is hereby granted, provided that the above |
977 | | * copyright notice and this permission notice appear in all copies. |
978 | | * |
979 | | */ |
980 | | /* |
981 | | * Copy string src to buffer dst of size dsize. At most dsize-1 |
982 | | * chars will be copied. Always NUL terminates (unless dsize == 0). |
983 | | * Returns strlen(src); if retval >= dsize, truncation occurred. |
984 | | */ |
985 | | GF_EXPORT |
986 | | size_t gf_strlcpy(char * dst, const char * src, size_t dsize) |
987 | 0 | { |
988 | 0 | const char *osrc = src; |
989 | 0 | size_t nleft = dsize; |
990 | | |
991 | | /* Copy as many bytes as will fit. */ |
992 | 0 | if (nleft != 0) { |
993 | 0 | while (--nleft != 0) { |
994 | 0 | if ((*dst++ = *src++) == '\0') |
995 | 0 | break; |
996 | 0 | } |
997 | 0 | } |
998 | | |
999 | | /* Not enough room in dst, add NUL and traverse rest of src. */ |
1000 | 0 | if (nleft == 0) { |
1001 | 0 | if (dsize != 0) |
1002 | 0 | *dst = '\0'; /* NUL-terminate dst */ |
1003 | 0 | while (*src++) |
1004 | 0 | ; |
1005 | 0 | } |
1006 | |
|
1007 | 0 | return(src - osrc - 1); /* count does not include NUL */ |
1008 | 0 | } |