Line data Source code
1 : /*
2 : * Copyright(c) 2019 Intel Corporation
3 : * SPDX - License - Identifier: BSD - 2 - Clause - Patent
4 : */
5 : #include <stdio.h>
6 : #include <inttypes.h>
7 :
8 :
9 : #include "EbMalloc.h"
10 : #include "EbThreads.h"
11 :
12 : #ifdef DEBUG_MEMORY_USAGE
13 :
14 : static EbHandle g_malloc_mutex;
15 :
16 : #ifdef _WIN32
17 :
18 : #include <windows.h>
19 :
20 : static INIT_ONCE g_malloc_once = INIT_ONCE_STATIC_INIT;
21 :
22 : BOOL CALLBACK create_malloc_mutex (
23 : PINIT_ONCE InitOnce,
24 : PVOID Parameter,
25 : PVOID *lpContext)
26 : {
27 : (void)InitOnce;
28 : (void)Parameter;
29 : (void)lpContext;
30 : g_malloc_mutex = eb_create_mutex();
31 : return TRUE;
32 : }
33 :
34 : static EbHandle get_malloc_mutex()
35 : {
36 : InitOnceExecuteOnce(&g_malloc_once, create_malloc_mutex, NULL, NULL);
37 : return g_malloc_mutex;
38 : }
39 : #else
40 : #include <pthread.h>
41 2 : static void create_malloc_mutex()
42 : {
43 2 : g_malloc_mutex = eb_create_mutex();
44 2 : }
45 :
46 : static pthread_once_t g_malloc_once = PTHREAD_ONCE_INIT;
47 :
48 769968 : static EbHandle get_malloc_mutex()
49 : {
50 769968 : pthread_once(&g_malloc_once, create_malloc_mutex);
51 769968 : return g_malloc_mutex;
52 : }
53 : #endif // _WIN32
54 :
55 : //hash function to speedup etnry search
56 769960 : uint32_t hash(void* p)
57 : {
58 : #define MASK32 ((((uint64_t)1)<<32)-1)
59 :
60 769960 : uint64_t v = (uint64_t)p;
61 769960 : uint64_t low32 = v & MASK32;
62 769960 : return (uint32_t)((v >> 32) + low32);
63 : }
64 :
65 : typedef struct MemoryEntry{
66 : void* ptr;
67 : EbPtrType type;
68 : size_t count;
69 : const char* file;
70 : uint32_t line;
71 : } MemoryEntry;
72 :
73 : //+1 to get a better hash result
74 : #define MEM_ENTRY_SIZE (4 * 1024 * 1024 + 1)
75 :
76 : MemoryEntry g_mem_entry[MEM_ENTRY_SIZE];
77 :
78 : #define TO_INDEX(v) ((v) % MEM_ENTRY_SIZE)
79 : static EbBool g_add_mem_entry_warning = EB_TRUE;
80 : static EbBool g_remove_mem_entry_warning = EB_TRUE;
81 :
82 :
83 : /*********************************************************************************
84 : *
85 : * @brief
86 : * compare and update current memory entry.
87 : *
88 : * @param[in] e
89 : * current memory entry.
90 : *
91 : * @param[in] param
92 : * param you set to for_each_mem_entry
93 : *
94 : *
95 : * @returns return EB_TRUE if you want get early exit in for_each_mem_entry
96 : *
97 : s*
98 : ********************************************************************************/
99 :
100 : typedef EbBool (*Predicate)(MemoryEntry* e, void* param);
101 :
102 : /*********************************************************************************
103 : *
104 : * @brief
105 : * Loop through mem entries.
106 : *
107 : * @param[in] bucket
108 : * the hash bucket
109 : *
110 : * @param[in] start
111 : * loop start position
112 : *
113 : * @param[in] pred
114 : * return EB_TRUE if you want early exit
115 : *
116 : * @param[out] param
117 : * param send to pred.
118 : *
119 : * @returns return EB_TRUE if we got early exit.
120 : *
121 : *
122 : ********************************************************************************/
123 1000020 : static EbBool for_each_hash_entry(MemoryEntry* bucket, uint32_t start, Predicate pred, void* param)
124 : {
125 :
126 1000020 : uint32_t s = TO_INDEX(start);
127 1000020 : uint32_t i = s;
128 :
129 : do {
130 29056400 : MemoryEntry* e = bucket + i;
131 29056400 : if (pred(e, param)) {
132 1000020 : return EB_TRUE;
133 : }
134 28056300 : i++;
135 28056300 : i = TO_INDEX(i);
136 28056300 : } while (i != s);
137 6 : return EB_FALSE;
138 : }
139 :
140 769962 : static EbBool for_each_mem_entry(uint32_t start, Predicate pred, void* param)
141 : {
142 : EbBool ret;
143 769962 : EbHandle m = get_malloc_mutex();
144 769962 : eb_block_on_mutex(m);
145 769962 : ret = for_each_hash_entry(g_mem_entry, start, pred, param);
146 769962 : eb_release_mutex(m);
147 769962 : return ret;
148 : }
149 :
150 62 : static const char* mem_type_name(EbPtrType type)
151 : {
152 : static const char *name[EB_PTR_TYPE_TOTAL] = {"malloced memory", "calloced memory", "aligned memory", "mutex", "semaphore", "thread"};
153 62 : return name[type];
154 : }
155 :
156 391696 : static EbBool add_mem_entry(MemoryEntry* e, void* param)
157 : {
158 391696 : MemoryEntry* new_item = (MemoryEntry*)param;
159 391696 : if (!e->ptr) {
160 385010 : EB_MEMCPY(e, new_item, sizeof(MemoryEntry));
161 385010 : return EB_TRUE;
162 : }
163 6686 : return EB_FALSE;
164 : }
165 :
166 :
167 385010 : void eb_add_mem_entry(void* ptr, EbPtrType type, size_t count, const char* file, uint32_t line)
168 : {
169 : MemoryEntry item;
170 385010 : item.ptr = ptr;
171 385010 : item.type = type;
172 385010 : item.count = count;
173 385010 : item.file = file;
174 385010 : item.line = line;
175 385010 : if (for_each_mem_entry(hash(ptr), add_mem_entry, &item))
176 385010 : return;
177 0 : if (g_add_mem_entry_warning) {
178 0 : fprintf(stderr, "SVT: can't add memory entry.\r\n");
179 0 : fprintf(stderr, "SVT: You have memory leak or you need increase MEM_ENTRY_SIZE\r\n");
180 0 : g_add_mem_entry_warning = EB_FALSE;
181 : }
182 : }
183 :
184 391635 : static EbBool remove_mem_entry(MemoryEntry* e, void* param)
185 : {
186 391635 : MemoryEntry* item = (MemoryEntry*)param;
187 391635 : if (e->ptr == item->ptr) {
188 384950 : if (e->type == item->type) {
189 280922 : e->ptr = NULL;
190 280922 : return EB_TRUE;
191 104028 : } else if (e->type == EB_C_PTR && item->type == EB_N_PTR) {
192 : //speical case, we use EB_FREE to free calloced memory
193 104028 : e->ptr = NULL;
194 104028 : return EB_TRUE;
195 : }
196 : }
197 6685 : return EB_FALSE;
198 : }
199 :
200 442152 : void eb_remove_mem_entry(void* ptr, EbPtrType type)
201 : {
202 442152 : if (!ptr)
203 442152 : return;
204 : MemoryEntry item;
205 384950 : item.ptr = ptr;
206 384950 : item.type = type;
207 384950 : if (for_each_mem_entry(hash(ptr), remove_mem_entry, &item))
208 384950 : return;
209 0 : if (g_remove_mem_entry_warning) {
210 0 : fprintf(stderr, "SVT: something wrong. you freed a unallocated memory %p, type = %s\r\n", ptr, mem_type_name(type));
211 0 : g_remove_mem_entry_warning = EB_FALSE;
212 : }
213 : }
214 :
215 : typedef struct MemSummary {
216 : uint64_t amount[EB_PTR_TYPE_TOTAL];
217 : uint32_t occupied;
218 : } MemSummary;
219 :
220 8388610 : static EbBool count_mem_entry(MemoryEntry* e, void* param)
221 : {
222 8388610 : MemSummary* sum = (MemSummary*)param;
223 8388610 : if (e->ptr) {
224 384464 : sum->amount[e->type] += e->count;
225 384464 : sum->occupied++;
226 : }
227 8388610 : return EB_FALSE;
228 : }
229 :
230 28 : static void get_memory_usage_and_scale(uint64_t amount, double* usage, char* scale)
231 : {
232 28 : char scales[] = { ' ', 'K', 'M', 'G' };
233 : size_t i;
234 : uint64_t v;
235 88 : for (i = 1; i < sizeof(scales); i++) {
236 84 : v = (uint64_t)1 << (i * 10);
237 84 : if (amount < v)
238 24 : break;
239 : }
240 28 : i--;
241 28 : v = (uint64_t)1 << (i * 10);
242 28 : *usage = (double)amount / v;
243 28 : *scale = scales[i];
244 28 : }
245 :
246 : //this need more memory and cpu
247 : #define PROFILE_MEMORY_USAGE
248 : #ifdef PROFILE_MEMORY_USAGE
249 :
250 : //if we use a static array here, this size + sizeof(g_mem_entry) will exceed max size allowed on windows.
251 : static MemoryEntry* g_profile_entry;
252 :
253 0 : uint32_t hash_location(FILE* f, int line) {
254 : #define MASK32 ((((uint64_t)1)<<32)-1)
255 :
256 0 : uint64_t v = (uint64_t)f;
257 0 : uint64_t low32 = v & MASK32;
258 0 : return (uint32_t)((v >> 32) + low32 + line);
259 : }
260 :
261 3107200 : static EbBool add_location(MemoryEntry* e, void* param) {
262 3107200 : MemoryEntry* new_item = (MemoryEntry*)param;
263 3107200 : if (!e->ptr) {
264 268 : *e = *new_item;
265 268 : return EB_TRUE;
266 3106930 : } else if (e->file == new_item->file && e->line == new_item->line) {
267 229788 : e->count += new_item->count;
268 229788 : return EB_TRUE;
269 : }
270 : //to next position.
271 2877140 : return EB_FALSE;
272 : }
273 :
274 8388610 : static EbBool collect_mem(MemoryEntry* e, void* param) {
275 8388610 : EbPtrType type = *(EbPtrType*)param;
276 8388610 : if (e->ptr && e->type == type) {
277 230056 : for_each_hash_entry(g_profile_entry, 0, add_location, e);
278 : }
279 : //Loop entire bucket.
280 8388610 : return EB_FALSE;
281 : }
282 :
283 92275200 : static int compare_count(const void* a,const void* b)
284 : {
285 92275200 : const MemoryEntry* pa = (const MemoryEntry*)a;
286 92275200 : const MemoryEntry* pb = (const MemoryEntry*)b;
287 92275200 : if (pb->count < pa->count) return -1;
288 92270500 : if (pb->count == pa->count) return 0;
289 648 : return 1;
290 : }
291 :
292 2 : static void print_top_10_locations() {
293 2 : EbHandle m = get_malloc_mutex();
294 2 : EbPtrType type = EB_N_PTR;
295 2 : eb_block_on_mutex(m);
296 2 : g_profile_entry = (MemoryEntry*)calloc(MEM_ENTRY_SIZE, sizeof(MemoryEntry));
297 2 : if (!g_profile_entry) {
298 0 : fprintf(stderr, "not enough memory for memory profile");
299 0 : eb_release_mutex(m);
300 0 : return;
301 : }
302 :
303 2 : for_each_hash_entry(g_mem_entry, 0, collect_mem, &type);
304 2 : qsort(g_profile_entry, MEM_ENTRY_SIZE, sizeof(MemoryEntry), compare_count);
305 :
306 2 : printf("top 10 %s locations:\r\n", mem_type_name(type));
307 22 : for (int i = 0; i < 10; i++) {
308 : double usage;
309 : char scale;
310 20 : MemoryEntry* e = g_profile_entry + i;
311 20 : get_memory_usage_and_scale(e->count, &usage, &scale);
312 20 : printf("(%.2lf %cB): %s:%d\r\n", usage, scale, e->file, e->line);
313 : }
314 2 : free(g_profile_entry);
315 2 : eb_release_mutex(m);
316 : }
317 : #endif //PROFILE_MEMORY_USAGE
318 :
319 : static int g_component_count;
320 :
321 : #endif //DEBUG_MEMORY_USAGE
322 :
323 2 : void eb_print_memory_usage()
324 : {
325 : #ifdef DEBUG_MEMORY_USAGE
326 : MemSummary sum;
327 : double fulless;
328 : double usage;
329 : char scale;
330 2 : memset(&sum, 0, sizeof(MemSummary));
331 :
332 2 : for_each_mem_entry(0, count_mem_entry, &sum);
333 2 : printf("SVT Memory Usage:\r\n");
334 2 : get_memory_usage_and_scale(sum.amount[EB_N_PTR] + sum.amount[EB_C_PTR] + sum.amount[EB_A_PTR], &usage, &scale);
335 2 : printf(" total allocated memory: %.2lf %cB\r\n", usage, scale);
336 2 : get_memory_usage_and_scale(sum.amount[EB_N_PTR], &usage, &scale);
337 2 : printf(" malloced memory: %.2lf %cB\r\n", usage, scale);
338 2 : get_memory_usage_and_scale(sum.amount[EB_C_PTR], &usage, &scale);
339 2 : printf(" callocated memory: %.2lf %cB\r\n", usage, scale);
340 2 : get_memory_usage_and_scale(sum.amount[EB_A_PTR], &usage, &scale);
341 2 : printf(" allocated aligned memory: %.2lf %cB\r\n", usage, scale);
342 :
343 2 : printf(" mutex count: %d\r\n", (int)sum.amount[EB_MUTEX]);
344 2 : printf(" semaphore count: %d\r\n", (int)sum.amount[EB_SEMAPHORE]);
345 2 : printf(" thread count: %d\r\n", (int)sum.amount[EB_THREAD]);
346 2 : fulless = (double)sum.occupied / MEM_ENTRY_SIZE;
347 2 : printf(" hash table fulless: %f, hash bucket is %s\r\n", fulless, fulless < .3 ? "healthy":"too full" );
348 : #ifdef PROFILE_MEMORY_USAGE
349 2 : print_top_10_locations();
350 : #endif
351 : #endif
352 2 : }
353 :
354 :
355 2 : void eb_increase_component_count()
356 : {
357 : #ifdef DEBUG_MEMORY_USAGE
358 2 : EbHandle m = get_malloc_mutex();
359 2 : eb_block_on_mutex(m);
360 2 : g_component_count++;
361 2 : eb_release_mutex(m);
362 : #endif
363 2 : }
364 :
365 : #ifdef DEBUG_MEMORY_USAGE
366 8388610 : static EbBool print_leak(MemoryEntry* e, void* param)
367 : {
368 8388610 : if (e->ptr) {
369 60 : EbBool* leaked = (EbBool*)param;
370 60 : *leaked = EB_TRUE;
371 60 : fprintf(stderr, "SVt: %s leaked at %s:L%d\r\n", mem_type_name(e->type), e->file, e->line);
372 : }
373 : //loop through all items
374 8388610 : return EB_FALSE;
375 : }
376 : #endif
377 :
378 2 : void eb_decrease_component_count()
379 : {
380 : #ifdef DEBUG_MEMORY_USAGE
381 2 : EbHandle m = get_malloc_mutex();
382 2 : eb_block_on_mutex(m);
383 2 : g_component_count--;
384 2 : if (!g_component_count) {
385 2 : EbBool leaked = EB_FALSE;
386 2 : for_each_hash_entry(g_mem_entry, 0, print_leak, &leaked);
387 2 : if (!leaked) {
388 1 : printf("SVT: you have no memory leak\r\n");
389 : }
390 : }
391 2 : eb_release_mutex(m);
392 : #endif
393 2 : }
|