This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] track heap usage with -fmem-report
- From: Richard Biener <rguenther at suse dot de>
- To: gcc-patches at gcc dot gnu dot org
- Date: Thu, 7 Mar 2013 13:59:31 +0100 (CET)
- Subject: [PATCH] track heap usage with -fmem-report
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))