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
This commit is contained in:
parent
151fbaac5c
commit
2d44c7dea7
|
@ -1,3 +1,77 @@
|
|||
2015-05-27 Martin Liska <mliska@suse.cz>
|
||||
|
||||
* 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 <rguenther@suse.de>
|
||||
|
||||
* tree-vect-stmts.c (vectorizable_load): Initialize slp_perm
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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<const char *, alloc_pool_descriptor> (10);
|
||||
alloc_pool_hash = new hash_map<const char *, alloc_pool_descriptor> (10,
|
||||
false,
|
||||
false);
|
||||
|
||||
return &alloc_pool_hash->get_or_insert (name);
|
||||
}
|
||||
|
|
178
gcc/bitmap.c
178
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_descriptor> bitmap_descriptors;
|
||||
|
||||
/* Hashtable helpers. */
|
||||
|
||||
struct loc
|
||||
{
|
||||
const char *file;
|
||||
const char *function;
|
||||
int line;
|
||||
};
|
||||
|
||||
struct bitmap_desc_hasher : typed_noop_remove <bitmap_descriptor_d>
|
||||
{
|
||||
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_hasher> *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<bitmap_desc_hasher> (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_usage> 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 <bitmap_output_info *, print_statistics> (&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
|
||||
|
|
56
gcc/bitmap.h
56
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_usage> bitmap_mem_desc;
|
||||
|
||||
/* Fundamental storage type for bitmap. */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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\
|
||||
|
|
399
gcc/ggc-common.c
399
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,138 +831,166 @@ 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) {}
|
||||
|
||||
/* 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 ());
|
||||
}
|
||||
|
||||
/* 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++;
|
||||
}
|
||||
|
||||
/* Release overhead of SIZE bytes. */
|
||||
inline void release_overhead (size_t size)
|
||||
{
|
||||
m_freed += size;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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';
|
||||
|
||||
dump (s, total);
|
||||
}
|
||||
|
||||
/* Dump footer. */
|
||||
inline void dump_footer ()
|
||||
{
|
||||
print_dash_line ();
|
||||
dump ("Total", *this);
|
||||
print_dash_line ();
|
||||
}
|
||||
|
||||
/* Get balance which is GGC allocation leak. */
|
||||
inline long get_balance () const
|
||||
{
|
||||
return m_allocated + m_overhead - m_collected - m_freed;
|
||||
}
|
||||
|
||||
typedef std::pair<mem_location *, ggc_usage *> mem_pair_t;
|
||||
|
||||
/* 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 (*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;
|
||||
};
|
||||
|
||||
/* Hash table helper. */
|
||||
/* GCC memory description. */
|
||||
static mem_alloc_description<ggc_usage> ggc_mem_desc;
|
||||
|
||||
struct ggc_loc_desc_hasher : typed_noop_remove <ggc_loc_descriptor>
|
||||
/* Dump per-site memory statistics. */
|
||||
|
||||
void
|
||||
dump_ggc_loc_statistics (bool final)
|
||||
{
|
||||
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 *);
|
||||
};
|
||||
if (! GATHER_STATISTICS)
|
||||
return;
|
||||
|
||||
inline hashval_t
|
||||
ggc_loc_desc_hasher::hash (const ggc_loc_descriptor *d)
|
||||
{
|
||||
return htab_hash_pointer (d->function) | d->line;
|
||||
}
|
||||
ggc_force_collect = true;
|
||||
ggc_collect ();
|
||||
|
||||
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);
|
||||
}
|
||||
ggc_mem_desc.dump (GGC, final ? ggc_usage::compare_final : NULL);
|
||||
|
||||
/* Hashtable used for statistics. */
|
||||
static hash_table<ggc_loc_desc_hasher> *loc_hash;
|
||||
|
||||
struct ggc_ptr_hash_entry
|
||||
{
|
||||
void *ptr;
|
||||
struct ggc_loc_descriptor *loc;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/* Helper for ptr_hash table. */
|
||||
|
||||
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 *);
|
||||
};
|
||||
|
||||
inline hashval_t
|
||||
ptr_hash_hasher::hash (const ggc_ptr_hash_entry *d)
|
||||
{
|
||||
return htab_hash_pointer (d->ptr);
|
||||
}
|
||||
|
||||
inline bool
|
||||
ptr_hash_hasher::equal (const ggc_ptr_hash_entry *p, const void *p2)
|
||||
{
|
||||
return (p->ptr == p2);
|
||||
}
|
||||
|
||||
/* Hashtable converting address of allocated field to loc descriptor. */
|
||||
static hash_table<ptr_hash_hasher> *ptr_hash;
|
||||
|
||||
/* 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;
|
||||
ggc_force_collect = false;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
ggc_record_overhead (size_t allocated, size_t overhead, void *ptr MEM_STAT_DECL)
|
||||
{
|
||||
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;
|
||||
ggc_usage *usage = ggc_mem_desc.register_descriptor (ptr, GGC, false
|
||||
FINAL_PASS_MEM_STAT);
|
||||
|
||||
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;
|
||||
ggc_mem_desc.register_object_overhead (usage, allocated + overhead, ptr);
|
||||
usage->register_overhead (allocated, overhead);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
/* Notice that the pointer has been freed. */
|
||||
void
|
||||
ggc_free_overhead (void *ptr)
|
||||
{
|
||||
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_mem_desc.release_object_overhead (ptr);
|
||||
}
|
||||
|
||||
/* After live values has been marked, walk all recorded pointers and see if
|
||||
|
@ -969,134 +998,14 @@ ggc_prune_ptr (ggc_ptr_hash_entry **slot, void *b ATTRIBUTE_UNUSED)
|
|||
void
|
||||
ggc_prune_overhead_list (void)
|
||||
{
|
||||
ptr_hash->traverse <void *, ggc_prune_ptr> (NULL);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
|
||||
void
|
||||
dump_ggc_loc_statistics (bool final)
|
||||
{
|
||||
int nentries = 0;
|
||||
char s[4096];
|
||||
size_t collected = 0, freed = 0, allocated = 0, overhead = 0, times = 0;
|
||||
int i;
|
||||
|
||||
if (! GATHER_STATISTICS)
|
||||
return;
|
||||
|
||||
ggc_force_collect = true;
|
||||
ggc_collect ();
|
||||
|
||||
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;
|
||||
typedef hash_map<const void *, std::pair<ggc_usage *, size_t > > map_t;
|
||||
|
||||
map_t::iterator it = ggc_mem_desc.m_reverse_object_map->begin ();
|
||||
|
||||
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;
|
||||
|
||||
delete ggc_mem_desc.m_reverse_object_map;
|
||||
ggc_mem_desc.m_reverse_object_map = new map_t (13, false, false);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#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<typename T>
|
||||
static hashval_t
|
||||
hash (T *p)
|
||||
{
|
||||
return uintptr_t (p) >> 3;
|
||||
}
|
||||
|
||||
/* If the value converts to hashval_t just use it. */
|
||||
|
||||
template<typename T> static hashval_t hash (T v) { return v; }
|
||||
|
||||
/* Return true if the two keys passed as arguments are equal. */
|
||||
|
||||
template<typename T>
|
||||
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<typename T> static void remove (T &v) { v.~T (); }
|
||||
|
||||
/* Mark the passed in entry as being deleted. */
|
||||
|
||||
template<typename T>
|
||||
static void
|
||||
mark_deleted (T &e)
|
||||
{
|
||||
mark_key_deleted (e.m_key);
|
||||
}
|
||||
|
||||
/* Mark the passed in entry as being empty. */
|
||||
|
||||
template<typename T>
|
||||
static void
|
||||
mark_empty (T &e)
|
||||
{
|
||||
mark_key_empty (e.m_key);
|
||||
}
|
||||
|
||||
/* Return true if the passed in entry is marked as deleted. */
|
||||
|
||||
template<typename T>
|
||||
static bool
|
||||
is_deleted (T &e)
|
||||
{
|
||||
return e.m_key == (void *)1;
|
||||
}
|
||||
|
||||
/* Return true if the passed in entry is marked as empty. */
|
||||
|
||||
template<typename T> static bool is_empty (T &e) { return e.m_key == NULL; }
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
static void
|
||||
mark_key_deleted (T *&k)
|
||||
{
|
||||
k = reinterpret_cast<T *> (1);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void
|
||||
mark_key_empty (T *&k)
|
||||
{
|
||||
k = static_cast<T *> (0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // HASH_MAP_TRAITS_H
|
|
@ -24,87 +24,12 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include <new>
|
||||
#include <utility>
|
||||
#include "hash-table.h"
|
||||
|
||||
/* implement default behavior for traits when types allow it. */
|
||||
|
||||
struct default_hashmap_traits
|
||||
{
|
||||
/* Hashes the passed in key. */
|
||||
|
||||
template<typename T>
|
||||
static hashval_t
|
||||
hash (T *p)
|
||||
{
|
||||
return uintptr_t(p) >> 3;
|
||||
}
|
||||
|
||||
/* If the value converts to hashval_t just use it. */
|
||||
|
||||
template<typename T> static hashval_t hash (T v) { return v; }
|
||||
|
||||
/* Return true if the two keys passed as arguments are equal. */
|
||||
|
||||
template<typename T>
|
||||
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<typename T> static void remove (T &v) { v.~T (); }
|
||||
|
||||
/* Mark the passed in entry as being deleted. */
|
||||
|
||||
template<typename T>
|
||||
static void
|
||||
mark_deleted (T &e)
|
||||
{
|
||||
mark_key_deleted (e.m_key);
|
||||
}
|
||||
|
||||
/* Mark the passed in entry as being empty. */
|
||||
|
||||
template<typename T>
|
||||
static void
|
||||
mark_empty (T &e)
|
||||
{
|
||||
mark_key_empty (e.m_key);
|
||||
}
|
||||
|
||||
/* Return true if the passed in entry is marked as deleted. */
|
||||
|
||||
template<typename T>
|
||||
static bool
|
||||
is_deleted (T &e)
|
||||
{
|
||||
return e.m_key == (void *)1;
|
||||
}
|
||||
|
||||
/* Return true if the passed in entry is marked as empty. */
|
||||
|
||||
template<typename T> static bool is_empty (T &e) { return e.m_key == NULL; }
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
static void
|
||||
mark_key_deleted (T *&k)
|
||||
{
|
||||
k = reinterpret_cast<T *> (1);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void
|
||||
mark_key_empty (T *&k)
|
||||
{
|
||||
k = static_cast<T *> (0);
|
||||
}
|
||||
};
|
||||
#include "hash-map-traits.h"
|
||||
#include "mem-stats.h"
|
||||
#include "vec.h"
|
||||
|
||||
template<typename Key, typename Value,
|
||||
typename Traits = default_hashmap_traits>
|
||||
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<hash_map> ();
|
||||
new (map) hash_map (size, true);
|
||||
new (map) hash_map (size, true, gather_mem_stats PASS_MEM_STAT);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<typename T> 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. */
|
||||
|
||||
|
|
|
@ -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<mem_usage> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -199,6 +199,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "ggc.h"
|
||||
#include "hashtab.h"
|
||||
#include <new>
|
||||
#include "mem-stats-traits.h"
|
||||
|
||||
template<typename, typename, typename> class hash_map;
|
||||
template<typename, typename> class hash_set;
|
||||
|
@ -551,6 +552,8 @@ struct mark_empty_helper<Type *, Traits, false>
|
|||
}
|
||||
};
|
||||
|
||||
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<hash_table> ();
|
||||
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<mem_usage> hash_table_usage;
|
||||
|
||||
/* Support function for statistics. */
|
||||
extern void dump_hash_table_loc_statistics (void);
|
||||
|
||||
template<typename Descriptor, template<typename Type> class Allocator>
|
||||
hash_table<Descriptor, Allocator>::hash_table (size_t size, bool ggc
|
||||
MEM_STAT_DECL) :
|
||||
hash_table<Descriptor, Allocator>::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<Descriptor, Allocator>::~hash_table ()
|
|||
Allocator <value_type> ::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<Descriptor, Allocator>::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 <value_type> ::data_alloc (n);
|
||||
else
|
||||
|
@ -881,6 +915,11 @@ hash_table<Descriptor, Allocator>::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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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<typename Key, typename Value,
|
||||
typename Traits = default_hashmap_traits>
|
||||
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_location *, mem_usage *> 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 <class T>
|
||||
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 T>
|
||||
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_location *, T *, mem_alloc_hashmap_traits> mem_map_t;
|
||||
typedef hash_map <const void *, mem_usage_pair<T>, default_hashmap_traits>
|
||||
reverse_mem_map_t;
|
||||
typedef hash_map <const void *, std::pair<T *, size_t> > reverse_object_map_t;
|
||||
typedef std::pair <mem_location *, T *> 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 <class T>
|
||||
inline bool
|
||||
mem_alloc_description<T>::contains_descriptor_for_instance (const void *ptr)
|
||||
{
|
||||
return m_reverse_map->get (ptr);
|
||||
}
|
||||
|
||||
/* Return descriptor for instance PTR. */
|
||||
|
||||
template <class T>
|
||||
inline T*
|
||||
mem_alloc_description<T>::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 <class T>
|
||||
inline T*
|
||||
mem_alloc_description<T>::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<T> (usage, 0));
|
||||
|
||||
return usage;
|
||||
}
|
||||
|
||||
/* Register instance overhead identified by PTR pointer. Allocation takes
|
||||
SIZE bytes. */
|
||||
|
||||
template <class T>
|
||||
inline T*
|
||||
mem_alloc_description<T>::register_instance_overhead (size_t size,
|
||||
const void *ptr)
|
||||
{
|
||||
mem_usage_pair <T> *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 <class T>
|
||||
void
|
||||
mem_alloc_description<T>::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<T *, size_t> (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 <class T>
|
||||
inline T*
|
||||
mem_alloc_description<T>::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 <class T>
|
||||
inline void
|
||||
mem_alloc_description<T>::release_instance_overhead (void *ptr, size_t size,
|
||||
bool remove_from_map)
|
||||
{
|
||||
mem_usage_pair<T> *slot = m_reverse_map->get (ptr);
|
||||
|
||||
if (!slot)
|
||||
{
|
||||
/* Due to PCH, it can really happen. */
|
||||
return;
|
||||
}
|
||||
|
||||
mem_usage_pair<T> 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 <class T>
|
||||
inline void
|
||||
mem_alloc_description<T>::release_object_overhead (void *ptr)
|
||||
{
|
||||
std::pair <T *, size_t> *entry = m_reverse_object_map->get (ptr);
|
||||
if (entry)
|
||||
{
|
||||
entry->first->release_overhead (entry->second);
|
||||
m_reverse_object_map->remove (ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Default contructor. */
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
mem_alloc_description<T>::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 <class T>
|
||||
inline
|
||||
mem_alloc_description<T>::~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 <class T>
|
||||
inline
|
||||
typename mem_alloc_description<T>::mem_list_t *
|
||||
mem_alloc_description<T>::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<mem_location*, T*> (*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 <class T>
|
||||
inline T
|
||||
mem_alloc_description<T>::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 <class T>
|
||||
inline void
|
||||
mem_alloc_description<T>::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
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
278
gcc/vec.c
278
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<T, A, L> 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
|
||||
/* Vector memory usage. */
|
||||
struct vec_usage: public mem_usage
|
||||
{
|
||||
const char *function;
|
||||
const char *file;
|
||||
int line;
|
||||
size_t allocated;
|
||||
size_t times;
|
||||
size_t peak;
|
||||
/* 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_location *, vec_usage *> 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;
|
||||
};
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
/* 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_usage> 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);
|
||||
}
|
||||
|
|
14
gcc/vec.h
14
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<T, va_heap, vl_embed> *&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<T, va_heap, vl_embed>::embedded_size (alloc);
|
||||
unsigned nelem = v ? v->length () : 0;
|
||||
|
@ -311,7 +313,7 @@ va_heap::reserve (vec<T, va_heap, vl_embed> *&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<T, va_heap, vl_embed> *&v)
|
|||
return;
|
||||
|
||||
if (GATHER_STATISTICS)
|
||||
v->m_vecpfx.release_overhead ();
|
||||
v->m_vecpfx.release_overhead (v, v->allocated (), true);
|
||||
::free (v);
|
||||
v = NULL;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue