[PATCH] track heap usage with -fmem-report

Richard Biener rguenther@suse.de
Thu Mar 7 12:59:00 GMT 2013


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))



More information about the Gcc-patches mailing list