This hacky patch tracks per-pass heap usage with -fmem-report using glibc malloc hooks (which are deprecated!? eh ... I can see no replacement?)
Still somewhat broken accounting for the overall numbers (and peak memory use, that is). But it's a start. And it uses internal glibc allocator implementation details ... (eh). At least it's low-overhead. FYI, Richard. Index: gcc/toplev.c =================================================================== *** gcc/toplev.c (revision 196517) --- gcc/toplev.c (working copy) *************** along with GCC; see the file COPYING3. *** 74,79 **** --- 74,80 ---- #include "gimple.h" #include "tree-ssa-alias.h" #include "plugin.h" + #include "tree-pass.h" #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO) #include "dbxout.h" *************** dump_memory_report (bool final) *** 1783,1788 **** --- 1784,1790 ---- dump_ggc_loc_statistics (final); dump_alias_stats (stderr); dump_pta_stats (stderr); + dump_pass_memory_stats (stderr); } /* Clean up: close opened files, etc. */ Index: gcc/tree-pass.h =================================================================== *** gcc/tree-pass.h (revision 196517) --- gcc/tree-pass.h (working copy) *************** struct opt_pass *** 78,83 **** --- 78,88 ---- /* Flags indicating common sets things to do before and after. */ unsigned int todo_flags_start; unsigned int todo_flags_finish; + + size_t alloc_cnt; + size_t alloc_sum; + size_t alloc_curr; + size_t alloc_peak; }; /* Description of GIMPLE pass. */ *************** extern void disable_pass (const char *); *** 557,560 **** --- 562,567 ---- extern void enable_pass (const char *); extern void dump_passes (void); + extern void dump_pass_memory_stats (FILE *); + #endif /* GCC_TREE_PASS_H */ Index: gcc/passes.c =================================================================== *** gcc/passes.c (revision 196517) --- gcc/passes.c (working copy) *************** along with GCC; see the file COPYING3. *** 24,29 **** --- 24,30 ---- #include "config.h" #include "system.h" + #include <malloc.h> #include "coretypes.h" #include "tm.h" #include "line-map.h" *************** bool in_gimple_form; *** 105,110 **** --- 106,245 ---- bool first_pass_instance; + struct malloc_chunk_head { + size_t x; + size_t size; + }; + typedef struct malloc_chunk_head* mchunkptr; + #define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*sizeof(size_t))) + + static void (*gcc_old_free_hook) (void *ptr, const void *); + static void *(*gcc_old_malloc_hook) (size_t size, const void *); + static void *(*gcc_old_realloc_hook) (void *ptr, size_t size, const void *); + + static void *gcc_malloc_hook (size_t size, const void *caller); + static void *gcc_realloc_hook (void *ptr, size_t size, const void *caller); + static void gcc_free_hook (void *ptr, const void *caller); + + struct opt_pass rest_of_comp; + + static void * + gcc_malloc_hook (size_t size, const void *caller) + { + void *res; + __malloc_hook = gcc_old_malloc_hook; + __realloc_hook = gcc_old_realloc_hook; + __free_hook = gcc_old_free_hook; + if (gcc_old_malloc_hook != NULL) + res = gcc_old_malloc_hook (size, caller); + else + res = xmalloc (size); + __malloc_hook = gcc_malloc_hook; + __realloc_hook = gcc_realloc_hook; + __free_hook = gcc_free_hook; + struct opt_pass *p = current_pass ? current_pass : &rest_of_comp; + /* The allocator allocates a chunk >= size. */ + size = res ? mem2chunk (res)->size : 0; + p->alloc_cnt++; + p->alloc_sum += size; + p->alloc_curr += size; + if ((ssize_t) p->alloc_curr > (ssize_t) p->alloc_peak) + p->alloc_peak = p->alloc_curr; + return res; + } + + static void * + gcc_realloc_hook (void *ptr, size_t size, const void *caller) + { + size_t oldsize = ptr ? mem2chunk (ptr)->size : 0; + void *res; + __malloc_hook = gcc_old_malloc_hook; + __realloc_hook = gcc_old_realloc_hook; + __free_hook = gcc_old_free_hook; + if (gcc_old_realloc_hook != NULL) + res = gcc_old_realloc_hook (ptr, size, caller); + else + res = xrealloc (ptr, size); + __malloc_hook = gcc_malloc_hook; + __realloc_hook = gcc_realloc_hook; + __free_hook = gcc_free_hook; + struct opt_pass *p = current_pass ? current_pass : &rest_of_comp; + p->alloc_cnt++; + p->alloc_sum += (size - oldsize); + p->alloc_curr += (size - oldsize); + if ((ssize_t) p->alloc_curr > (ssize_t) p->alloc_peak) + p->alloc_peak = p->alloc_curr; + return res; + } + + static void + gcc_free_hook (void *ptr, const void *caller) + { + struct opt_pass *p = current_pass ? current_pass : &rest_of_comp; + size_t size = ptr ? mem2chunk (ptr)->size : 0; + p->alloc_curr -= size; + __malloc_hook = gcc_old_malloc_hook; + __realloc_hook = gcc_old_realloc_hook; + __free_hook = gcc_old_free_hook; + if (gcc_old_free_hook != NULL) + gcc_old_free_hook (ptr, caller); + else + free (ptr); + __malloc_hook = gcc_malloc_hook; + __realloc_hook = gcc_realloc_hook; + __free_hook = gcc_free_hook; + } + + static void + dump_pass_memory_stats_1 (FILE *file, struct opt_pass *pass) + { + do + { + if (pass->sub) + dump_pass_memory_stats_1 (file, pass->sub); + + if (pass->alloc_cnt != 0 + || pass->alloc_curr != 0) + { + fprintf (file, "%.14s:", pass->name); + if (strlen (pass->name) < 7) + fprintf (file, "\t"); + fprintf (file, "\t%zd\t%zd\t%zd\t%zd\n", + pass->alloc_cnt, + pass->alloc_sum / 1024, ((ssize_t) pass->alloc_curr) / 1024, + pass->alloc_peak / 1024); + } + rest_of_comp.alloc_cnt += pass->alloc_cnt; + rest_of_comp.alloc_sum += pass->alloc_sum; + rest_of_comp.alloc_curr += pass->alloc_curr; + rest_of_comp.alloc_peak += pass->alloc_peak; + pass = pass->next; + } + while (pass); + } + + void + dump_pass_memory_stats (FILE *file) + { + struct opt_pass rest_of_comp_saved; + fprintf (file, "\n\nPer pass non-gc memory allocation stats\n\n"); + fprintf (file, "Pass\t\t Cnt\t Total kB\t Leak kB\t Peak kB\n"); + rest_of_comp.name = "(no pass)"; + rest_of_comp_saved = rest_of_comp; + dump_pass_memory_stats_1 (file, &rest_of_comp); + rest_of_comp = rest_of_comp_saved; + dump_pass_memory_stats_1 (file, all_lowering_passes); + dump_pass_memory_stats_1 (file, all_small_ipa_passes); + dump_pass_memory_stats_1 (file, all_regular_ipa_passes); + dump_pass_memory_stats_1 (file, all_lto_gen_passes); + dump_pass_memory_stats_1 (file, all_late_ipa_passes); + dump_pass_memory_stats_1 (file, all_passes); + rest_of_comp.name = "TOTAL"; + dump_pass_memory_stats_1 (file, &rest_of_comp); + fprintf (file, "\n"); + } + + /* This is called from various places for FUNCTION_DECL, VAR_DECL, and TYPE_DECL nodes. *************** init_optimization_passes (void) *** 1688,1693 **** --- 1823,1838 ---- register_dump_files (all_passes, PROP_gimple_any | PROP_gimple_lcf | PROP_gimple_leh | PROP_cfg); + + if (1) + { + gcc_old_malloc_hook = __malloc_hook; + __malloc_hook = gcc_malloc_hook; + gcc_old_free_hook = __free_hook; + __free_hook = gcc_free_hook; + gcc_old_realloc_hook = __realloc_hook; + __realloc_hook = gcc_realloc_hook; + } } /* If we are in IPA mode (i.e., current_function_decl is NULL), call *************** execute_function_todo (void *data) *** 1965,1970 **** --- 2110,2118 ---- } else if (flags & TODO_verify_stmts) verify_gimple_in_cfg (cfun); + if (current_loops + && cfun->curr_properties & PROP_loops) + verify_loop_structure (); if (flags & TODO_verify_flow) verify_flow_info (); if (current_loops && loops_state_satisfies_p (LOOP_CLOSED_SSA))