LCOV - code coverage report
Current view: top level - Codec - EbMalloc.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 153 167 91.6 %
Date: 2019-11-25 17:38:06 Functions: 20 21 95.2 %

          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 : }

Generated by: LCOV version 1.14