Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Romain Bouqueau - Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2010-2018
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 : #define MALLOC malloc
122 : #define CALLOC calloc
123 : #define REALLOC realloc
124 : #define FREE free
125 : #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 : {
150 : return MALLOC(size);
151 : }
152 : GF_EXPORT
153 : void *gf_calloc(size_t num, size_t size_of)
154 : {
155 : return CALLOC(num, size_of);
156 : }
157 : GF_EXPORT
158 : void *gf_realloc(void *ptr, size_t size)
159 : {
160 : return REALLOC(ptr, size);
161 : }
162 : GF_EXPORT
163 : void gf_free(void *ptr)
164 : {
165 : FREE(ptr);
166 : }
167 : GF_EXPORT
168 : char *gf_strdup(const char *str)
169 : {
170 : STRDUP(str);
171 : }
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 : #ifdef _WIN32_WCE
196 : #define assert(p)
197 : #endif
198 :
199 : //backtrace not supported on these platforms
200 : #if defined(GPAC_CONFIG_IOS) || defined(GPAC_CONFIG_ANDROID)
201 : #define GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE
202 : #endif
203 :
204 : int gf_mem_track_enabled = 0;
205 :
206 : #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE
207 : static int gf_mem_backtrace_enabled = 0;
208 :
209 : /*malloc dynamic storage needed for each alloc is STACK_PRINT_SIZE*SYMBOL_MAX_SIZE+1, keep them small!*/
210 : #define STACK_FIRST_IDX 5 //remove the gpac memory allocator self trace
211 : #define STACK_PRINT_SIZE 10
212 :
213 : #ifdef WIN32
214 : #define SYMBOL_MAX_SIZE 50
215 : #include <windows.h>
216 : /* on visual studio 2015 windows sdk 8.1 dbghelp has a typedef enum with no name that throws a warning */
217 : #if !defined(__GNUC__)
218 : #pragma warning(disable: 4091)
219 : #endif
220 : #include <dbghelp.h>
221 : #if !defined(__GNUC__)
222 : #pragma comment(lib, "dbghelp.lib")
223 : #endif
224 : /*memory ownership to the caller*/
225 : static void store_backtrace(char *s_backtrace)
226 : {
227 : void *stack[STACK_PRINT_SIZE];
228 : size_t i, frames, bt_idx = 0;
229 : SYMBOL_INFO *symbol;
230 : HANDLE process;
231 :
232 : process = GetCurrentProcess();
233 : SymInitialize(process, NULL, TRUE);
234 :
235 : symbol = (SYMBOL_INFO*)_alloca(sizeof(SYMBOL_INFO) + SYMBOL_MAX_SIZE);
236 : symbol->MaxNameLen = SYMBOL_MAX_SIZE-1;
237 : symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
238 :
239 : frames = CaptureStackBackTrace(STACK_FIRST_IDX, STACK_PRINT_SIZE, stack, NULL);
240 :
241 : for (i=0; i<frames; i++) {
242 : int len;
243 : int bt_len;
244 : char *symbol_name = "unresolved";
245 : SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
246 : if (symbol->Name) symbol_name = (char*)symbol->Name;
247 :
248 : bt_len = (int) strlen(symbol_name) + 10;
249 : if (bt_idx + bt_len > STACK_PRINT_SIZE*SYMBOL_MAX_SIZE) {
250 : gf_memory_log(GF_MEMORY_WARNING, "[MemoryInfo] Not enough space to hold backtrace - truncating\n");
251 : break;
252 : }
253 :
254 : len = _snprintf(s_backtrace+bt_idx, SYMBOL_MAX_SIZE-1, "\t%02u 0x%I64X %s", (unsigned int) (frames-i-1), symbol->Address, symbol_name);
255 : if (len<0) len = SYMBOL_MAX_SIZE-1;
256 : s_backtrace[bt_idx+len]='\n';
257 : bt_idx += (len+1);
258 : }
259 : assert(bt_idx < STACK_PRINT_SIZE*SYMBOL_MAX_SIZE);
260 : s_backtrace[bt_idx-1] = '\0';
261 : }
262 :
263 : #else /*WIN32*/
264 :
265 : #define SYMBOL_MAX_SIZE 100
266 : #include <execinfo.h>
267 :
268 : /*memory ownership to the caller*/
269 866 : static void store_backtrace(char *s_backtrace)
270 : {
271 : size_t i, size, bt_idx=0;
272 : void *stack[STACK_PRINT_SIZE+STACK_FIRST_IDX];
273 : char **messages;
274 :
275 866 : size = backtrace(stack, STACK_PRINT_SIZE+STACK_FIRST_IDX);
276 866 : messages = backtrace_symbols(stack, size);
277 :
278 6755 : for (i=STACK_FIRST_IDX; i<size && messages!=NULL; ++i) {
279 5889 : int bt_len = strlen(messages[i]) + 10;
280 : int len;
281 :
282 5889 : if (bt_idx + bt_len > STACK_PRINT_SIZE*SYMBOL_MAX_SIZE) {
283 0 : gf_memory_log(GF_MEMORY_WARNING, "[MemoryInfo] Not enough space to hold backtrace - truncating\n");
284 0 : break;
285 : }
286 :
287 5889 : len = snprintf(s_backtrace+bt_idx, SYMBOL_MAX_SIZE-1, "\t%02zu %s", i, messages[i]);
288 5889 : if (len<0) len = SYMBOL_MAX_SIZE-1;
289 5889 : s_backtrace[bt_idx+len]='\n';
290 5889 : bt_idx += (len+1);
291 :
292 : }
293 : assert(bt_idx < STACK_PRINT_SIZE*SYMBOL_MAX_SIZE);
294 866 : s_backtrace[bt_idx-1] = '\0';
295 866 : free(messages);
296 866 : }
297 : #endif /*WIN32*/
298 :
299 :
300 : #endif /*GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE*/
301 :
302 :
303 : static void register_address(void *ptr, size_t size, const char *filename, int line);
304 : static int unregister_address(void *ptr, const char *filename, int line);
305 :
306 :
307 37468 : static void *gf_mem_malloc_basic(size_t size, const char *filename, int line)
308 : {
309 37468 : return MALLOC(size);
310 : }
311 1 : static void *gf_mem_calloc_basic(size_t num, size_t size_of, const char *filename, int line)
312 : {
313 1 : return CALLOC(num, size_of);
314 : }
315 60269 : static void *gf_mem_realloc_basic(void *ptr, size_t size, const char *filename, int line)
316 : {
317 60269 : return REALLOC(ptr, size);
318 : }
319 137261 : static void gf_mem_free_basic(void *ptr, const char *filename, int line)
320 : {
321 137261 : FREE(ptr);
322 137261 : }
323 84797 : static char *gf_mem_strdup_basic(const char *str, const char *filename, int line)
324 : {
325 84797 : STRDUP(str);
326 : }
327 :
328 :
329 : static unsigned int nb_calls_alloc = 0;
330 : static unsigned int nb_calls_calloc = 0;
331 : static unsigned int nb_calls_realloc = 0;
332 : static unsigned int nb_calls_free = 0;
333 :
334 9702281 : void *gf_mem_malloc_tracker(size_t size, const char *filename, int line)
335 : {
336 9702281 : void *ptr = MALLOC(size);
337 9702281 : if (!ptr) {
338 0 : gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] malloc() has returned a NULL pointer\n");
339 : assert(0);
340 : } else {
341 9702281 : register_address(ptr, size, filename, line);
342 : }
343 9702280 : gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] malloc %3d bytes at %p in:\n", size, ptr);
344 9702281 : gf_memory_log(GF_MEMORY_DEBUG, " file %s at line %d\n" , filename, line);
345 9702282 : nb_calls_alloc++;
346 9702282 : return ptr;
347 : }
348 :
349 4439 : void *gf_mem_calloc_tracker(size_t num, size_t size_of, const char *filename, int line)
350 : {
351 4439 : size_t size = num*size_of;
352 4439 : void *ptr = CALLOC(num, size_of);
353 4439 : if (!ptr) {
354 0 : gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] calloc() has returned a NULL pointer\n");
355 : assert(0);
356 : } else {
357 4439 : register_address(ptr, size, filename, line);
358 : }
359 4439 : gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] calloc %3d bytes at %p in:\n", ptr, size);
360 4439 : gf_memory_log(GF_MEMORY_DEBUG, " file %s at line %d\n" , filename, line);
361 4439 : nb_calls_calloc++;
362 4439 : return ptr;
363 : }
364 :
365 10141122 : void gf_mem_free_tracker(void *ptr, const char *filename, int line)
366 : {
367 : int size_prev;
368 10141122 : if (ptr && (size_prev=unregister_address(ptr, filename, line))) {
369 9690603 : gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] free %3d bytes at %p in:\n", size_prev, ptr);
370 9690603 : gf_memory_log(GF_MEMORY_DEBUG, " file %s at line %d\n" , filename, line);
371 9690601 : FREE(ptr);
372 : }
373 10141122 : nb_calls_free++;
374 10141122 : }
375 :
376 4181097 : void *gf_mem_realloc_tracker(void *ptr, size_t size, const char *filename, int line)
377 : {
378 : void *ptr_g;
379 : int size_prev;
380 4181097 : if (!ptr) {
381 1308145 : gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] realloc() from a null pointer: calling malloc() instead\n");
382 1308145 : return gf_mem_malloc_tracker(size, filename, line);
383 : }
384 : /*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.*/
385 2872952 : if (!size) {
386 0 : gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] realloc() with a null size: calling free() instead\n");
387 0 : gf_mem_free_tracker(ptr, filename, line);
388 0 : return NULL;
389 : }
390 2872952 : size_prev = unregister_address(ptr, filename, line);
391 2872952 : ptr_g = REALLOC(ptr, size);
392 2872952 : if (!ptr_g) {
393 : /*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.*/
394 0 : gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] realloc() has returned a NULL pointer\n");
395 0 : register_address(ptr, size_prev, filename, line);
396 : assert(0);
397 : } else {
398 2872952 : register_address(ptr_g, size, filename, line);
399 : // 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);
400 2872952 : gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] realloc %3d (instead of %3d) bytes at %p\n", size, size_prev, ptr_g);
401 2872952 : gf_memory_log(GF_MEMORY_DEBUG, " file %s at line %d\n" , filename, line);
402 : }
403 2872952 : nb_calls_realloc++;
404 2872952 : return ptr_g;
405 : }
406 :
407 1665909 : char *gf_mem_strdup_tracker(const char *str, const char *filename, int line)
408 : {
409 : char *ptr;
410 1665909 : if (!str) return NULL;
411 1665909 : ptr = (char*)gf_mem_malloc_tracker(strlen(str)+1, filename, line);
412 : strcpy(ptr, str);
413 1665909 : return ptr;
414 : }
415 :
416 :
417 : static void *(*gf_mem_malloc_proto)(size_t size, const char *filename, int line) = gf_mem_malloc_basic;
418 : static void *(*gf_mem_calloc_proto)(size_t num, size_t size_of, const char *filename, int line) = gf_mem_calloc_basic;
419 : static void *(*gf_mem_realloc_proto)(void *ptr, size_t size, const char *filename, int line) = gf_mem_realloc_basic;
420 : static void (*gf_mem_free_proto)(void *ptr, const char *filename, int line) = gf_mem_free_basic;
421 : static char *(*gf_mem_strdup_proto)(const char *str, const char *filename, int line) = gf_mem_strdup_basic;
422 :
423 : #ifndef MY_GF_EXPORT
424 : #if defined(__GNUC__) && __GNUC__ >= 4
425 : #define MY_GF_EXPORT __attribute__((visibility("default")))
426 : #else
427 : /*use def files for windows or let compiler decide*/
428 : #define MY_GF_EXPORT
429 : #endif
430 : #endif
431 :
432 6765687 : MY_GF_EXPORT void *gf_mem_malloc(size_t size, const char *filename, int line)
433 : {
434 6765687 : return gf_mem_malloc_proto(size, filename, line);
435 : }
436 :
437 4440 : MY_GF_EXPORT void *gf_mem_calloc(size_t num, size_t size_of, const char *filename, int line)
438 : {
439 4440 : return gf_mem_calloc_proto(num, size_of, filename, line);
440 : }
441 :
442 : MY_GF_EXPORT
443 4241366 : void *gf_mem_realloc(void *ptr, size_t size, const char *filename, int line)
444 : {
445 4241366 : return gf_mem_realloc_proto(ptr, size, filename, line);
446 : }
447 :
448 : MY_GF_EXPORT
449 10278382 : void gf_mem_free(void *ptr, const char *filename, int line)
450 : {
451 10278382 : gf_mem_free_proto(ptr, filename, line);
452 10278382 : }
453 :
454 : MY_GF_EXPORT
455 1750706 : char *gf_mem_strdup(const char *str, const char *filename, int line)
456 : {
457 1750706 : return gf_mem_strdup_proto(str, filename, line);
458 : }
459 :
460 : MY_GF_EXPORT
461 6212 : void gf_mem_enable_tracker(unsigned int enable_backtrace)
462 : {
463 : #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE
464 6212 : gf_mem_backtrace_enabled = enable_backtrace ? 1 : 0;
465 : #endif
466 6212 : gf_mem_track_enabled = 1;
467 6212 : gf_mem_malloc_proto = gf_mem_malloc_tracker;
468 6212 : gf_mem_calloc_proto = gf_mem_calloc_tracker;
469 6212 : gf_mem_realloc_proto = gf_mem_realloc_tracker;
470 6212 : gf_mem_free_proto = gf_mem_free_tracker;
471 6212 : gf_mem_strdup_proto = gf_mem_strdup_tracker;
472 6212 : }
473 :
474 67555661 : size_t gf_mem_get_stats(unsigned int *nb_allocs, unsigned int *nb_callocs, unsigned int *nb_reallocs, unsigned int *nb_free)
475 : {
476 67555661 : if (nb_allocs) (*nb_allocs) = nb_calls_alloc;
477 67555661 : if (nb_callocs) (*nb_callocs) = nb_calls_calloc;
478 67555661 : if (nb_reallocs) (*nb_reallocs) = nb_calls_realloc;
479 67555661 : if (nb_free) (*nb_free) = nb_calls_free;
480 67555661 : return gpac_allocated_memory;
481 : }
482 :
483 : typedef struct s_memory_element
484 : {
485 : void *ptr;
486 : unsigned int size;
487 : struct s_memory_element *next;
488 : #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE
489 : char *backtrace_stack;
490 : #endif
491 : int line;
492 : char *filename;
493 : } memory_element;
494 :
495 : /*pointer to the first element of the list*/
496 : typedef memory_element** memory_list;
497 :
498 :
499 : #define HASH_ENTRIES 4096
500 :
501 : #if !defined(WIN32)
502 : #include <stdint.h>
503 : #endif
504 :
505 : static unsigned int gf_memory_hash(void *ptr)
506 : {
507 : #if defined(WIN32)
508 : return (unsigned int) ( (((unsigned __int64)ptr>>4)+(unsigned __int64)ptr) % HASH_ENTRIES );
509 : #else
510 62830155 : return (unsigned int) ( (((uint64_t) ((intptr_t) ptr)>>4) + (uint64_t) ((intptr_t)ptr) ) % HASH_ENTRIES );
511 : #endif
512 : }
513 :
514 :
515 : /*base functions (add, find, del_item, del) are implemented upon a stack model*/
516 25132468 : static void gf_memory_add_stack(memory_element **p, void *ptr, unsigned int size, const char *filename, int line)
517 : {
518 25132468 : memory_element *element = (memory_element*)MALLOC(sizeof(memory_element));
519 25132468 : if (!element) {
520 0 : gf_memory_log(GF_MEMORY_ERROR, ("[Mem] Fail to register stack for allocation\n"));
521 0 : return;
522 : }
523 25132468 : element->ptr = ptr;
524 25132468 : element->size = size;
525 25132468 : element->line = line;
526 :
527 : #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE
528 25132468 : if (gf_mem_backtrace_enabled) {
529 866 : element->backtrace_stack = MALLOC(sizeof(char) * STACK_PRINT_SIZE * SYMBOL_MAX_SIZE);
530 866 : if (!element->backtrace_stack) {
531 0 : gf_memory_log(GF_MEMORY_WARNING, ("[Mem] Fail to register backtrace of allocation\n"));
532 0 : element->backtrace_stack = NULL;
533 : } else {
534 866 : store_backtrace(element->backtrace_stack);
535 : }
536 : } else {
537 25131602 : element->backtrace_stack = NULL;
538 : }
539 : #endif
540 :
541 25132468 : element->filename = MALLOC(strlen(filename) + 1);
542 25132468 : if (element->filename)
543 : strcpy(element->filename, filename);
544 :
545 25132468 : element->next = *p;
546 25132468 : *p = element;
547 : }
548 :
549 : /*returns the position of a ptr from a memory_element, 0 if not found*/
550 : static int gf_memory_find_stack(memory_element *p, void *ptr)
551 : {
552 : int i = 1;
553 : memory_element *element = p;
554 14524926 : while (element) {
555 14524926 : if (element->ptr == ptr) {
556 : return i;
557 : }
558 1959707 : element = element->next;
559 1959707 : i++;
560 : }
561 : return 0;
562 : }
563 :
564 : /*returns the size of the deleted item*/
565 25132468 : static unsigned int gf_memory_del_item_stack(memory_element **p, void *ptr)
566 : {
567 : unsigned int size;
568 25132468 : memory_element *curr_element=*p, *prev_element=NULL;
569 59613014 : while (curr_element) {
570 26761233 : if (curr_element->ptr == ptr) {
571 17413155 : if (prev_element) prev_element->next = curr_element->next;
572 16733537 : else *p = curr_element->next;
573 17413155 : size = curr_element->size;
574 : #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE
575 17413155 : if (curr_element->backtrace_stack) {
576 610 : FREE(curr_element->backtrace_stack);
577 : }
578 : #endif
579 17413155 : FREE(curr_element);
580 17413155 : return size;
581 : }
582 : prev_element = curr_element;
583 9348078 : curr_element = curr_element->next;
584 : }
585 : return 0;
586 : }
587 :
588 : /*this list is implemented as a stack to minimise the cost of freeing recent allocations*/
589 25132468 : static void gf_memory_add(memory_list *p, void *ptr, unsigned int size, const char *filename, int line)
590 : {
591 : unsigned int hash;
592 25132468 : if (!*p) *p = (memory_list) CALLOC(HASH_ENTRIES, sizeof(memory_element*));
593 : assert(*p);
594 :
595 : hash = gf_memory_hash(ptr);
596 25132468 : gf_memory_add_stack(&((*p)[hash]), ptr, size, filename, line);
597 25132468 : }
598 :
599 :
600 : static int gf_memory_find(memory_list p, void *ptr)
601 : {
602 : unsigned int hash;
603 : assert(p);
604 0 : if (!p) return 0;
605 : hash = gf_memory_hash(ptr);
606 12565219 : return gf_memory_find_stack(p[hash], ptr);
607 : }
608 :
609 25132468 : static unsigned int gf_memory_del_item(memory_list *p, void *ptr)
610 : {
611 : unsigned int hash;
612 : unsigned int ret;
613 : memory_element **sub_list;
614 25132468 : if (!*p) *p = (memory_list) CALLOC(HASH_ENTRIES, sizeof(memory_element*));
615 : assert(*p);
616 : hash = gf_memory_hash(ptr);
617 25132468 : sub_list = &((*p)[hash]);
618 25132468 : if (!sub_list) return 0;
619 25132468 : ret = gf_memory_del_item_stack(sub_list, ptr);
620 25132468 : if (ret && !((*p)[hash])) {
621 : /*check for deletion*/
622 : int i;
623 0 : for (i=0; i<HASH_ENTRIES; i++)
624 12395005 : if (&((*p)[i])) break;
625 12395005 : if (i==HASH_ENTRIES) {
626 0 : FREE(*p);
627 : }
628 : }
629 : return ret;
630 : }
631 :
632 :
633 :
634 : #endif /*GPAC_MEMORY_TRACKING*/
635 :
636 :
637 : #include <gpac/tools.h>
638 :
639 :
640 : /*GPAC memory tracking*/
641 : #ifdef GPAC_MEMORY_TRACKING
642 :
643 : #include <gpac/thread.h>
644 :
645 : /*global lists of allocations and deallocations*/
646 : memory_list memory_add = NULL, memory_rem = NULL;
647 : GF_Mutex *gpac_allocations_lock = NULL;
648 :
649 12579669 : static void register_address(void *ptr, size_t size, const char *filename, int line)
650 : {
651 : /*mutex initialization*/
652 12579669 : if (gpac_allocations_lock == 0) {
653 : assert(!memory_add);
654 : assert(!memory_rem);
655 6212 : gpac_allocations_lock = (GF_Mutex*)1; /*must be non-null to avoid a recursive infinite call*/
656 6212 : gpac_allocations_lock = gf_mx_new("gpac_allocations_lock");
657 : }
658 12573457 : else if (gpac_allocations_lock == (void*)1) {
659 : /*we're initializing the mutex (ie called by the gf_mx_new() above)*/
660 : return;
661 : }
662 :
663 : /*lock*/
664 12567246 : gf_mx_p(gpac_allocations_lock);
665 :
666 12567249 : gf_memory_add(&memory_add, ptr, (unsigned int)size, filename, line);
667 12567249 : gf_memory_del_item(&memory_rem, ptr); /*the same block can be reallocated, so remove it from the deallocation list*/
668 :
669 : /*update stats*/
670 12567249 : gpac_allocated_memory += size;
671 12567249 : gpac_nb_alloc_blocs++;
672 :
673 : /*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);*/
674 :
675 : /*unlock*/
676 12567249 : gf_mx_v(gpac_allocations_lock);
677 : }
678 :
679 140 : void log_backtrace(unsigned int log_level, memory_element *element)
680 : {
681 : #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE
682 140 : if (gf_mem_backtrace_enabled) {
683 140 : gf_memory_log(log_level, "file %s at line %d\n%s\n", element->filename, element->line, element->backtrace_stack);
684 : } else
685 : #endif
686 : {
687 0 : gf_memory_log(log_level, "file %s at line %d\n", element->filename, element->line);
688 : }
689 140 : }
690 :
691 :
692 : #if 0 //unused
693 : Bool gf_mem_check_address(void *ptr)
694 : {
695 : Bool res = GF_TRUE;
696 : int pos;
697 :
698 : if (!gpac_allocations_lock) return res;
699 :
700 : /*lock*/
701 : gf_mx_p(gpac_allocations_lock);
702 :
703 : if ( (pos=gf_memory_find(memory_rem, ptr)) ) {
704 : int i;
705 : unsigned int hash = gf_memory_hash(ptr);
706 : memory_element *element = memory_rem[hash];
707 : assert(element);
708 : for (i=1; i<pos; i++)
709 : element = element->next;
710 : assert(element);
711 : gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] the block %p was already freed in:\n", ptr);
712 : res = GF_FALSE;
713 : log_backtrace(GF_MEMORY_ERROR, element);
714 : // assert(0);
715 : }
716 : /*unlock*/
717 : gf_mx_v(gpac_allocations_lock);
718 : return res;
719 : }
720 : #endif
721 :
722 : /*returns the size of the unregistered block*/
723 12565217 : static int unregister_address(void *ptr, const char *filename, int line)
724 : {
725 : unsigned int size = 0; /*default: failure*/
726 :
727 : /*lock*/
728 12565217 : gf_mx_p(gpac_allocations_lock);
729 :
730 12565219 : if (!memory_add) {
731 0 : if (!memory_rem) {
732 : /*assume we're rather destroying the mutex (ie calling the gf_mx_del() below)
733 : than being called by free() before the first allocation occured*/
734 : return 1;
735 : /*gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] calling free() before the first allocation occured\n");
736 : assert(0); */
737 : }
738 : } else {
739 12565219 : if (!gf_memory_find(memory_add, ptr)) {
740 : int pos;
741 0 : if (!(pos=gf_memory_find(memory_rem, ptr))) {
742 0 : gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] trying to free a never allocated block (%p)\n", ptr);
743 : /* assert(0); */ /*don't assert since this is often due to allocations that occured out of gpac (fonts, etc.)*/
744 : } else {
745 : int i;
746 : unsigned int hash = gf_memory_hash(ptr);
747 : memory_element *element = memory_rem[hash];
748 :
749 : assert(element);
750 0 : for (i=1; i<pos; i++)
751 0 : element = element->next;
752 : assert(element);
753 0 : gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] the block %p trying to be deleted in:\n", ptr);
754 0 : gf_memory_log(GF_MEMORY_ERROR, " file %s at line %d\n", filename, line);
755 0 : gf_memory_log(GF_MEMORY_ERROR, " was already freed in:\n");
756 0 : log_backtrace(GF_MEMORY_ERROR, element);
757 : assert(0);
758 : }
759 : } else {
760 12565219 : size = gf_memory_del_item(&memory_add, ptr);
761 :
762 : /*update stats*/
763 12565219 : gpac_allocated_memory -= size;
764 12565219 : gpac_nb_alloc_blocs--;
765 :
766 : /*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); */
767 :
768 : /*the allocation list is empty: free the lists to avoid a leak (we should be exiting)*/
769 12565219 : if (!memory_add) {
770 : assert(!gpac_allocated_memory);
771 : assert(!gpac_nb_alloc_blocs);
772 :
773 : /*we destroy the mutex we own, then we return*/
774 0 : gf_mx_del(gpac_allocations_lock);
775 0 : gpac_allocations_lock = NULL;
776 :
777 0 : gf_memory_log(GF_MEMORY_DEBUG, "[MemTracker] the allocated-blocks-list is empty: the freed-blocks-list will be emptied too.\n");
778 :
779 : //reset the freed block list
780 : memory_list *m_list = &memory_rem;
781 : int i;
782 0 : for (i=0; i<HASH_ENTRIES; i++) {
783 0 : memory_element **m_elt = &((*m_list)[i]) ;
784 :
785 0 : memory_element *curr_element=*m_elt, *next_element;
786 0 : while (curr_element) {
787 0 : next_element = curr_element->next;
788 : #ifndef GPAC_MEMORY_TRACKING_DISABLE_STACKTRACE
789 0 : if (curr_element->backtrace_stack) {
790 0 : FREE(curr_element->backtrace_stack);
791 : }
792 : #endif
793 0 : FREE(curr_element);
794 : curr_element = next_element;
795 : }
796 0 : *m_elt = NULL;
797 : }
798 :
799 0 : FREE(*m_list);
800 :
801 0 : return size;
802 : } else {
803 12565219 : gf_memory_add(&memory_rem, ptr, size, filename, line);
804 : }
805 : }
806 : }
807 :
808 : /*unlock*/
809 12565219 : gf_mx_v(gpac_allocations_lock);
810 :
811 12565218 : return size;
812 : }
813 :
814 45849207 : static void gf_memory_log(unsigned int level, const char *fmt, ...)
815 : {
816 : va_list vl;
817 : char msg[1024];
818 : assert(strlen(fmt) < 200);
819 45849207 : va_start(vl, fmt);
820 : vsnprintf(msg, 1024, fmt, vl);
821 45849207 : GF_LOG(level, GF_LOG_MEMORY, (msg));
822 45849249 : va_end(vl);
823 45849249 : }
824 :
825 : /*prints allocations sum-up*/
826 1 : static void print_memory_size()
827 : {
828 1 : 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 ));
829 1 : }
830 :
831 : GF_EXPORT
832 6145 : u64 gf_memory_size()
833 : {
834 6145 : return (u64) gpac_allocated_memory;
835 : }
836 :
837 : /*prints the state of current allocations*/
838 : GF_EXPORT
839 1 : void gf_memory_print()
840 : {
841 : /*if lists are empty, the mutex is also NULL*/
842 1 : if (!memory_add) {
843 : assert(!gpac_allocations_lock);
844 0 : gf_memory_log(GF_MEMORY_INFO, "[MemTracker] gf_memory_print(): the memory tracker is not initialized, some file handles are not closed.\n");
845 : } else {
846 1 : int i=0;
847 : assert(gpac_allocations_lock);
848 : const char *enum_open_handles(u32 *idx);
849 1 : u32 nb_handles = gf_file_handles_count();
850 :
851 :
852 1 : gf_memory_log(GF_MEMORY_INFO, "\n[MemTracker] Printing the current state of allocations (%d open file handles) :\n", nb_handles);
853 :
854 : /*lock*/
855 1 : gf_mx_p(gpac_allocations_lock);
856 4097 : for (i=0; i<HASH_ENTRIES; i++) {
857 4096 : memory_element *curr_element = memory_add[i], *next_element;
858 8332 : while (curr_element) {
859 : char szVal[51];
860 : char szHexVal[101];
861 : u32 size, j;
862 140 : next_element = curr_element->next;
863 140 : size = curr_element->size>=50 ? 50 : curr_element->size;
864 3154 : for (j=0 ; j<size ; j++) {
865 3014 : unsigned char byte = *((unsigned char*)(curr_element->ptr) + j);
866 3014 : szVal[j] = (byte > 31 && byte < 127) ? byte : '.';
867 3014 : sprintf(szHexVal+2*j, "%02X", byte);
868 : }
869 140 : szVal[size] = 0;
870 140 : szHexVal[2*size] = 0;
871 140 : gf_memory_log(GF_MEMORY_INFO, "[MemTracker] Memory Block %p (size %d) allocated in:\n", curr_element->ptr, curr_element->size);
872 140 : log_backtrace(GF_MEMORY_INFO, curr_element);
873 140 : gf_memory_log(GF_MEMORY_INFO, " string dump: %s\n", szVal);
874 140 : gf_memory_log(GF_MEMORY_INFO, " hex dump: %s\n", szHexVal);
875 : curr_element = next_element;
876 : }
877 : }
878 1 : print_memory_size();
879 : /*unlock*/
880 1 : gf_mx_v(gpac_allocations_lock);
881 :
882 1 : i=0;
883 1 : while (1) {
884 2 : const char *n = enum_open_handles(&i);
885 2 : if (!n) break;
886 1 : gf_memory_log(GF_MEMORY_ERROR, "[MemTracker] File %s was not closed\n", n);
887 : }
888 : }
889 1 : }
890 :
891 : #endif /*GPAC_MEMORY_TRACKING*/
892 :
893 : #if 0 //unused
894 :
895 : /*gf_asprintf(): as_printf portable implementation*/
896 : #if defined(WIN32) || defined(_WIN32_WCE) || (defined (__SVR4) && defined (__sun))
897 : static GFINLINE int gf_vasprintf (char **strp, const char *fmt, va_list ap)
898 : {
899 : int vsn_ret, size;
900 : char *buffer, *realloc_buffer;
901 :
902 : size = 2 * (u32) strlen(fmt); /*first guess for the size*/
903 : buffer = (char*)gf_malloc(size);
904 : if (buffer == NULL)
905 : return -1;
906 :
907 : while (1) {
908 : #if !defined(WIN32) && !defined(_WIN32_WCE)
909 : #define _vsnprintf vsnprintf
910 : #endif
911 : vsn_ret = _vsnprintf(buffer, size, fmt, ap);
912 :
913 : /* If that worked, return the string. */
914 : if (vsn_ret>-1 && vsn_ret<size) {
915 : *strp = buffer;
916 : return vsn_ret;
917 : }
918 :
919 : /*else double the allocated size*/
920 : size *= 2;
921 : realloc_buffer = (char*)gf_realloc(buffer, size);
922 : if (!realloc_buffer) {
923 : gf_free(buffer);
924 : return -1;
925 : } else {
926 : buffer = realloc_buffer;
927 : }
928 :
929 : }
930 : }
931 : #endif
932 :
933 : GF_EXPORT
934 : int gf_asprintf(char **strp, const char *fmt, ...)
935 : {
936 : s32 size;
937 : va_list args;
938 : va_start(args, fmt);
939 : #if defined(WIN32) || defined(_WIN32_WCE) || (defined (__SVR4) && defined (__sun))
940 : size = gf_vasprintf(strp, fmt, args);
941 : #else
942 : size = asprintf(strp, fmt, args);
943 : #endif
944 : va_end(args);
945 : return size;
946 : }
947 :
948 : #endif //unused
949 :
950 : /*
951 : * FROM: https://github.com/freebsd/freebsd-src/blob/master/sys/libkern/strlcpy.c
952 : *
953 : * Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
954 : *
955 : * Permission to use, copy, modify, and distribute this software for any
956 : * purpose with or without fee is hereby granted, provided that the above
957 : * copyright notice and this permission notice appear in all copies.
958 : *
959 : */
960 : /*
961 : * Copy string src to buffer dst of size dsize. At most dsize-1
962 : * chars will be copied. Always NUL terminates (unless dsize == 0).
963 : * Returns strlen(src); if retval >= dsize, truncation occurred.
964 : */
965 : GF_EXPORT
966 4189 : size_t gf_strlcpy(char * dst, const char * src, size_t dsize)
967 : {
968 : const char *osrc = src;
969 : size_t nleft = dsize;
970 :
971 : /* Copy as many bytes as will fit. */
972 4189 : if (nleft != 0) {
973 60421 : while (--nleft != 0) {
974 60419 : if ((*dst++ = *src++) == '\0')
975 : break;
976 : }
977 : }
978 :
979 : /* Not enough room in dst, add NUL and traverse rest of src. */
980 4189 : if (nleft == 0) {
981 2 : if (dsize != 0)
982 2 : *dst = '\0'; /* NUL-terminate dst */
983 16 : while (*src++)
984 : ;
985 : }
986 :
987 4189 : return(src - osrc - 1); /* count does not include NUL */
988 : }
|