Coverage Report

Created: 2025-11-10 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}