diff options
Diffstat (limited to 'gcc/ggc-common.c')
-rw-r--r-- | gcc/ggc-common.c | 379 |
1 files changed, 144 insertions, 235 deletions
diff --git a/gcc/ggc-common.c b/gcc/ggc-common.c index eff326a..2e94ca4 100644 --- a/gcc/ggc-common.c +++ b/gcc/ggc-common.c @@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see #include "plugin.h" #include "vec.h" #include "timevar.h" +#include "mem-stats.h" /* When set, ggc_collect will do collection. */ bool ggc_force_collect; @@ -830,273 +831,181 @@ init_ggc_heuristics (void) #endif } -/* Datastructure used to store per-call-site statistics. */ -struct ggc_loc_descriptor +/* GGC memory usage. */ +struct ggc_usage: public mem_usage { - const char *file; - int line; - const char *function; - int times; - size_t allocated; - size_t overhead; - size_t freed; - size_t collected; -}; + /* Default constructor. */ + ggc_usage (): m_freed (0), m_collected (0), m_overhead (0) {} + /* Constructor. */ + ggc_usage (size_t allocated, size_t times, size_t peak, + size_t freed, size_t collected, size_t overhead) + : mem_usage (allocated, times, peak), + m_freed (freed), m_collected (collected), m_overhead (overhead) {} -/* Hash table helper. */ + /* Comparison operator. */ + inline bool operator< (const ggc_usage &second) const + { + return (get_balance () == second.get_balance () ? + (m_peak == second.m_peak ? m_times < second.m_times + : m_peak < second.m_peak) + : get_balance () < second.get_balance ()); + } -struct ggc_loc_desc_hasher : typed_noop_remove <ggc_loc_descriptor> -{ - typedef ggc_loc_descriptor *value_type; - typedef ggc_loc_descriptor *compare_type; - static inline hashval_t hash (const ggc_loc_descriptor *); - static inline bool equal (const ggc_loc_descriptor *, - const ggc_loc_descriptor *); -}; + /* Register overhead of ALLOCATED and OVERHEAD bytes. */ + inline void register_overhead (size_t allocated, size_t overhead) + { + m_allocated += allocated; + m_overhead += overhead; + m_times++; + } -inline hashval_t -ggc_loc_desc_hasher::hash (const ggc_loc_descriptor *d) -{ - return htab_hash_pointer (d->function) | d->line; -} + /* Release overhead of SIZE bytes. */ + inline void release_overhead (size_t size) + { + m_freed += size; + } -inline bool -ggc_loc_desc_hasher::equal (const ggc_loc_descriptor *d, - const ggc_loc_descriptor *d2) -{ - return (d->file == d2->file && d->line == d2->line - && d->function == d2->function); -} + /* Sum the usage with SECOND usage. */ + ggc_usage operator+ (const ggc_usage &second) + { + return ggc_usage (m_allocated + second.m_allocated, + m_times + second.m_times, + m_peak + second.m_peak, + m_freed + second.m_freed, + m_collected + second.m_collected, + m_overhead + second.m_overhead); + } -/* Hashtable used for statistics. */ -static hash_table<ggc_loc_desc_hasher> *loc_hash; + /* Dump usage with PREFIX, where TOTAL is sum of all rows. */ + inline void dump (const char *prefix, ggc_usage &total) const + { + long balance = get_balance (); + fprintf (stderr, + "%-48s %10li:%5.1f%%%10li:%5.1f%%" + "%10li:%5.1f%%%10li:%5.1f%%%10li\n", + prefix, (long)m_collected, + get_percent (m_collected, total.m_collected), + (long)m_freed, get_percent (m_freed, total.m_freed), + (long)balance, get_percent (balance, total.get_balance ()), + (long)m_overhead, get_percent (m_overhead, total.m_overhead), + (long)m_times); + } -struct ggc_ptr_hash_entry -{ - void *ptr; - struct ggc_loc_descriptor *loc; - size_t size; -}; + /* Dump usage coupled to LOC location, where TOTAL is sum of all rows. */ + inline void dump (mem_location *loc, ggc_usage &total) const + { + char s[4096]; + sprintf (s, "%s:%i (%s)", loc->get_trimmed_filename (), + loc->m_line, loc->m_function); + s[48] = '\0'; -/* Helper for ptr_hash table. */ + dump (s, total); + } -struct ptr_hash_hasher : typed_noop_remove <ggc_ptr_hash_entry> -{ - typedef ggc_ptr_hash_entry *value_type; - typedef void *compare_type; - static inline hashval_t hash (const ggc_ptr_hash_entry *); - static inline bool equal (const ggc_ptr_hash_entry *, const void *); -}; + /* Dump footer. */ + inline void dump_footer () + { + print_dash_line (); + dump ("Total", *this); + print_dash_line (); + } -inline hashval_t -ptr_hash_hasher::hash (const ggc_ptr_hash_entry *d) -{ - return htab_hash_pointer (d->ptr); -} + /* Get balance which is GGC allocation leak. */ + inline long get_balance () const + { + return m_allocated + m_overhead - m_collected - m_freed; + } -inline bool -ptr_hash_hasher::equal (const ggc_ptr_hash_entry *p, const void *p2) -{ - return (p->ptr == p2); -} + typedef std::pair<mem_location *, ggc_usage *> mem_pair_t; -/* Hashtable converting address of allocated field to loc descriptor. */ -static hash_table<ptr_hash_hasher> *ptr_hash; + /* Compare wrapper used by qsort method. */ + static int compare (const void *first, const void *second) + { + const mem_pair_t f = *(const mem_pair_t *)first; + const mem_pair_t s = *(const mem_pair_t *)second; -/* Return descriptor for given call site, create new one if needed. */ -static struct ggc_loc_descriptor * -make_loc_descriptor (const char *name, int line, const char *function) -{ - struct ggc_loc_descriptor loc; - struct ggc_loc_descriptor **slot; - - loc.file = name; - loc.line = line; - loc.function = function; - if (!loc_hash) - loc_hash = new hash_table<ggc_loc_desc_hasher> (10); - - slot = loc_hash->find_slot (&loc, INSERT); - if (*slot) - return *slot; - *slot = XCNEW (struct ggc_loc_descriptor); - (*slot)->file = name; - (*slot)->line = line; - (*slot)->function = function; - return *slot; -} + return (*f.second) < (*s.second); + } + + /* Compare rows in final GGC summary dump. */ + static int compare_final (const void *first, const void *second) + { typedef std::pair<mem_location *, ggc_usage *> mem_pair_t; + + const ggc_usage *f = ((const mem_pair_t *)first)->second; + const ggc_usage *s = ((const mem_pair_t *)second)->second; + + size_t a = f->m_allocated + f->m_overhead - f->m_freed; + size_t b = s->m_allocated + s->m_overhead - s->m_freed; + + return a == b ? 0 : (a < b ? 1 : -1); + } + + /* Dump header with NAME. */ + static inline void dump_header (const char *name) + { + fprintf (stderr, "%-48s %11s%17s%17s%16s%17s\n", name, "Garbage", "Freed", + "Leak", "Overhead", "Times"); + print_dash_line (); + } + + /* Freed memory in bytes. */ + size_t m_freed; + /* Collected memory in bytes. */ + size_t m_collected; + /* Overhead memory in bytes. */ + size_t m_overhead; +}; + +/* GCC memory description. */ +static mem_alloc_description<ggc_usage> ggc_mem_desc; + +/* Dump per-site memory statistics. */ -/* Record ALLOCATED and OVERHEAD bytes to descriptor NAME:LINE (FUNCTION). */ void -ggc_record_overhead (size_t allocated, size_t overhead, void *ptr, - const char *name, int line, const char *function) +dump_ggc_loc_statistics (bool final) { - struct ggc_loc_descriptor *loc = make_loc_descriptor (name, line, function); - struct ggc_ptr_hash_entry *p = XNEW (struct ggc_ptr_hash_entry); - ggc_ptr_hash_entry **slot; - - p->ptr = ptr; - p->loc = loc; - p->size = allocated + overhead; - if (!ptr_hash) - ptr_hash = new hash_table<ptr_hash_hasher> (10); - slot = ptr_hash->find_slot_with_hash (ptr, htab_hash_pointer (ptr), INSERT); - gcc_assert (!*slot); - *slot = p; - - loc->times++; - loc->allocated+=allocated; - loc->overhead+=overhead; -} + if (! GATHER_STATISTICS) + return; -/* Helper function for prune_overhead_list. See if SLOT is still marked and - remove it from hashtable if it is not. */ -int -ggc_prune_ptr (ggc_ptr_hash_entry **slot, void *b ATTRIBUTE_UNUSED) -{ - struct ggc_ptr_hash_entry *p = *slot; - if (!ggc_marked_p (p->ptr)) - { - p->loc->collected += p->size; - ptr_hash->clear_slot (slot); - free (p); - } - return 1; + ggc_force_collect = true; + ggc_collect (); + + ggc_mem_desc.dump (GGC, final ? ggc_usage::compare_final : NULL); + + ggc_force_collect = false; } -/* After live values has been marked, walk all recorded pointers and see if - they are still live. */ +/* Record ALLOCATED and OVERHEAD bytes to descriptor NAME:LINE (FUNCTION). */ void -ggc_prune_overhead_list (void) +ggc_record_overhead (size_t allocated, size_t overhead, void *ptr MEM_STAT_DECL) { - ptr_hash->traverse <void *, ggc_prune_ptr> (NULL); + ggc_usage *usage = ggc_mem_desc.register_descriptor (ptr, GGC, false + FINAL_PASS_MEM_STAT); + + ggc_mem_desc.register_object_overhead (usage, allocated + overhead, ptr); + usage->register_overhead (allocated, overhead); } /* Notice that the pointer has been freed. */ void ggc_free_overhead (void *ptr) { - ggc_ptr_hash_entry **slot - = ptr_hash->find_slot_with_hash (ptr, htab_hash_pointer (ptr), NO_INSERT); - struct ggc_ptr_hash_entry *p; - /* The pointer might be not found if a PCH read happened between allocation - and ggc_free () call. FIXME: account memory properly in the presence of - PCH. */ - if (!slot) - return; - p = (struct ggc_ptr_hash_entry *) *slot; - p->loc->freed += p->size; - ptr_hash->clear_slot (slot); - free (p); -} - -/* Helper for qsort; sort descriptors by amount of memory consumed. */ -static int -final_cmp_statistic (const void *loc1, const void *loc2) -{ - const struct ggc_loc_descriptor *const l1 = - *(const struct ggc_loc_descriptor *const *) loc1; - const struct ggc_loc_descriptor *const l2 = - *(const struct ggc_loc_descriptor *const *) loc2; - long diff; - diff = ((long)(l1->allocated + l1->overhead - l1->freed) - - (l2->allocated + l2->overhead - l2->freed)); - return diff > 0 ? 1 : diff < 0 ? -1 : 0; + ggc_mem_desc.release_object_overhead (ptr); } -/* Helper for qsort; sort descriptors by amount of memory consumed. */ -static int -cmp_statistic (const void *loc1, const void *loc2) -{ - const struct ggc_loc_descriptor *const l1 = - *(const struct ggc_loc_descriptor *const *) loc1; - const struct ggc_loc_descriptor *const l2 = - *(const struct ggc_loc_descriptor *const *) loc2; - long diff; - - diff = ((long)(l1->allocated + l1->overhead - l1->freed - l1->collected) - - (l2->allocated + l2->overhead - l2->freed - l2->collected)); - if (diff) - return diff > 0 ? 1 : diff < 0 ? -1 : 0; - diff = ((long)(l1->allocated + l1->overhead - l1->freed) - - (l2->allocated + l2->overhead - l2->freed)); - return diff > 0 ? 1 : diff < 0 ? -1 : 0; -} - -/* Collect array of the descriptors from hashtable. */ -static struct ggc_loc_descriptor **loc_array; -int -ggc_add_statistics (ggc_loc_descriptor **slot, int *n) -{ - loc_array[*n] = *slot; - (*n)++; - return 1; -} - -/* Dump per-site memory statistics. */ - +/* After live values has been marked, walk all recorded pointers and see if + they are still live. */ void -dump_ggc_loc_statistics (bool final) +ggc_prune_overhead_list (void) { - int nentries = 0; - char s[4096]; - size_t collected = 0, freed = 0, allocated = 0, overhead = 0, times = 0; - int i; + typedef hash_map<const void *, std::pair<ggc_usage *, size_t > > map_t; - if (! GATHER_STATISTICS) - return; + map_t::iterator it = ggc_mem_desc.m_reverse_object_map->begin (); - ggc_force_collect = true; - ggc_collect (); + for (; it != ggc_mem_desc.m_reverse_object_map->end (); ++it) + if (!ggc_marked_p ((*it).first)) + (*it).second.first->m_collected += (*it).second.second; - loc_array = XCNEWVEC (struct ggc_loc_descriptor *, - loc_hash->elements_with_deleted ()); - fprintf (stderr, "-------------------------------------------------------\n"); - fprintf (stderr, "\n%-48s %10s %10s %10s %10s %10s\n", - "source location", "Garbage", "Freed", "Leak", "Overhead", "Times"); - fprintf (stderr, "-------------------------------------------------------\n"); - loc_hash->traverse <int *, ggc_add_statistics> (&nentries); - qsort (loc_array, nentries, sizeof (*loc_array), - final ? final_cmp_statistic : cmp_statistic); - for (i = 0; i < nentries; i++) - { - struct ggc_loc_descriptor *d = loc_array[i]; - allocated += d->allocated; - times += d->times; - freed += d->freed; - collected += d->collected; - overhead += d->overhead; - } - for (i = 0; i < nentries; i++) - { - struct ggc_loc_descriptor *d = loc_array[i]; - if (d->allocated) - { - const char *s1 = d->file; - const char *s2; - while ((s2 = strstr (s1, "gcc/"))) - s1 = s2 + 4; - sprintf (s, "%s:%i (%s)", s1, d->line, d->function); - s[48] = 0; - fprintf (stderr, "%-48s %10li:%4.1f%% %10li:%4.1f%% %10li:%4.1f%% %10li:%4.1f%% %10li\n", s, - (long)d->collected, - (d->collected) * 100.0 / collected, - (long)d->freed, - (d->freed) * 100.0 / freed, - (long)(d->allocated + d->overhead - d->freed - d->collected), - (d->allocated + d->overhead - d->freed - d->collected) * 100.0 - / (allocated + overhead - freed - collected), - (long)d->overhead, - d->overhead * 100.0 / overhead, - (long)d->times); - } - } - fprintf (stderr, "%-48s %10ld %10ld %10ld %10ld %10ld\n", - "Total", (long)collected, (long)freed, - (long)(allocated + overhead - freed - collected), (long)overhead, - (long)times); - fprintf (stderr, "%-48s %10s %10s %10s %10s %10s\n", - "source location", "Garbage", "Freed", "Leak", "Overhead", "Times"); - fprintf (stderr, "-------------------------------------------------------\n"); - ggc_force_collect = false; + delete ggc_mem_desc.m_reverse_object_map; + ggc_mem_desc.m_reverse_object_map = new map_t (13, false, false); } |