aboutsummaryrefslogtreecommitdiff
path: root/gcc/ggc-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ggc-common.c')
-rw-r--r--gcc/ggc-common.c379
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);
}