Coverage Report

Created: 2026-05-16 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/svt-av1/Source/Lib/Codec/svt_malloc.c
Line
Count
Source
1
/*
2
* Copyright(c) 2019 Intel Corporation
3
*
4
* This source code is subject to the terms of the BSD 2 Clause License and
5
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6
* was not distributed with this source code in the LICENSE file, you can
7
* obtain it at https://www.aomedia.org/license/software-license. If the Alliance for Open
8
* Media Patent License 1.0 was not distributed with this source code in the
9
* PATENTS file, you can obtain it at https://www.aomedia.org/license/patent-license.
10
*/
11
#include "svt_malloc.h"
12
13
0
#define LOG_TAG "SvtMalloc"
14
#include "svt_log.h"
15
16
0
void svt_print_alloc_fail_impl(const char* file, int line) {
17
0
    SVT_FATAL("allocate memory failed, at %s:%d\n", file, line);
18
0
}
19
20
#ifdef DEBUG_MEMORY_USAGE
21
#include <stdint.h>
22
#include <limits.h>
23
#include "definitions.h"
24
#include "svt_threads.h"
25
26
static EbHandle g_malloc_mutex;
27
28
static void malloc_mutex_cleanup(void) {
29
    svt_destroy_mutex(g_malloc_mutex);
30
}
31
32
static ONCE_ROUTINE(create_malloc_mutex) {
33
    g_malloc_mutex = svt_create_mutex();
34
    atexit(malloc_mutex_cleanup);
35
    ONCE_ROUTINE_EPILOG;
36
}
37
38
DEFINE_ONCE(g_malloc_once);
39
40
static EbHandle get_malloc_mutex() {
41
    svt_run_once(&g_malloc_once, create_malloc_mutex);
42
    return g_malloc_mutex;
43
}
44
45
// Simple hash function to speed up entry search
46
// Takes the top half and bottom half of the pointer and adds them together.
47
static inline uint32_t hash(const void* const p) {
48
    const uintptr_t bit_mask = ((uintptr_t)1 << sizeof(bit_mask) / 2 * CHAR_BIT) - 1, v = (uintptr_t)p;
49
    return (uint32_t)((v >> (sizeof(v) / 2 * CHAR_BIT)) + (v & bit_mask));
50
}
51
52
typedef struct MemoryEntry {
53
    void*       ptr;
54
    size_t      count;
55
    const char* file;
56
    EbPtrType   type;
57
    uint32_t    line;
58
} MemoryEntry;
59
60
//+1 to get a better hash result
61
#define MEM_ENTRY_SIZE (4 * 1024 * 1024 + 1)
62
63
static MemoryEntry g_mem_entry[MEM_ENTRY_SIZE];
64
65
#define TO_INDEX(v) ((v) % MEM_ENTRY_SIZE)
66
static bool g_add_mem_entry_warning    = true;
67
static bool g_remove_mem_entry_warning = true;
68
69
/*********************************************************************************
70
*
71
* @brief
72
*  compare and update current memory entry.
73
*
74
* @param[in] e
75
*  current memory entry.
76
*
77
* @param[in] param
78
*  param you set to for_each_mem_entry
79
*
80
*
81
* @returns  return true if you want get early exit in for_each_mem_entry
82
*
83
s*
84
********************************************************************************/
85
86
typedef bool (*Predicate)(MemoryEntry* e, void* param);
87
88
/*********************************************************************************
89
*
90
* @brief
91
*  Loop through mem entries.
92
*
93
* @param[in] bucket
94
*  the hash bucket
95
*
96
* @param[in] start
97
*  loop start position
98
*
99
* @param[in] pred
100
*  return true if you want early exit
101
*
102
* @param[out] param
103
*  param send to pred.
104
*
105
* @returns  return true if we got early exit.
106
*
107
*
108
********************************************************************************/
109
static bool for_each_hash_entry(MemoryEntry* bucket, uint32_t start, Predicate pred, void* param) {
110
    const uint32_t s = TO_INDEX(start);
111
    uint32_t       i = s;
112
113
    do {
114
        MemoryEntry* e = bucket + i;
115
        if (pred(e, param)) {
116
            return true;
117
        }
118
        i++;
119
        i = TO_INDEX(i);
120
    } while (i != s);
121
    return false;
122
}
123
124
static bool for_each_mem_entry(uint32_t start, Predicate pred, void* param) {
125
    bool     ret;
126
    EbHandle m = get_malloc_mutex();
127
    svt_block_on_mutex(m);
128
    ret = for_each_hash_entry(g_mem_entry, start, pred, param);
129
    svt_release_mutex(m);
130
    return ret;
131
}
132
133
static const char* mem_type_name(EbPtrType type) {
134
    static const char* name[EB_PTR_TYPE_TOTAL] = {
135
        "malloced memory", "calloced memory", "aligned memory", "mutex", "semaphore", "thread"};
136
    return name[type];
137
}
138
139
static bool add_mem_entry(MemoryEntry* e, void* param) {
140
    if (!e->ptr) {
141
        *e = *(MemoryEntry*)param;
142
        return true;
143
    }
144
    return false;
145
}
146
147
static bool remove_mem_entry(MemoryEntry* e, void* param) {
148
    MemoryEntry* item = param;
149
    if (e->ptr == item->ptr) {
150
        // The second case is a special case, we use EB_FREE to free calloced memory
151
        if (e->type == item->type || (e->type == EB_C_PTR && item->type == EB_N_PTR)) {
152
            e->ptr = NULL;
153
            return true;
154
        }
155
    }
156
    return false;
157
}
158
159
typedef struct MemSummary {
160
    size_t   amount[EB_PTR_TYPE_TOTAL];
161
    uint32_t occupied;
162
} MemSummary;
163
164
static bool count_mem_entry(MemoryEntry* e, void* param) {
165
    if (e->ptr) {
166
        MemSummary* sum = param;
167
        sum->amount[e->type] += e->count;
168
        sum->occupied++;
169
    }
170
    return false;
171
}
172
173
static inline void get_memory_usage_and_scale(size_t amount, double* const usage, char* const scale) {
174
    static const char scales[] = {' ', 'K', 'M', 'G', 'T'};
175
    size_t            i        = 1;
176
    for (; i < sizeof(scales) && amount >= (size_t)1 << (++i * 10);)
177
        ;
178
    *scale = scales[--i];
179
    *usage = (double)amount / (double)((size_t)1 << (i * 10));
180
}
181
182
//this need more memory and cpu
183
#define PROFILE_MEMORY_USAGE
184
#ifdef PROFILE_MEMORY_USAGE
185
186
//if we use a static array here, this size + sizeof(g_mem_entry) will exceed max size allowed on windows.
187
static MemoryEntry* g_profile_entry;
188
189
static bool add_location(MemoryEntry* e, void* param) {
190
    MemoryEntry* new_item = param;
191
    if (!e->ptr) {
192
        *e = *new_item;
193
        return true;
194
    }
195
    if (e->file == new_item->file && e->line == new_item->line) {
196
        e->count += new_item->count;
197
        return true;
198
    }
199
    // to next position.
200
    return false;
201
}
202
203
static bool collect_mem(MemoryEntry* e, void* param) {
204
    EbPtrType* type = param;
205
    if (e->ptr && e->type == *type) {
206
        for_each_hash_entry(g_profile_entry, 0, add_location, e);
207
    }
208
    //Loop entire bucket.
209
    return false;
210
}
211
212
static int compare_count(const void* a, const void* b) {
213
    const MemoryEntry* pa = a;
214
    const MemoryEntry* pb = b;
215
    return pb->count < pa->count ? -1 : pb->count != pa->count;
216
}
217
218
static void print_top_10_locations() {
219
    EbHandle  m    = get_malloc_mutex();
220
    EbPtrType type = EB_N_PTR;
221
    svt_block_on_mutex(m);
222
    g_profile_entry = calloc(MEM_ENTRY_SIZE, sizeof(*g_profile_entry));
223
    if (!g_profile_entry) {
224
        SVT_ERROR("not enough memory for memory profile");
225
        svt_release_mutex(m);
226
        return;
227
    }
228
229
    for_each_hash_entry(g_mem_entry, 0, collect_mem, &type);
230
    qsort(g_profile_entry, MEM_ENTRY_SIZE, sizeof(*g_profile_entry), compare_count);
231
232
    SVT_INFO("top 10 %s locations:\n", mem_type_name(type));
233
    for (int i = 0; i < 10; i++) {
234
        double       usage;
235
        char         scale;
236
        MemoryEntry* e = g_profile_entry + i;
237
        get_memory_usage_and_scale(e->count, &usage, &scale);
238
        SVT_INFO("(%.2lf %cB): %s:%d\n", usage, scale, e->file, e->line);
239
    }
240
    free(g_profile_entry);
241
    svt_release_mutex(m);
242
}
243
#endif //PROFILE_MEMORY_USAGE
244
245
static int g_component_count;
246
247
static bool print_leak(MemoryEntry* e, void* param) {
248
    if (e->ptr) {
249
        bool* leaked = param;
250
        *leaked      = true;
251
        SVT_ERROR("%s leaked at %s:%d\n", mem_type_name(e->type), e->file, e->line);
252
    }
253
    //loop through all items
254
    return false;
255
}
256
257
void svt_print_memory_usage() {
258
    MemSummary sum;
259
    double     usage;
260
    char       scale;
261
    memset(&sum, 0, sizeof(sum));
262
263
    for_each_mem_entry(0, count_mem_entry, &sum);
264
    SVT_INFO("SVT Memory Usage:\n");
265
    get_memory_usage_and_scale(sum.amount[EB_N_PTR] + sum.amount[EB_C_PTR] + sum.amount[EB_A_PTR], &usage, &scale);
266
    SVT_INFO("    total allocated memory:       %.2lf %cB\n", usage, scale);
267
    get_memory_usage_and_scale(sum.amount[EB_N_PTR], &usage, &scale);
268
    SVT_INFO("        malloced memory:          %.2lf %cB\n", usage, scale);
269
    get_memory_usage_and_scale(sum.amount[EB_C_PTR], &usage, &scale);
270
    SVT_INFO("        callocated memory:        %.2lf %cB\n", usage, scale);
271
    get_memory_usage_and_scale(sum.amount[EB_A_PTR], &usage, &scale);
272
    SVT_INFO("        allocated aligned memory: %.2lf %cB\n", usage, scale);
273
274
    SVT_INFO("    mutex count: %zu\n", sum.amount[EB_MUTEX]);
275
    SVT_INFO("    semaphore count: %zu\n", sum.amount[EB_SEMAPHORE]);
276
    SVT_INFO("    thread count: %zu\n", sum.amount[EB_THREAD]);
277
    SVT_INFO("    hash table fulless: %f, hash bucket is %s\n",
278
             (double)sum.occupied / MEM_ENTRY_SIZE,
279
             (double)sum.occupied / MEM_ENTRY_SIZE < .3 ? "healthy" : "too full");
280
#ifdef PROFILE_MEMORY_USAGE
281
    print_top_10_locations();
282
#endif
283
}
284
285
void svt_increase_component_count() {
286
    EbHandle m = get_malloc_mutex();
287
    svt_block_on_mutex(m);
288
    g_component_count++;
289
    svt_release_mutex(m);
290
}
291
292
void svt_decrease_component_count() {
293
    EbHandle m = get_malloc_mutex();
294
    svt_block_on_mutex(m);
295
    g_component_count--;
296
    if (!g_component_count) {
297
        bool leaked = false;
298
        for_each_hash_entry(g_mem_entry, 0, print_leak, &leaked);
299
        if (!leaked) {
300
            SVT_INFO("you have no memory leak\n");
301
        }
302
    }
303
    svt_release_mutex(m);
304
}
305
306
void svt_add_mem_entry_impl(void* ptr, EbPtrType type, size_t count, const char* file, uint32_t line) {
307
    if (for_each_mem_entry(hash(ptr),
308
                           add_mem_entry,
309
                           &(MemoryEntry){.ptr = ptr, .type = type, .count = count, .file = file, .line = line})) {
310
        return;
311
    }
312
    if (g_add_mem_entry_warning) {
313
        SVT_ERROR(
314
            "can't add memory entry.\n"
315
            "You have memory leak or you need increase MEM_ENTRY_SIZE\n");
316
        g_add_mem_entry_warning = false;
317
    }
318
}
319
320
void svt_remove_mem_entry(void* ptr, EbPtrType type) {
321
    if (!ptr) {
322
        return;
323
    }
324
    if (for_each_mem_entry(hash(ptr), remove_mem_entry, &(MemoryEntry){.ptr = ptr, .type = type})) {
325
        return;
326
    }
327
    if (g_remove_mem_entry_warning) {
328
        SVT_ERROR("something wrong. you freed a unallocated memory %p, type = %s\n", ptr, mem_type_name(type));
329
        g_remove_mem_entry_warning = false;
330
    }
331
}
332
#endif