From 2d44c7dea7c8d2917eec28a5e08e8f405aad8c3e Mon Sep 17 00:00:00 2001 From: Martin Liska Date: Wed, 27 May 2015 14:47:22 +0200 Subject: [PATCH] New memory allocation statistics infrastructure. * Makefile.in: Add additional dependencies related to memory report enhancement. * alloc-pool.c (allocate_pool_descriptor): Use new ctor. * bitmap.c (struct bitmap_descriptor_d): Remove. (struct loc): Likewise. (struct bitmap_desc_hasher): Likewise. (bitmap_desc_hasher::hash): Likewise. (bitmap_desc_hasher::equal): Likewise. (get_bitmap_descriptor): Likewise. (bitmap_register): User new memory descriptor API. (register_overhead): Likewise. (bitmap_find_bit): Register nsearches and search_iter statistics. (struct bitmap_output_info): Remove. (print_statistics): Likewise. (dump_bitmap_statistics): Use new memory descriptor. * bitmap.h (struct bitmap_usage): New class. * genmatch.c: Extend header file inclusion. * genpreds.c: Likewise. * ggc-common.c (struct ggc_usage): New class. (struct ggc_loc_desc_hasher): Remove. (ggc_loc_desc_hasher::hash): Likewise. (ggc_loc_desc_hasher::equal): Likewise. (struct ggc_ptr_hash_entry): Likewise. (struct ptr_hash_hasher): Likewise. (ptr_hash_hasher::hash): Likewise. (ptr_hash_hasher::equal): Likewise. (make_loc_descriptor): Likewise. (ggc_prune_ptr): Likewise. (dump_ggc_loc_statistics): Use new memory descriptor. (ggc_record_overhead): Likewise. (ggc_free_overhead): Likewise. (final_cmp_statistic): Remove. (cmp_statistic): Likewise. (ggc_add_statistics): Liekwise. (ggc_prune_overhead_list): Likewise. * hash-map-traits.h: New file. * hash-map.h (struct default_hashmap_traits): Move the traits to a separate header file. * hash-set.h: Pass memory statistics info to ctor. * hash-table.c (void dump_hash_table_loc_statistics): New function. * hash-table.h (hash_table::hash_table): Add new ctor arguments. (hash_table::~hash_table): Register memory release operation. (hash_table::alloc_entries): Handle memory allocation operation. (hash_table::expand): Likewise. * inchash.c (iterative_hash_hashval_t): Move implementation to header file. (iterative_hash_host_wide_int): Likewise. * inchash.h (class hash): Likewise. * mem-stats-traits.h: New file. * mem-stats.h: New file. (mem_location): Add new class. (mem_usage): Likewise. (mem_alloc_description): Likewise. * sese.c: Add new header file inclusision. * toplev.c (dump_memory_report): Add report for hash_table, hash_map and hash_set. * tree-sra.c: Add new header file inclusision. * vec.c (struct vec_descriptor): Remove. (hash_descriptor): Likewise. (struct vec_usage): Likewise. (struct ptr_hash_entry): Likewise. (hash_ptr): Likewise. (eq_ptr): Likewise. (vec_prefix::register_overhead): Use new memory descriptor API. (vec_prefix::release_overhead): Likewise. (add_statistics): Remove. (dump_vec_loc_statistics): Use new memory descriptor API. * vec.h (struct vec_prefix): Likewise. (va_heap::reserve): Likewise. (va_heap::release): Likewise. * emit-rtl.c (gen_raw_REG): Fix passing MEM_STAT. From-SVN: r223748 --- gcc/ChangeLog | 74 ++++++ gcc/Makefile.in | 10 +- gcc/alloc-pool.c | 4 +- gcc/bitmap.c | 178 ++----------- gcc/bitmap.h | 56 +++++ gcc/emit-rtl.c | 2 +- gcc/genmatch.c | 1 + gcc/genpreds.c | 1 + gcc/ggc-common.c | 379 +++++++++++----------------- gcc/hash-map-traits.h | 104 ++++++++ gcc/hash-map.h | 92 +------ gcc/hash-set.h | 5 +- gcc/hash-table.c | 13 +- gcc/hash-table.h | 51 +++- gcc/inchash.c | 51 ---- gcc/inchash.h | 65 ++++- gcc/mem-stats-traits.h | 20 ++ gcc/mem-stats.h | 550 +++++++++++++++++++++++++++++++++++++++++ gcc/sese.c | 1 + gcc/toplev.c | 1 + gcc/tree-sra.c | 1 + gcc/vec.c | 278 +++++++-------------- gcc/vec.h | 14 +- 23 files changed, 1215 insertions(+), 736 deletions(-) create mode 100644 gcc/hash-map-traits.h create mode 100644 gcc/mem-stats-traits.h create mode 100644 gcc/mem-stats.h diff --git a/gcc/ChangeLog b/gcc/ChangeLog index ffde59c1584..fa6ce5cd08b 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,77 @@ +2015-05-27 Martin Liska + + * Makefile.in: Add additional dependencies related to memory report + enhancement. + * alloc-pool.c (allocate_pool_descriptor): Use new ctor. + * bitmap.c (struct bitmap_descriptor_d): Remove. + (struct loc): Likewise. + (struct bitmap_desc_hasher): Likewise. + (bitmap_desc_hasher::hash): Likewise. + (bitmap_desc_hasher::equal): Likewise. + (get_bitmap_descriptor): Likewise. + (bitmap_register): User new memory descriptor API. + (register_overhead): Likewise. + (bitmap_find_bit): Register nsearches and search_iter statistics. + (struct bitmap_output_info): Remove. + (print_statistics): Likewise. + (dump_bitmap_statistics): Use new memory descriptor. + * bitmap.h (struct bitmap_usage): New class. + * genmatch.c: Extend header file inclusion. + * genpreds.c: Likewise. + * ggc-common.c (struct ggc_usage): New class. + (struct ggc_loc_desc_hasher): Remove. + (ggc_loc_desc_hasher::hash): Likewise. + (ggc_loc_desc_hasher::equal): Likewise. + (struct ggc_ptr_hash_entry): Likewise. + (struct ptr_hash_hasher): Likewise. + (ptr_hash_hasher::hash): Likewise. + (ptr_hash_hasher::equal): Likewise. + (make_loc_descriptor): Likewise. + (ggc_prune_ptr): Likewise. + (dump_ggc_loc_statistics): Use new memory descriptor. + (ggc_record_overhead): Likewise. + (ggc_free_overhead): Likewise. + (final_cmp_statistic): Remove. + (cmp_statistic): Likewise. + (ggc_add_statistics): Liekwise. + (ggc_prune_overhead_list): Likewise. + * hash-map-traits.h: New file. + * hash-map.h (struct default_hashmap_traits): Move the traits to a + separate header file. + * hash-set.h: Pass memory statistics info to ctor. + * hash-table.c (void dump_hash_table_loc_statistics): New function. + * hash-table.h (hash_table::hash_table): Add new ctor arguments. + (hash_table::~hash_table): Register memory release operation. + (hash_table::alloc_entries): Handle memory allocation operation. + (hash_table::expand): Likewise. + * inchash.c (iterative_hash_hashval_t): Move implementation to header + file. + (iterative_hash_host_wide_int): Likewise. + * inchash.h (class hash): Likewise. + * mem-stats-traits.h: New file. + * mem-stats.h: New file. + (mem_location): Add new class. + (mem_usage): Likewise. + (mem_alloc_description): Likewise. + * sese.c: Add new header file inclusision. + * toplev.c (dump_memory_report): Add report for hash_table, hash_map + and hash_set. + * tree-sra.c: Add new header file inclusision. + * vec.c (struct vec_descriptor): Remove. + (hash_descriptor): Likewise. + (struct vec_usage): Likewise. + (struct ptr_hash_entry): Likewise. + (hash_ptr): Likewise. + (eq_ptr): Likewise. + (vec_prefix::register_overhead): Use new memory descriptor API. + (vec_prefix::release_overhead): Likewise. + (add_statistics): Remove. + (dump_vec_loc_statistics): Use new memory descriptor API. + * vec.h (struct vec_prefix): Likewise. + (va_heap::reserve): Likewise. + (va_heap::release): Likewise. + * emit-rtl.c (gen_raw_REG): Fix passing MEM_STAT. + 2015-05-27 Richard Biener * tree-vect-stmts.c (vectorizable_load): Initialize slp_perm diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 45f210a7f3a..03c2ac6a62e 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1028,7 +1028,7 @@ BUILD_LIBS = $(BUILD_LIBIBERTY) BUILD_RTL = build/rtl.o build/read-rtl.o build/ggc-none.o \ build/vec.o build/min-insn-modes.o build/gensupport.o \ - build/print-rtl.o + build/print-rtl.o build/hash-table.o BUILD_MD = build/read-md.o BUILD_ERRORS = build/errors.o @@ -1501,7 +1501,7 @@ OBJS = \ # Objects in libcommon.a, potentially used by all host binaries and with # no target dependencies. OBJS-libcommon = diagnostic.o diagnostic-color.o pretty-print.o intl.o \ - vec.o input.o version.o + vec.o input.o version.o hash-table.o ggc-none.o # Objects in libcommon-target.a, used by drivers and by the core # compiler and containing target-dependent code. @@ -1937,7 +1937,7 @@ gcc-nm.c: gcc-ar.c cp $^ $@ COLLECT2_OBJS = collect2.o collect2-aix.o tlink.o vec.o ggc-none.o \ - collect-utils.o file-find.o + collect-utils.o file-find.o hash-table.o COLLECT2_LIBS = @COLLECT2_LIBS@ collect2$(exeext): $(COLLECT2_OBJS) $(LIBDEPS) # Don't try modifying collect2 (aka ld) in place--it might be linking this. @@ -2651,10 +2651,12 @@ s-iov: build/gcov-iov$(build_exeext) $(BASEVER) $(DEVPHASE) GCOV_OBJS = gcov.o gcov$(exeext): $(GCOV_OBJS) $(LIBDEPS) - +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_OBJS) $(LIBS) -o $@ + +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_OBJS) \ + build/hash-table.o ggc-none.o $(LIBS) -o $@ GCOV_DUMP_OBJS = gcov-dump.o gcov-dump$(exeext): $(GCOV_DUMP_OBJS) $(LIBDEPS) +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) $(GCOV_DUMP_OBJS) \ + build/hash-table.o build/ggc-none.o\ $(LIBS) -o $@ GCOV_TOOL_DEP_FILES = $(srcdir)/../libgcc/libgcov-util.c gcov-io.c $(GCOV_IO_H) \ diff --git a/gcc/alloc-pool.c b/gcc/alloc-pool.c index 81909d8a247..e34acdb3500 100644 --- a/gcc/alloc-pool.c +++ b/gcc/alloc-pool.c @@ -91,7 +91,9 @@ static struct alloc_pool_descriptor * allocate_pool_descriptor (const char *name) { if (!alloc_pool_hash) - alloc_pool_hash = new hash_map (10); + alloc_pool_hash = new hash_map (10, + false, + false); return &alloc_pool_hash->get_or_insert (name); } diff --git a/gcc/bitmap.c b/gcc/bitmap.c index 58f443243a2..5fc46548beb 100644 --- a/gcc/bitmap.c +++ b/gcc/bitmap.c @@ -25,112 +25,26 @@ along with GCC; see the file COPYING3. If not see #include "bitmap.h" #include "hash-table.h" #include "vec.h" +#include "inchash.h" +#include "mem-stats.h" +#include "hash-map.h" -/* Store information about each particular bitmap, per allocation site. */ -struct bitmap_descriptor_d -{ - int id; - const char *function; - const char *file; - int line; - int created; - uint64_t allocated; - uint64_t peak; - uint64_t current; - uint64_t nsearches; - uint64_t search_iter; -}; - -typedef struct bitmap_descriptor_d *bitmap_descriptor; -typedef const struct bitmap_descriptor_d *const_bitmap_descriptor; - -/* Next available unique id number for bitmap desciptors. */ -static int next_bitmap_desc_id = 0; - -/* Vector mapping descriptor ids to descriptors. */ -static vec bitmap_descriptors; - -/* Hashtable helpers. */ - -struct loc -{ - const char *file; - const char *function; - int line; -}; - -struct bitmap_desc_hasher : typed_noop_remove -{ - typedef bitmap_descriptor_d *value_type; - typedef loc *compare_type; - static inline hashval_t hash (const bitmap_descriptor_d *); - static inline bool equal (const bitmap_descriptor_d *, const loc *); -}; - -inline hashval_t -bitmap_desc_hasher::hash (const bitmap_descriptor_d *d) -{ - return htab_hash_pointer (d->file) + d->line; -} - -inline bool -bitmap_desc_hasher::equal (const bitmap_descriptor_d *d, const loc *l) -{ - return d->file == l->file && d->function == l->function && d->line == l->line; -} - -/* Hashtable mapping bitmap names to descriptors. */ -static hash_table *bitmap_desc_hash; - -/* For given file and line, return descriptor, create new if needed. */ -static bitmap_descriptor -get_bitmap_descriptor (const char *file, int line, const char *function) -{ - bitmap_descriptor_d **slot; - struct loc loc; - - loc.file = file; - loc.function = function; - loc.line = line; - - if (!bitmap_desc_hash) - bitmap_desc_hash = new hash_table (10); - - slot - = bitmap_desc_hash->find_slot_with_hash (&loc, - htab_hash_pointer (file) + line, - INSERT); - if (*slot) - return *slot; - - *slot = XCNEW (struct bitmap_descriptor_d); - bitmap_descriptors.safe_push (*slot); - (*slot)->id = next_bitmap_desc_id++; - (*slot)->file = file; - (*slot)->function = function; - (*slot)->line = line; - return *slot; -} +/* Memory allocation statistics purpose instance. */ +mem_alloc_description bitmap_mem_desc; /* Register new bitmap. */ void bitmap_register (bitmap b MEM_STAT_DECL) { - bitmap_descriptor desc = get_bitmap_descriptor (ALONE_FINAL_PASS_MEM_STAT); - desc->created++; - b->descriptor_id = desc->id; + bitmap_mem_desc.register_descriptor (b, BITMAP, false FINAL_PASS_MEM_STAT); } /* Account the overhead. */ static void register_overhead (bitmap b, int amount) { - bitmap_descriptor desc = bitmap_descriptors[b->descriptor_id]; - desc->current += amount; - if (amount > 0) - desc->allocated += amount; - if (desc->peak < desc->current) - desc->peak = desc->current; + if (bitmap_mem_desc.contains_descriptor_for_instance (b)) + bitmap_mem_desc.register_instance_overhead (amount, b); } /* Global data */ @@ -579,10 +493,14 @@ bitmap_find_bit (bitmap head, unsigned int bit) && head->first->next == NULL) return NULL; + /* Usage can be NULL due to allocated bitmaps for which we do not + call initialize function. */ + bitmap_usage *usage = bitmap_mem_desc.get_descriptor_for_instance (head); + /* This bitmap has more than one element, and we're going to look through the elements list. Count that as a search. */ - if (GATHER_STATISTICS) - bitmap_descriptors[head->descriptor_id]->nsearches++; + if (GATHER_STATISTICS && usage) + usage->m_nsearches++; if (head->indx < indx) /* INDX is beyond head->indx. Search from head->current @@ -591,8 +509,8 @@ bitmap_find_bit (bitmap head, unsigned int bit) element->next != 0 && element->indx < indx; element = element->next) { - if (GATHER_STATISTICS) - bitmap_descriptors[head->descriptor_id]->search_iter++; + if (GATHER_STATISTICS && usage) + usage->m_search_iter++; } else if (head->indx / 2 < indx) @@ -602,8 +520,8 @@ bitmap_find_bit (bitmap head, unsigned int bit) element->prev != 0 && element->indx > indx; element = element->prev) { - if (GATHER_STATISTICS) - bitmap_descriptors[head->descriptor_id]->search_iter++; + if (GATHER_STATISTICS && usage) + usage->m_search_iter++; } else @@ -612,9 +530,9 @@ bitmap_find_bit (bitmap head, unsigned int bit) for (element = head->first; element->next != 0 && element->indx < indx; element = element->next) - if (GATHER_STATISTICS) + if (GATHER_STATISTICS && usage) { - bitmap_descriptors[head->descriptor_id]->search_iter++; + usage->m_search_iter++; } /* `element' is the nearest to the one we want. If it's not the one we @@ -2157,68 +2075,14 @@ bitmap_print (FILE *file, const_bitmap head, const char *prefix, fputs (suffix, file); } - -/* Used to accumulate statistics about bitmap sizes. */ -struct bitmap_output_info -{ - uint64_t size; - uint64_t count; -}; - -/* Called via hash_table::traverse. Output bitmap descriptor pointed out by - SLOT and update statistics. */ -int -print_statistics (bitmap_descriptor_d **slot, bitmap_output_info *i) -{ - bitmap_descriptor d = *slot; - char s[4096]; - - 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[41] = 0; - fprintf (stderr, - "%-41s %9u %15" PRId64" %15" PRId64" %15" PRId64 - " %10" PRId64" %10" PRId64"\n", - s, d->created, - d->allocated, d->peak, d->current, - d->nsearches, d->search_iter); - i->size += d->allocated; - i->count += d->created; - } - return 1; -} - /* Output per-bitmap memory usage statistics. */ void dump_bitmap_statistics (void) { - struct bitmap_output_info info; - if (! GATHER_STATISTICS) return; - if (!bitmap_desc_hash) - return; - - fprintf (stderr, - "\n%-41s %9s %15s %15s %15s %10s %10s\n", - "Bitmap", "Overall", - "Allocated", "Peak", "Leak", - "searched", "search_itr"); - fprintf (stderr, "---------------------------------------------------------------------------------\n"); - info.count = 0; - info.size = 0; - bitmap_desc_hash->traverse (&info); - fprintf (stderr, "---------------------------------------------------------------------------------\n"); - fprintf (stderr, - "%-41s %9" PRId64" %15" PRId64"\n", - "Total", info.count, info.size); - fprintf (stderr, "---------------------------------------------------------------------------------\n"); + bitmap_mem_desc.dump (BITMAP); } DEBUG_FUNCTION void diff --git a/gcc/bitmap.h b/gcc/bitmap.h index 3f9bbf3271c..40562f64c34 100644 --- a/gcc/bitmap.h +++ b/gcc/bitmap.h @@ -130,6 +130,62 @@ along with GCC; see the file COPYING3. If not see #include "hashtab.h" #include "statistics.h" #include "obstack.h" +#include "mem-stats.h" + +/* Bitmap memory usage. */ +struct bitmap_usage: public mem_usage +{ + /* Default contructor. */ + bitmap_usage (): m_nsearches (0), m_search_iter (0) {} + /* Constructor. */ + bitmap_usage (size_t allocated, size_t times, size_t peak, + uint64_t nsearches, uint64_t search_iter) + : mem_usage (allocated, times, peak), + m_nsearches (nsearches), m_search_iter (search_iter) {} + + /* Sum the usage with SECOND usage. */ + bitmap_usage operator+ (const bitmap_usage &second) + { + return bitmap_usage (m_allocated + second.m_allocated, + m_times + second.m_times, + m_peak + second.m_peak, + m_nsearches + second.m_nsearches, + m_search_iter + second.m_search_iter); + } + + /* Dump usage coupled to LOC location, where TOTAL is sum of all rows. */ + inline void dump (mem_location *loc, mem_usage &total) const + { + char s[4096]; + sprintf (s, "%s:%i (%s)", loc->get_trimmed_filename (), + loc->m_line, loc->m_function); + + s[48] = '\0'; + + fprintf (stderr, "%-48s %10li:%5.1f%%%10li%10li:%5.1f%%%12li%12li%10s\n", s, + (long)m_allocated, get_percent (m_allocated, total.m_allocated), + (long)m_peak, (long)m_times, + get_percent (m_times, total.m_times), + (long)m_nsearches, (long)m_search_iter, + loc->m_ggc ? "ggc" : "heap"); + } + + /* Dump header with NAME. */ + static inline void dump_header (const char *name) + { + fprintf (stderr, "%-48s %11s%16s%17s%12s%12s%10s\n", name, "Leak", "Peak", + "Times", "N searches", "Search iter", "Type"); + print_dash_line (); + } + + /* Number search operations. */ + uint64_t m_nsearches; + /* Number of search iterations. */ + uint64_t m_search_iter; +}; + +/* Bitmap memory description. */ +extern mem_alloc_description bitmap_mem_desc; /* Fundamental storage type for bitmap. */ diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c index 360f37d3793..49a15090fc1 100644 --- a/gcc/emit-rtl.c +++ b/gcc/emit-rtl.c @@ -454,7 +454,7 @@ set_mode_and_regno (rtx x, machine_mode mode, unsigned int regno) rtx gen_raw_REG (machine_mode mode, unsigned int regno) { - rtx x = rtx_alloc_stat (REG PASS_MEM_STAT); + rtx x = rtx_alloc_stat (REG MEM_STAT_INFO); set_mode_and_regno (x, mode, regno); REG_ATTRS (x) = NULL; ORIGINAL_REGNO (x) = regno; diff --git a/gcc/genmatch.c b/gcc/genmatch.c index 03947ba2fce..9dc3b8cf952 100644 --- a/gcc/genmatch.c +++ b/gcc/genmatch.c @@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see #include "errors.h" #include "hashtab.h" #include "hash-table.h" +#include "inchash.h" #include "hash-map.h" #include "hash-set.h" #include "vec.h" diff --git a/gcc/genpreds.c b/gcc/genpreds.c index dd14e20449a..576be864702 100644 --- a/gcc/genpreds.c +++ b/gcc/genpreds.c @@ -1492,6 +1492,7 @@ write_insn_preds_c (void) #include \"rtl.h\"\n\ #include \"hash-set.h\"\n\ #include \"machmode.h\"\n\ +#include \"hash-map.h\"\n\ #include \"vec.h\"\n\ #include \"double-int.h\"\n\ #include \"input.h\"\n\ diff --git a/gcc/ggc-common.c b/gcc/ggc-common.c index eff326a36c5..2e94ca4a6ef 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 -{ - 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 *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 -{ - 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_pair_t; -/* Hashtable converting address of allocated field to loc descriptor. */ -static hash_table *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 (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_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_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 (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 (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 > 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 (&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); } diff --git a/gcc/hash-map-traits.h b/gcc/hash-map-traits.h new file mode 100644 index 00000000000..ce9c4a74b40 --- /dev/null +++ b/gcc/hash-map-traits.h @@ -0,0 +1,104 @@ +/* A hash map traits. + Copyright (C) 2015 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef HASH_MAP_TRAITS_H +#define HASH_MAP_TRAITS_H + +/* Bacause mem-stats.h uses default hashmap traits, we have to + put the class to this separate header file. */ + +/* implement default behavior for traits when types allow it. */ + +struct default_hashmap_traits +{ + /* Hashes the passed in key. */ + + template + static hashval_t + hash (T *p) + { + return uintptr_t (p) >> 3; + } + + /* If the value converts to hashval_t just use it. */ + + template static hashval_t hash (T v) { return v; } + + /* Return true if the two keys passed as arguments are equal. */ + + template + static bool + equal_keys (const T &a, const T &b) + { + return a == b; + } + + /* Called to dispose of the key and value before marking the entry as + deleted. */ + + template static void remove (T &v) { v.~T (); } + + /* Mark the passed in entry as being deleted. */ + + template + static void + mark_deleted (T &e) + { + mark_key_deleted (e.m_key); + } + + /* Mark the passed in entry as being empty. */ + + template + static void + mark_empty (T &e) + { + mark_key_empty (e.m_key); + } + + /* Return true if the passed in entry is marked as deleted. */ + + template + static bool + is_deleted (T &e) + { + return e.m_key == (void *)1; + } + + /* Return true if the passed in entry is marked as empty. */ + + template static bool is_empty (T &e) { return e.m_key == NULL; } + +private: + template + static void + mark_key_deleted (T *&k) + { + k = reinterpret_cast (1); + } + + template + static void + mark_key_empty (T *&k) + { + k = static_cast (0); + } +}; + +#endif // HASH_MAP_TRAITS_H diff --git a/gcc/hash-map.h b/gcc/hash-map.h index 4cca702a0e2..fb1a522a25d 100644 --- a/gcc/hash-map.h +++ b/gcc/hash-map.h @@ -24,87 +24,12 @@ along with GCC; see the file COPYING3. If not see #include #include #include "hash-table.h" - -/* implement default behavior for traits when types allow it. */ - -struct default_hashmap_traits -{ - /* Hashes the passed in key. */ - - template - static hashval_t - hash (T *p) - { - return uintptr_t(p) >> 3; - } - - /* If the value converts to hashval_t just use it. */ - - template static hashval_t hash (T v) { return v; } - - /* Return true if the two keys passed as arguments are equal. */ - - template - static bool - equal_keys (const T &a, const T &b) - { - return a == b; - } - - /* Called to dispose of the key and value before marking the entry as - deleted. */ - - template static void remove (T &v) { v.~T (); } - - /* Mark the passed in entry as being deleted. */ - - template - static void - mark_deleted (T &e) - { - mark_key_deleted (e.m_key); - } - - /* Mark the passed in entry as being empty. */ - - template - static void - mark_empty (T &e) - { - mark_key_empty (e.m_key); - } - - /* Return true if the passed in entry is marked as deleted. */ - - template - static bool - is_deleted (T &e) - { - return e.m_key == (void *)1; - } - - /* Return true if the passed in entry is marked as empty. */ - - template static bool is_empty (T &e) { return e.m_key == NULL; } - -private: - template - static void - mark_key_deleted (T *&k) - { - k = reinterpret_cast (1); - } - - template - static void - mark_key_empty (T *&k) - { - k = static_cast (0); - } -}; +#include "hash-map-traits.h" +#include "mem-stats.h" +#include "vec.h" template + typename Traits> class GTY((user)) hash_map { struct hash_entry @@ -187,13 +112,16 @@ class GTY((user)) hash_map }; public: - explicit hash_map (size_t n = 13, bool ggc = false) : m_table (n, ggc) {} + explicit hash_map (size_t n = 13, bool ggc = false, + bool gather_mem_stats = true CXX_MEM_STAT_INFO) + : m_table (n, ggc, gather_mem_stats, HASH_MAP PASS_MEM_STAT) {} /* Create a hash_map in ggc memory. */ - static hash_map *create_ggc (size_t size) + static hash_map *create_ggc (size_t size, bool gather_mem_stats = true + CXX_MEM_STAT_INFO) { hash_map *map = ggc_alloc (); - new (map) hash_map (size, true); + new (map) hash_map (size, true, gather_mem_stats PASS_MEM_STAT); return map; } diff --git a/gcc/hash-set.h b/gcc/hash-set.h index 906545143a7..b4db21acedb 100644 --- a/gcc/hash-set.h +++ b/gcc/hash-set.h @@ -33,7 +33,7 @@ struct default_hashset_traits static hashval_t hash (T *p) { - return uintptr_t(p) >> 3; + return uintptr_t (p) >> 3; } template static hashval_t hash(const T &v) { return v; } @@ -180,7 +180,8 @@ class hash_set }; public: - explicit hash_set (size_t n = 13, bool ggc = false) : m_table (n, ggc) {} + explicit hash_set (size_t n = 13, bool ggc = false CXX_MEM_STAT_INFO) + : m_table (n, ggc, true, HASH_SET PASS_MEM_STAT) {} /* Create a hash_set in gc memory with space for at least n elements. */ diff --git a/gcc/hash-table.c b/gcc/hash-table.c index 3127e9c8394..012b241906d 100644 --- a/gcc/hash-table.c +++ b/gcc/hash-table.c @@ -31,7 +31,6 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "hash-table.h" - /* Table of primes and multiplicative inverses. Note that these are not minimally reduced inverses. Unlike when generating @@ -99,3 +98,15 @@ hash_table_higher_prime_index (unsigned long n) return low; } +mem_alloc_description hash_table_usage; + +/* Support function for statistics. */ +void dump_hash_table_loc_statistics (void) +{ + for (unsigned i = HASH_TABLE; i <= HASH_SET; i++) + { + mem_alloc_origin origin = (mem_alloc_origin) i; + hash_table_usage.dump (origin); + } +} + diff --git a/gcc/hash-table.h b/gcc/hash-table.h index f6375d1ec71..74c3722bbf3 100644 --- a/gcc/hash-table.h +++ b/gcc/hash-table.h @@ -199,6 +199,7 @@ along with GCC; see the file COPYING3. If not see #include "ggc.h" #include "hashtab.h" #include +#include "mem-stats-traits.h" template class hash_map; template class hash_set; @@ -551,6 +552,8 @@ struct mark_empty_helper } }; +class mem_usage; + /* User-facing hash table type. The table stores elements of type Descriptor::value_type. @@ -583,16 +586,18 @@ class hash_table typedef typename Descriptor::compare_type compare_type; public: - explicit hash_table (size_t, bool ggc = false CXX_MEM_STAT_INFO); + explicit hash_table (size_t, bool ggc = false, bool gather_mem_stats = true, + mem_alloc_origin origin = HASH_TABLE + CXX_MEM_STAT_INFO); ~hash_table (); /* Create a hash_table in gc memory. */ static hash_table * - create_ggc (size_t n) + create_ggc (size_t n CXX_MEM_STAT_INFO) { hash_table *table = ggc_alloc (); - new (table) hash_table (n, true); + new (table) hash_table (n, true, true, HASH_TABLE PASS_MEM_STAT); return table; } @@ -759,19 +764,40 @@ private: /* if m_entries is stored in ggc memory. */ bool m_ggc; + + /* If we should gather memory statistics for the table. */ + bool m_gather_mem_stats; }; +/* As mem-stats.h heavily utilizes hash maps (hash tables), we have to include + mem-stats.h after hash_table declaration. */ + +#include "mem-stats.h" +#include "hash-map.h" +#include "vec.h" + +extern mem_alloc_description hash_table_usage; + +/* Support function for statistics. */ +extern void dump_hash_table_loc_statistics (void); + template class Allocator> -hash_table::hash_table (size_t size, bool ggc - MEM_STAT_DECL) : +hash_table::hash_table (size_t size, bool ggc, bool + gather_mem_stats, + mem_alloc_origin origin + MEM_STAT_DECL) : m_n_elements (0), m_n_deleted (0), m_searches (0), m_collisions (0), - m_ggc (ggc) + m_ggc (ggc), m_gather_mem_stats (gather_mem_stats) { unsigned int size_prime_index; size_prime_index = hash_table_higher_prime_index (size); size = prime_tab[size_prime_index].prime; + if (m_gather_mem_stats) + hash_table_usage.register_descriptor (this, origin, ggc + FINAL_PASS_MEM_STAT); + m_entries = alloc_entries (size PASS_MEM_STAT); m_size = size; m_size_prime_index = size_prime_index; @@ -788,6 +814,11 @@ hash_table::~hash_table () Allocator ::data_free (m_entries); else ggc_free (m_entries); + + if (m_gather_mem_stats) + hash_table_usage.release_instance_overhead (this, + sizeof (value_type) * m_size, + true); } /* This function returns an array of empty hash table elements. */ @@ -798,6 +829,9 @@ hash_table::alloc_entries (size_t n MEM_STAT_DECL) const { value_type *nentries; + if (m_gather_mem_stats) + hash_table_usage.register_instance_overhead (sizeof (value_type) * n, this); + if (!m_ggc) nentries = Allocator ::data_alloc (n); else @@ -881,6 +915,11 @@ hash_table::expand () } value_type *nentries = alloc_entries (nsize); + + if (m_gather_mem_stats) + hash_table_usage.release_instance_overhead (this, sizeof (value_type) + * osize); + m_entries = nentries; m_size = nsize; m_size_prime_index = nindex; diff --git a/gcc/inchash.c b/gcc/inchash.c index c555046dd3a..90c62e82867 100644 --- a/gcc/inchash.c +++ b/gcc/inchash.c @@ -26,54 +26,3 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "hashtab.h" #include "inchash.h" - -/* Borrowed from hashtab.c iterative_hash implementation. */ -#define mix(a,b,c) \ -{ \ - a -= b; a -= c; a ^= (c>>13); \ - b -= c; b -= a; b ^= (a<< 8); \ - c -= a; c -= b; c ^= ((b&0xffffffff)>>13); \ - a -= b; a -= c; a ^= ((c&0xffffffff)>>12); \ - b -= c; b -= a; b = (b ^ (a<<16)) & 0xffffffff; \ - c -= a; c -= b; c = (c ^ (b>> 5)) & 0xffffffff; \ - a -= b; a -= c; a = (a ^ (c>> 3)) & 0xffffffff; \ - b -= c; b -= a; b = (b ^ (a<<10)) & 0xffffffff; \ - c -= a; c -= b; c = (c ^ (b>>15)) & 0xffffffff; \ -} - - -/* Produce good hash value combining VAL and VAL2. */ -hashval_t -iterative_hash_hashval_t (hashval_t val, hashval_t val2) -{ - /* the golden ratio; an arbitrary value. */ - hashval_t a = 0x9e3779b9; - - mix (a, val, val2); - return val2; -} - -/* Produce good hash value combining VAL and VAL2. */ - -hashval_t -iterative_hash_host_wide_int (HOST_WIDE_INT val, hashval_t val2) -{ - if (sizeof (HOST_WIDE_INT) == sizeof (hashval_t)) - return iterative_hash_hashval_t (val, val2); - else - { - hashval_t a = (hashval_t) val; - /* Avoid warnings about shifting of more than the width of the type on - hosts that won't execute this path. */ - int zero = 0; - hashval_t b = (hashval_t) (val >> (sizeof (hashval_t) * 8 + zero)); - mix (a, b, val2); - if (sizeof (HOST_WIDE_INT) > 2 * sizeof (hashval_t)) - { - hashval_t a = (hashval_t) (val >> (sizeof (hashval_t) * 16 + zero)); - hashval_t b = (hashval_t) (val >> (sizeof (hashval_t) * 24 + zero)); - mix (a, b, val2); - } - return val2; - } -} diff --git a/gcc/inchash.h b/gcc/inchash.h index 54fb0d853b2..4bb1272c1a9 100644 --- a/gcc/inchash.h +++ b/gcc/inchash.h @@ -36,8 +36,8 @@ along with GCC; see the file COPYING3. If not see Currently it just implements the plain old jhash based incremental hash from gcc's tree.c. */ -extern hashval_t iterative_hash_host_wide_int (HOST_WIDE_INT, hashval_t); -extern hashval_t iterative_hash_hashval_t (hashval_t, hashval_t); +hashval_t iterative_hash_host_wide_int (HOST_WIDE_INT, hashval_t); +hashval_t iterative_hash_hashval_t (hashval_t, hashval_t); namespace inchash { @@ -72,7 +72,7 @@ class hash } /* Hash in pointer PTR. */ - void add_ptr (void *ptr) + void add_ptr (const void *ptr) { add (&ptr, sizeof (ptr)); } @@ -121,13 +121,13 @@ class hash { if (a.end() > b.end()) { - merge (b); + merge (b); merge (a); } else { - merge (a); - merge (b); + merge (a); + merge (b); } } @@ -138,4 +138,57 @@ class hash } +/* Borrowed from hashtab.c iterative_hash implementation. */ +#define mix(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<< 8); \ + c -= a; c -= b; c ^= ((b&0xffffffff)>>13); \ + a -= b; a -= c; a ^= ((c&0xffffffff)>>12); \ + b -= c; b -= a; b = (b ^ (a<<16)) & 0xffffffff; \ + c -= a; c -= b; c = (c ^ (b>> 5)) & 0xffffffff; \ + a -= b; a -= c; a = (a ^ (c>> 3)) & 0xffffffff; \ + b -= c; b -= a; b = (b ^ (a<<10)) & 0xffffffff; \ + c -= a; c -= b; c = (c ^ (b>>15)) & 0xffffffff; \ +} + + +/* Produce good hash value combining VAL and VAL2. */ +inline +hashval_t +iterative_hash_hashval_t (hashval_t val, hashval_t val2) +{ + /* the golden ratio; an arbitrary value. */ + hashval_t a = 0x9e3779b9; + + mix (a, val, val2); + return val2; +} + +/* Produce good hash value combining VAL and VAL2. */ + +inline +hashval_t +iterative_hash_host_wide_int (HOST_WIDE_INT val, hashval_t val2) +{ + if (sizeof (HOST_WIDE_INT) == sizeof (hashval_t)) + return iterative_hash_hashval_t (val, val2); + else + { + hashval_t a = (hashval_t) val; + /* Avoid warnings about shifting of more than the width of the type on + hosts that won't execute this path. */ + int zero = 0; + hashval_t b = (hashval_t) (val >> (sizeof (hashval_t) * 8 + zero)); + mix (a, b, val2); + if (sizeof (HOST_WIDE_INT) > 2 * sizeof (hashval_t)) + { + hashval_t a = (hashval_t) (val >> (sizeof (hashval_t) * 16 + zero)); + hashval_t b = (hashval_t) (val >> (sizeof (hashval_t) * 24 + zero)); + mix (a, b, val2); + } + return val2; + } +} + #endif diff --git a/gcc/mem-stats-traits.h b/gcc/mem-stats-traits.h new file mode 100644 index 00000000000..de1614e49d6 --- /dev/null +++ b/gcc/mem-stats-traits.h @@ -0,0 +1,20 @@ +#ifndef GCC_MEM_STATS_TRAITS_H +#define GCC_MEM_STATS_TRAITS_H + +/* Memory allocation origin. */ +enum mem_alloc_origin +{ + HASH_TABLE, + HASH_MAP, + HASH_SET, + VEC, + BITMAP, + GGC, + MEM_ALLOC_ORIGIN_LENGTH +}; + +/* Verbose names of the memory allocation origin. */ +static const char * mem_alloc_origin_names[] = { "Hash tables", "Hash maps", + "Hash sets", "Heap vectors", "Bitmaps", "GGC memory" }; + +#endif // GCC_MEM_STATS_TRAITS_H diff --git a/gcc/mem-stats.h b/gcc/mem-stats.h new file mode 100644 index 00000000000..ac472315fd3 --- /dev/null +++ b/gcc/mem-stats.h @@ -0,0 +1,550 @@ +#ifndef GCC_MEM_STATS_H +#define GCC_MEM_STATS_H + +#include "hash-map-traits.h" +#include "inchash.h" +#include "mem-stats-traits.h" +#include "vec.h" + +/* Forward declaration. */ +template +class hash_map; + +/* Memory allocation location. */ +struct mem_location +{ + /* Default constructor. */ + inline mem_location () {} + + /* Constructor. */ + inline mem_location (const char *filename, const char *function, int line, + mem_alloc_origin origin, bool ggc): + m_filename (filename), m_function (function), m_line (line), m_origin + (origin), m_ggc (ggc) {} + + /* Compute hash value based on file name, function name and line in + source code. As there is just a single pointer registered for every + constant that points to e.g. the same file name, we can use hash + of the pointer. */ + hashval_t hash () + { + inchash::hash hash; + + hash.add_ptr (m_filename); + hash.add_ptr (m_function); + hash.add_int (m_line); + + return hash.end (); + } + + /* Return true if the memory location is equal to OTHER. */ + int equal (mem_location &other) + { + return m_filename == other.m_filename && m_function == other.m_function + && m_line == other.m_line; + } + + /* Return trimmed filename for the location. */ + inline const char *get_trimmed_filename () + { + const char *s1 = m_filename; + const char *s2; + + while ((s2 = strstr (s1, "gcc/"))) + s1 = s2 + 4; + + return s1; + } + + /* Return display name associated to ORIGIN type. */ + static const char *get_origin_name (mem_alloc_origin origin) + { + return mem_alloc_origin_names[(unsigned) origin]; + } + + /* File name of source code. */ + const char *m_filename; + /* Funcation name. */ + const char *m_function; + /* Line number in source code. */ + int m_line; + /* Origin type. */ + mem_alloc_origin m_origin; + /* Flag if used by GGC allocation. */ + bool m_ggc; +}; + +/* Memory usage register to a memory location. */ +struct mem_usage +{ + /* Default constructor. */ + mem_usage (): m_allocated (0), m_times (0), m_peak (0) {} + + /* Constructor. */ + mem_usage (size_t allocated, size_t times, size_t peak): + m_allocated (allocated), m_times (times), m_peak (peak) {} + + /* Register overhead of SIZE bytes. */ + inline void register_overhead (size_t size) + { + m_allocated += size; + m_times++; + + if (m_peak < m_allocated) + m_peak = m_allocated; + } + + /* Release overhead of SIZE bytes. */ + inline void release_overhead (size_t size) + { + gcc_assert (size <= m_allocated); + + m_allocated -= size; + } + + /* Sum the usage with SECOND usage. */ + mem_usage operator+ (const mem_usage &second) + { + return mem_usage (m_allocated + second.m_allocated, + m_times + second.m_times, + m_peak + second.m_peak); + } + + /* Comparison operator. */ + inline bool operator< (const mem_usage &second) const + { + return (m_allocated == second.m_allocated ? + (m_peak == second.m_peak ? m_times < second.m_times + : m_peak < second.m_peak) : m_allocated < second.m_allocated); + } + + /* Compare wrapper used by qsort method. */ + static int compare (const void *first, const void *second) + { + typedef std::pair mem_pair_t; + + const mem_pair_t f = *(const mem_pair_t *)first; + const mem_pair_t s = *(const mem_pair_t *)second; + + return (*f.second) < (*s.second); + } + + /* Dump usage coupled to LOC location, where TOTAL is sum of all rows. */ + inline void dump (mem_location *loc, mem_usage &total) const + { + char s[4096]; + sprintf (s, "%s:%i (%s)", loc->get_trimmed_filename (), + loc->m_line, loc->m_function); + + s[48] = '\0'; + + fprintf (stderr, "%-48s %10li:%5.1f%%%10li%10li:%5.1f%%%10s\n", s, + (long)m_allocated, get_percent (m_allocated, total.m_allocated), + (long)m_peak, (long)m_times, + get_percent (m_times, total.m_times), loc->m_ggc ? "ggc" : "heap"); + } + + /* Dump footer. */ + inline void dump_footer () + { + print_dash_line (); + fprintf (stderr, "%s%54li%27li\n", "Total", (long)m_allocated, + (long)m_times); + print_dash_line (); + } + + /* Return fraction of NOMINATOR and DENOMINATOR in percent. */ + static inline float get_percent (size_t nominator, size_t denominator) + { + return denominator == 0 ? 0.0f : nominator * 100.0 / denominator; + } + + /* Print line made of dashes. */ + static inline void print_dash_line () + { + fprintf (stderr, "%s\n", std::string (128, '-').c_str ()); + } + + /* Dump header with NAME. */ + static inline void dump_header (const char *name) + { + fprintf (stderr, "%-48s %11s%16s%10s%17s\n", name, "Leak", "Peak", + "Times", "Type"); + print_dash_line (); + } + + /* Current number of allocated bytes. */ + size_t m_allocated; + /* Number of allocations. */ + size_t m_times; + /* Peak allocation in bytes. */ + size_t m_peak; +}; + +/* Memory usage pair that connectes memory usage and number + of allocated bytes. */ +template +struct mem_usage_pair +{ + mem_usage_pair (T *usage_, size_t allocated_): usage (usage_), + allocated (allocated_) {} + + T *usage; + size_t allocated; +}; + +/* Memory allocation description. */ +template +class mem_alloc_description +{ +public: + struct mem_alloc_hashmap_traits: default_hashmap_traits + { + static hashval_t + hash (const mem_location *l) + { + inchash::hash hstate; + + hstate.add_ptr ((const void *)l->m_filename); + hstate.add_ptr (l->m_function); + hstate.add_int (l->m_line); + + return hstate.end (); + } + + static bool + equal_keys (const mem_location *l1, const mem_location *l2) + { + return l1->m_filename == l2->m_filename + && l1->m_function == l2->m_function + && l1->m_line == l2->m_line; + } + }; + + /* Internal class type definitions. */ + typedef hash_map mem_map_t; + typedef hash_map , default_hashmap_traits> + reverse_mem_map_t; + typedef hash_map > reverse_object_map_t; + typedef std::pair mem_list_t; + + /* Default contructor. */ + mem_alloc_description (); + + /* Default destructor. */ + ~mem_alloc_description (); + + /* Returns true if instance PTR is registered by the memory description. */ + bool contains_descriptor_for_instance (const void *ptr); + + /* Return descriptor for instance PTR. */ + T *get_descriptor_for_instance (const void *ptr); + + /* Register memory allocation descriptor for container PTR. ORIGIN identifies + type of container and GGC identifes if the allocation is handled in GGC + memory. Each location is identified by file NAME, LINE in source code and + FUNCTION name. */ + T *register_descriptor (const void *ptr, mem_alloc_origin origin, + bool ggc, const char *name, int line, + const char *function); + + /* Register instance overhead identified by PTR pointer. Allocation takes + SIZE bytes. */ + T *register_instance_overhead (size_t size, const void *ptr); + + /* For containers (and GGC) where we want to track every instance object, + we register allocation of SIZE bytes, identified by PTR pointer, belonging + to USAGE descriptor. */ + void register_object_overhead (T *usage, size_t size, const void *ptr); + + /* Release PTR pointer of SIZE bytes. If REMOVE_FROM_MAP is set to true, + remove the instance from reverse map. */ + void release_instance_overhead (void *ptr, size_t size, + bool remove_from_map = false); + + /* Release intance object identified by PTR pointer. */ + void release_object_overhead (void *ptr); + + /* Get sum value for ORIGIN type of allocation for the descriptor. */ + T get_sum (mem_alloc_origin origin); + + /* Get all tracked instances registered by the description. Items + are filtered by ORIGIN type, LENGTH is return value where we register + the number of elements in the list. If we want to process custom order, + CMP comparator can be provided. */ + mem_list_t *get_list (mem_alloc_origin origin, unsigned *length, + int (*cmp) (const void *first, const void *second) + = NULL); + + /* Dump all tracked instances of type ORIGIN. If we want to process custom + order, CMP comparator can be provided. */ + void dump (mem_alloc_origin origin, + int (*cmp) (const void *first, const void *second) = NULL); + + /* Reverse object map used for every object allocation mapping. */ + reverse_object_map_t *m_reverse_object_map; + +private: + /* Register overhead of SIZE bytes of ORIGIN type. PTR pointer is allocated + in NAME source file, at LINE in source code, in FUNCTION. */ + T *register_overhead (size_t size, mem_alloc_origin origin, const char *name, + int line, const char *function, const void *ptr); + + /* Allocation location coupled to the description. */ + mem_location m_location; + + /* Location to usage mapping. */ + mem_map_t *m_map; + + /* Reverse pointer to usage mapping. */ + reverse_mem_map_t *m_reverse_map; +}; + +#include "hash-map.h" + +/* Returns true if instance PTR is registered by the memory description. */ + +template +inline bool +mem_alloc_description::contains_descriptor_for_instance (const void *ptr) +{ + return m_reverse_map->get (ptr); +} + +/* Return descriptor for instance PTR. */ + +template +inline T* +mem_alloc_description::get_descriptor_for_instance (const void *ptr) +{ + return m_reverse_map->get (ptr) ? (*m_reverse_map->get (ptr)).usage : NULL; +} + +/* Register memory allocation descriptor for container PTR. ORIGIN identifies + type of container and GGC identifes if the allocation is handled in GGC + memory. Each location is identified by file NAME, LINE in source code and + FUNCTION name. */ + +template +inline T* +mem_alloc_description::register_descriptor (const void *ptr, + mem_alloc_origin origin, + bool ggc, + const char *filename, + int line, + const char *function) +{ + mem_location *l = new mem_location (filename, function, line, origin, ggc); + T *usage = NULL; + + T **slot = m_map->get (l); + if (slot) + { + delete l; + usage = *slot; + } + else + { + usage = new T (); + m_map->put (l, usage); + } + + if (!m_reverse_map->get (ptr)) + m_reverse_map->put (ptr, mem_usage_pair (usage, 0)); + + return usage; +} + +/* Register instance overhead identified by PTR pointer. Allocation takes + SIZE bytes. */ + +template +inline T* +mem_alloc_description::register_instance_overhead (size_t size, + const void *ptr) +{ + mem_usage_pair *slot = m_reverse_map->get (ptr); + if (!slot) + { + /* Due to PCH, it can really happen. */ + return NULL; + } + + T *usage = (*slot).usage; + usage->register_overhead (size); + + return usage; +} + +/* For containers (and GGC) where we want to track every instance object, + we register allocation of SIZE bytes, identified by PTR pointer, belonging + to USAGE descriptor. */ + +template +void +mem_alloc_description::register_object_overhead (T *usage, size_t size, + const void *ptr) +{ + /* In case of GGC, it is possible to have already occupied the memory + location. */ + m_reverse_object_map->put (ptr, std::pair (usage, size)); +} + +/* Register overhead of SIZE bytes of ORIGIN type. PTR pointer is allocated + in NAME source file, at LINE in source code, in FUNCTION. */ + +template +inline T* +mem_alloc_description::register_overhead (size_t size, + mem_alloc_origin origin, + const char *filename, + int line, + const char *function, + const void *ptr) +{ + T *usage = register_descriptor (ptr, origin, filename, line, function); + usage->register_overhead (size); + + return usage; +} + +/* Release PTR pointer of SIZE bytes. */ + +template +inline void +mem_alloc_description::release_instance_overhead (void *ptr, size_t size, + bool remove_from_map) +{ + mem_usage_pair *slot = m_reverse_map->get (ptr); + + if (!slot) + { + /* Due to PCH, it can really happen. */ + return; + } + + mem_usage_pair usage_pair = *slot; + usage_pair.usage->release_overhead (size); + + if (remove_from_map) + m_reverse_map->remove (ptr); +} + +/* Release intance object identified by PTR pointer. */ + +template +inline void +mem_alloc_description::release_object_overhead (void *ptr) +{ + std::pair *entry = m_reverse_object_map->get (ptr); + if (entry) + { + entry->first->release_overhead (entry->second); + m_reverse_object_map->remove (ptr); + } +} + +/* Default contructor. */ + +template +inline +mem_alloc_description::mem_alloc_description () +{ + m_map = new mem_map_t (13, false, false); + m_reverse_map = new reverse_mem_map_t (13, false, false); + m_reverse_object_map = new reverse_object_map_t (13, false, false); +} + +/* Default destructor. */ + +template +inline +mem_alloc_description::~mem_alloc_description () +{ + for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end (); + ++it) + { + delete (*it).first; + delete (*it).second; + } + + delete m_map; + delete m_reverse_map; + delete m_reverse_object_map; +} + +/* Get all tracked instances registered by the description. Items are filtered + by ORIGIN type, LENGTH is return value where we register the number of + elements in the list. If we want to process custom order, CMP comparator + can be provided. */ + +template +inline +typename mem_alloc_description::mem_list_t * +mem_alloc_description::get_list (mem_alloc_origin origin, unsigned *length, + int (*cmp) (const void *first, const void *second)) +{ + /* vec data structure is not used because all vectors generate memory + allocation info a it would create a cycle. */ + size_t element_size = sizeof (mem_list_t); + mem_list_t *list = XCNEWVEC (mem_list_t, m_map->elements ()); + unsigned i = 0; + + for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end (); + ++it) + if ((*it).first->m_origin == origin) + list[i++] = std::pair (*it); + + qsort (list, i, element_size, cmp == NULL ? T::compare : cmp); + *length = i; + + return list; +} + +/* Get sum value for ORIGIN type of allocation for the descriptor. */ + +template +inline T +mem_alloc_description::get_sum (mem_alloc_origin origin) +{ + unsigned length; + mem_list_t *list = get_list (origin, &length); + T sum; + + for (unsigned i = 0; i < length; i++) + sum = sum + *list[i].second; + + XDELETEVEC (list); + + return sum; +} + +/* Dump all tracked instances of type ORIGIN. If we want to process custom + order, CMP comparator can be provided. */ + +template +inline void +mem_alloc_description::dump (mem_alloc_origin origin, + int (*cmp) (const void *first, + const void *second)) +{ + unsigned length; + + fprintf (stderr, "\n"); + + mem_list_t *list = get_list (origin, &length, cmp); + T total = get_sum (origin); + + T::dump_header (mem_location::get_origin_name (origin)); + for (int i = length - 1; i >= 0; i--) + list[i].second->dump (list[i].first, total); + + total.dump_footer (); + + XDELETEVEC (list); + + fprintf (stderr, "\n"); +} + +#endif // GCC_MEM_STATS_H diff --git a/gcc/sese.c b/gcc/sese.c index fb5b6365b02..c274547c85a 100644 --- a/gcc/sese.c +++ b/gcc/sese.c @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #include "system.h" #include "coretypes.h" +#include "mem-stats.h" #include "hash-map.h" #include "hash-set.h" #include "machmode.h" diff --git a/gcc/toplev.c b/gcc/toplev.c index 18a725a779f..3b57b482c3d 100644 --- a/gcc/toplev.c +++ b/gcc/toplev.c @@ -1963,6 +1963,7 @@ dump_memory_report (bool final) dump_rtx_statistics (); dump_alloc_pool_statistics (); dump_bitmap_statistics (); + dump_hash_table_loc_statistics (); dump_vec_loc_statistics (); dump_ggc_loc_statistics (final); dump_alias_stats (stderr); diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c index 4b0d2a8b389..d799751da30 100644 --- a/gcc/tree-sra.c +++ b/gcc/tree-sra.c @@ -74,6 +74,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #include "system.h" #include "coretypes.h" +#include "mem-stats.h" #include "hash-map.h" #include "hash-table.h" #include "alloc-pool.h" diff --git a/gcc/vec.c b/gcc/vec.c index 06b887b4477..d4a7a0229be 100644 --- a/gcc/vec.c +++ b/gcc/vec.c @@ -33,6 +33,9 @@ along with GCC; see the file COPYING3. If not see #include "vec.h" #include "diagnostic-core.h" #include "hashtab.h" +#include "mem-stats.h" +#include "hash-map.h" +#include "mem-stats.h" /* vNULL is an empty type with a template cast operation that returns a zero-initialized vec instance. Use this when you want @@ -44,129 +47,110 @@ along with GCC; see the file COPYING3. If not see they cannot have ctors/dtors. */ vnull vNULL; - -/* Store information about each particular vector. */ -struct vec_descriptor -{ - const char *function; - const char *file; - int line; - size_t allocated; - size_t times; - size_t peak; -}; - - -/* Hashtable mapping vec addresses to descriptors. */ -static htab_t vec_desc_hash; - -/* Hashtable helpers. */ -static hashval_t -hash_descriptor (const void *p) -{ - const struct vec_descriptor *const d = - (const struct vec_descriptor *) p; - return htab_hash_pointer (d->file) + d->line; -} -static int -eq_descriptor (const void *p1, const void *p2) +/* Vector memory usage. */ +struct vec_usage: public mem_usage { - const struct vec_descriptor *const d = (const struct vec_descriptor *) p1; - const struct vec_descriptor *const l = (const struct vec_descriptor *) p2; - return d->file == l->file && d->function == l->function && d->line == l->line; -} - -/* Hashtable converting address of allocated field to loc descriptor. */ -static htab_t ptr_hash; -struct ptr_hash_entry -{ - void *ptr; - struct vec_descriptor *loc; - size_t allocated; + /* Default constructor. */ + vec_usage (): m_items (0), m_items_peak (0) {} + + /* Constructor. */ + vec_usage (size_t allocated, size_t times, size_t peak, + size_t items, size_t items_peak) + : mem_usage (allocated, times, peak), + m_items (items), m_items_peak (items_peak) {} + + /* Comparison operator. */ + inline bool operator< (const vec_usage &second) const + { + return (m_allocated == second.m_allocated ? + (m_peak == second.m_peak ? m_times < second.m_times + : m_peak < second.m_peak) : m_allocated < second.m_allocated); + } + + /* Sum the usage with SECOND usage. */ + vec_usage operator+ (const vec_usage &second) + { + return vec_usage (m_allocated + second.m_allocated, + m_times + second.m_times, + m_peak + second.m_peak, + m_items + second.m_items, + m_items_peak + second.m_items_peak); + } + + /* Dump usage coupled to LOC location, where TOTAL is sum of all rows. */ + inline void dump (mem_location *loc, mem_usage &total) const + { + char s[4096]; + sprintf (s, "%s:%i (%s)", loc->get_trimmed_filename (), + loc->m_line, loc->m_function); + + s[48] = '\0'; + + fprintf (stderr, "%-48s %10li:%4.1f%%%10li%10li:%4.1f%%%11li%11li\n", s, + (long)m_allocated, m_allocated * 100.0 / total.m_allocated, + (long)m_peak, (long)m_times, m_times * 100.0 / total.m_times, + (long)m_items, (long)m_items_peak); + } + + /* Dump footer. */ + inline void dump_footer () + { + print_dash_line (); + fprintf (stderr, "%s%55li%25li%17li\n", "Total", (long)m_allocated, + (long)m_times, (long)m_items); + print_dash_line (); + } + + /* Dump header with NAME. */ + static inline void dump_header (const char *name) + { + fprintf (stderr, "%-48s %11s%15s%10s%17s%11s\n", name, "Leak", "Peak", + "Times", "Leak items", "Peak items"); + print_dash_line (); + } + + /* Compare wrapper used by qsort method. */ + static int compare (const void *first, const void *second) + { + typedef std::pair mem_pair_t; + + const mem_pair_t f = *(const mem_pair_t *)first; + const mem_pair_t s = *(const mem_pair_t *)second; + + return (*f.second) < (*s.second); + } + + /* Current number of items allocated. */ + size_t m_items; + /* Peak value of number of allocated items. */ + size_t m_items_peak; }; -/* Hash table helpers functions. */ -static hashval_t -hash_ptr (const void *p) -{ - const struct ptr_hash_entry *const d = (const struct ptr_hash_entry *) p; - - return htab_hash_pointer (d->ptr); -} - -static int -eq_ptr (const void *p1, const void *p2) -{ - const struct ptr_hash_entry *const p = (const struct ptr_hash_entry *) p1; - - return (p->ptr == p2); -} - -/* Return descriptor for given call site, create new one if needed. */ -static struct vec_descriptor * -vec_descriptor (const char *name, int line, const char *function) -{ - struct vec_descriptor loc; - struct vec_descriptor **slot; - - loc.file = name; - loc.line = line; - loc.function = function; - if (!vec_desc_hash) - vec_desc_hash = htab_create (10, hash_descriptor, eq_descriptor, NULL); - - slot = (struct vec_descriptor **) htab_find_slot (vec_desc_hash, &loc, - INSERT); - if (*slot) - return *slot; - *slot = XCNEW (struct vec_descriptor); - (*slot)->file = name; - (*slot)->line = line; - (*slot)->function = function; - (*slot)->allocated = 0; - (*slot)->peak = 0; - return *slot; -} +/* Vector memory description. */ +static mem_alloc_description vec_mem_desc; /* Account the overhead. */ void -vec_prefix::register_overhead (size_t size, const char *name, int line, - const char *function) +vec_prefix::register_overhead (void *ptr, size_t size, size_t elements + MEM_STAT_DECL) { - struct vec_descriptor *loc = vec_descriptor (name, line, function); - struct ptr_hash_entry *p = XNEW (struct ptr_hash_entry); - PTR *slot; - - p->ptr = this; - p->loc = loc; - p->allocated = size; - if (!ptr_hash) - ptr_hash = htab_create (10, hash_ptr, eq_ptr, NULL); - slot = htab_find_slot_with_hash (ptr_hash, this, htab_hash_pointer (this), - INSERT); - gcc_assert (!*slot); - *slot = p; - - loc->allocated += size; - if (loc->peak < loc->allocated) - loc->peak += loc->allocated; - loc->times++; + vec_mem_desc.register_descriptor (ptr, VEC, false FINAL_PASS_MEM_STAT); + vec_usage *usage = vec_mem_desc.register_instance_overhead (size, ptr); + usage->m_items += elements; + if (usage->m_items_peak < usage->m_items) + usage->m_items_peak = usage->m_items; } - /* Notice that the memory allocated for the vector has been freed. */ void -vec_prefix::release_overhead (void) +vec_prefix::release_overhead (void *ptr, size_t size, bool in_dtor + MEM_STAT_DECL) { - PTR *slot = htab_find_slot_with_hash (ptr_hash, this, - htab_hash_pointer (this), - NO_INSERT); - struct ptr_hash_entry *p = (struct ptr_hash_entry *) *slot; - p->loc->allocated -= p->allocated; - htab_clear_slot (ptr_hash, slot); - ::free (p); + if (!vec_mem_desc.contains_descriptor_for_instance (ptr)) + vec_mem_desc.register_descriptor (ptr, VEC, false FINAL_PASS_MEM_STAT); + vec_mem_desc.release_instance_overhead (ptr, size, in_dtor); } @@ -195,84 +179,10 @@ vec_prefix::calculate_allocation_1 (unsigned alloc, unsigned desired) return alloc; } - -/* Helper for qsort; sort descriptors by amount of memory consumed. */ - -static int -cmp_statistic (const void *loc1, const void *loc2) -{ - const struct vec_descriptor *const l1 = - *(const struct vec_descriptor *const *) loc1; - const struct vec_descriptor *const l2 = - *(const struct vec_descriptor *const *) loc2; - long diff; - diff = l1->allocated - l2->allocated; - if (!diff) - diff = l1->peak - l2->peak; - if (!diff) - diff = l1->times - l2->times; - return diff > 0 ? 1 : diff < 0 ? -1 : 0; -} - - -/* Collect array of the descriptors from hashtable. */ - -static struct vec_descriptor **loc_array; -static int -add_statistics (void **slot, void *b) -{ - int *n = (int *)b; - loc_array[*n] = (struct vec_descriptor *) *slot; - (*n)++; - return 1; -} - /* Dump per-site memory statistics. */ void dump_vec_loc_statistics (void) { - int nentries = 0; - char s[4096]; - size_t allocated = 0; - size_t times = 0; - int i; - - if (! GATHER_STATISTICS) - return; - - loc_array = XCNEWVEC (struct vec_descriptor *, vec_desc_hash->n_elements); - fprintf (stderr, "Heap vectors:\n"); - fprintf (stderr, "\n%-48s %10s %10s %10s\n", - "source location", "Leak", "Peak", "Times"); - fprintf (stderr, "-------------------------------------------------------\n"); - htab_traverse (vec_desc_hash, add_statistics, &nentries); - qsort (loc_array, nentries, sizeof (*loc_array), cmp_statistic); - for (i = 0; i < nentries; i++) - { - struct vec_descriptor *d = loc_array[i]; - allocated += d->allocated; - times += d->times; - } - for (i = 0; i < nentries; i++) - { - struct vec_descriptor *d = loc_array[i]; - 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 %10li:%4.1f%% \n", s, - (long)d->allocated, - (d->allocated) * 100.0 / allocated, - (long)d->peak, - (long)d->times, - (d->times) * 100.0 / times); - } - fprintf (stderr, "%-48s %10ld %10ld\n", - "Total", (long)allocated, (long)times); - fprintf (stderr, "\n%-48s %10s %10s %10s\n", - "source location", "Leak", "Peak", "Times"); - fprintf (stderr, "-------------------------------------------------------\n"); + vec_mem_desc.dump (VEC); } diff --git a/gcc/vec.h b/gcc/vec.h index aa9a255bf97..7b9697922ad 100644 --- a/gcc/vec.h +++ b/gcc/vec.h @@ -51,7 +51,7 @@ along with GCC; see the file COPYING3. If not see extern void ggc_free (void *); extern size_t ggc_round_alloc_size (size_t requested_size); - extern void *ggc_realloc (void *, size_t CXX_MEM_STAT_INFO); + extern void *ggc_realloc (void *, size_t MEM_STAT_DECL); # endif // GCC_GGC_H #endif // VEC_GC_ENABLED @@ -206,6 +206,8 @@ along with GCC; see the file COPYING3. If not see /* Support function for statistics. */ extern void dump_vec_loc_statistics (void); +/* Hashtable mapping vec addresses to descriptors. */ +extern htab_t vec_mem_usage_hash; /* Control data for vectors. This contains the number of allocated and used slots inside a vector. */ @@ -216,8 +218,8 @@ struct vec_prefix compilers that have stricter notions of PODness for types. */ /* Memory allocation support routines in vec.c. */ - void register_overhead (size_t, const char *, int, const char *); - void release_overhead (void); + void register_overhead (void *, size_t, size_t CXX_MEM_STAT_INFO); + void release_overhead (void *, size_t, bool CXX_MEM_STAT_INFO); static unsigned calculate_allocation (vec_prefix *, unsigned, bool); static unsigned calculate_allocation_1 (unsigned, unsigned); @@ -303,7 +305,7 @@ va_heap::reserve (vec *&v, unsigned reserve, bool exact gcc_checking_assert (alloc); if (GATHER_STATISTICS && v) - v->m_vecpfx.release_overhead (); + v->m_vecpfx.release_overhead (v, v->allocated (), false); size_t size = vec::embedded_size (alloc); unsigned nelem = v ? v->length () : 0; @@ -311,7 +313,7 @@ va_heap::reserve (vec *&v, unsigned reserve, bool exact v->embedded_init (alloc, nelem); if (GATHER_STATISTICS) - v->m_vecpfx.register_overhead (size FINAL_PASS_MEM_STAT); + v->m_vecpfx.register_overhead (v, alloc, nelem PASS_MEM_STAT); } @@ -325,7 +327,7 @@ va_heap::release (vec *&v) return; if (GATHER_STATISTICS) - v->m_vecpfx.release_overhead (); + v->m_vecpfx.release_overhead (v, v->allocated (), true); ::free (v); v = NULL; } -- 2.30.2