(root)/
gcc-13.2.0/
gcc/
mem-stats.h
       1  /* A memory statistics tracking infrastructure.
       2     Copyright (C) 2015-2023 Free Software Foundation, Inc.
       3     Contributed by Martin Liska  <mliska@suse.cz>
       4  
       5  This file is part of GCC.
       6  
       7  GCC is free software; you can redistribute it and/or modify it under
       8  the terms of the GNU General Public License as published by the Free
       9  Software Foundation; either version 3, or (at your option) any later
      10  version.
      11  
      12  GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      13  WARRANTY; without even the implied warranty of MERCHANTABILITY or
      14  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      15  for more details.
      16  
      17  You should have received a copy of the GNU General Public License
      18  along with GCC; see the file COPYING3.  If not see
      19  <http://www.gnu.org/licenses/>.  */
      20  
      21  #ifndef GCC_MEM_STATS_H
      22  #define GCC_MEM_STATS_H
      23  
      24  /* Forward declaration.  */
      25  template<typename Key, typename Value,
      26  	 typename Traits = simple_hashmap_traits<default_hash_traits<Key>,
      27  						 Value> >
      28  class hash_map;
      29  
      30  #define LOCATION_LINE_EXTRA_SPACE 30
      31  #define LOCATION_LINE_WIDTH	  48
      32  
      33  /* Memory allocation location.  */
      34  class mem_location
      35  {
      36  public:
      37    /* Default constructor.  */
      38    inline
      39    mem_location () {}
      40  
      41    /* Constructor.  */
      42    inline
      43    mem_location (mem_alloc_origin origin, bool ggc,
      44  		const char *filename = NULL, int line = 0,
      45  		const char *function = NULL):
      46      m_filename (filename), m_function (function), m_line (line), m_origin
      47      (origin), m_ggc (ggc) {}
      48  
      49    /* Copy constructor.  */
      50    inline
      51    mem_location (mem_location &other): m_filename (other.m_filename),
      52      m_function (other.m_function), m_line (other.m_line),
      53      m_origin (other.m_origin), m_ggc (other.m_ggc) {}
      54  
      55    /* Compute hash value based on file name, function name and line in
      56       source code. As there is just a single pointer registered for every
      57       constant that points to e.g. the same file name, we can use hash
      58       of the pointer.  */
      59    hashval_t
      60    hash ()
      61    {
      62      inchash::hash hash;
      63  
      64      hash.add_ptr (m_filename);
      65      hash.add_ptr (m_function);
      66      hash.add_int (m_line);
      67  
      68      return hash.end ();
      69    }
      70  
      71    /* Return true if the memory location is equal to OTHER.  */
      72    int
      73    equal (const mem_location &other)
      74    {
      75      return m_filename == other.m_filename && m_function == other.m_function
      76        && m_line == other.m_line;
      77    }
      78  
      79    /* Return trimmed filename for the location.  */
      80    inline const char *
      81    get_trimmed_filename ()
      82    {
      83      const char *s1 = m_filename;
      84      const char *s2;
      85  
      86      while ((s2 = strstr (s1, "gcc/")))
      87        s1 = s2 + 4;
      88  
      89      return s1;
      90    }
      91  
      92    inline char *
      93    to_string ()
      94    {
      95      unsigned l = strlen (get_trimmed_filename ()) + strlen (m_function)
      96        + LOCATION_LINE_EXTRA_SPACE;
      97  
      98      char *s = XNEWVEC (char, l);
      99      sprintf (s, "%s:%i (%s)", get_trimmed_filename (),
     100  	     m_line, m_function);
     101  
     102      s[MIN (LOCATION_LINE_WIDTH, l - 1)] = '\0';
     103  
     104      return s;
     105    }
     106  
     107    /* Return display name associated to ORIGIN type.  */
     108    static const char *
     109    get_origin_name (mem_alloc_origin origin)
     110    {
     111      return mem_alloc_origin_names[(unsigned) origin];
     112    }
     113  
     114    /* File name of source code.  */
     115    const char *m_filename;
     116    /* Funcation name.  */
     117    const char *m_function;
     118    /* Line number in source code.  */
     119    int m_line;
     120    /* Origin type.  */
     121    mem_alloc_origin m_origin;
     122    /* Flag if used by GGC allocation.  */
     123    bool m_ggc;
     124  };
     125  
     126  /* Memory usage register to a memory location.  */
     127  class mem_usage
     128  {
     129  public:
     130    /* Default constructor.  */
     131    mem_usage (): m_allocated (0), m_times (0), m_peak (0), m_instances (1) {}
     132  
     133    /* Constructor.  */
     134    mem_usage (size_t allocated, size_t times, size_t peak, size_t instances = 0):
     135      m_allocated (allocated), m_times (times), m_peak (peak),
     136      m_instances (instances) {}
     137  
     138    /* Register overhead of SIZE bytes.  */
     139    inline void
     140    register_overhead (size_t size)
     141    {
     142      m_allocated += size;
     143      m_times++;
     144  
     145      if (m_peak < m_allocated)
     146        m_peak = m_allocated;
     147    }
     148  
     149    /* Release overhead of SIZE bytes.  */
     150    inline void
     151    release_overhead (size_t size)
     152    {
     153      gcc_assert (size <= m_allocated);
     154  
     155      m_allocated -= size;
     156    }
     157  
     158    /* Sum the usage with SECOND usage.  */
     159    mem_usage
     160    operator+ (const mem_usage &second)
     161    {
     162      return mem_usage (m_allocated + second.m_allocated,
     163  		      m_times + second.m_times,
     164  		      m_peak + second.m_peak,
     165  		      m_instances + second.m_instances);
     166    }
     167  
     168    /* Equality operator.  */
     169    inline bool
     170    operator== (const mem_usage &second) const
     171    {
     172      return (m_allocated == second.m_allocated
     173  	    && m_peak == second.m_peak
     174  	    && m_times == second.m_times);
     175    }
     176  
     177    /* Comparison operator.  */
     178    inline bool
     179    operator< (const mem_usage &second) const
     180    {
     181      if (*this == second)
     182        return false;
     183  
     184      return (m_allocated == second.m_allocated ?
     185  	    (m_peak == second.m_peak ? m_times < second.m_times
     186  	     : m_peak < second.m_peak) : m_allocated < second.m_allocated);
     187    }
     188  
     189    /* Compare wrapper used by qsort method.  */
     190    static int
     191    compare (const void *first, const void *second)
     192    {
     193      typedef std::pair<mem_location *, mem_usage *> mem_pair_t;
     194  
     195      const mem_pair_t f = *(const mem_pair_t *)first;
     196      const mem_pair_t s = *(const mem_pair_t *)second;
     197  
     198      if (*f.second == *s.second)
     199        return 0;
     200  
     201      return *f.second < *s.second ? 1 : -1;
     202    }
     203  
     204    /* Dump usage coupled to LOC location, where TOTAL is sum of all rows.  */
     205    inline void
     206    dump (mem_location *loc, const mem_usage &total) const
     207    {
     208      char *location_string = loc->to_string ();
     209  
     210      fprintf (stderr, "%-48s " PRsa (9) ":%5.1f%%"
     211  	     PRsa (9) PRsa (9) ":%5.1f%%%10s\n",
     212  	     location_string, SIZE_AMOUNT (m_allocated),
     213  	     get_percent (m_allocated, total.m_allocated),
     214  	     SIZE_AMOUNT (m_peak), SIZE_AMOUNT (m_times),
     215  	     get_percent (m_times, total.m_times), loc->m_ggc ? "ggc" : "heap");
     216  
     217      free (location_string);
     218    }
     219  
     220    /* Dump footer.  */
     221    inline void
     222    dump_footer () const
     223    {
     224      fprintf (stderr, "%s" PRsa (53) PRsa (26) "\n", "Total",
     225  	     SIZE_AMOUNT (m_allocated), SIZE_AMOUNT (m_times));
     226    }
     227  
     228    /* Return fraction of NOMINATOR and DENOMINATOR in percent.  */
     229    static inline float
     230    get_percent (size_t nominator, size_t denominator)
     231    {
     232      return denominator == 0 ? 0.0f : nominator * 100.0 / denominator;
     233    }
     234  
     235    /* Print line made of dashes.  */
     236    static inline void
     237    print_dash_line (size_t count = 140)
     238    {
     239      while (count--)
     240        fputc ('-', stderr);
     241      fputc ('\n', stderr);
     242    }
     243  
     244    /* Dump header with NAME.  */
     245    static inline void
     246    dump_header (const char *name)
     247    {
     248      fprintf (stderr, "%-48s %11s%16s%10s%17s\n", name, "Leak", "Peak",
     249  	     "Times", "Type");
     250    }
     251  
     252    /* Current number of allocated bytes.  */
     253    size_t m_allocated;
     254    /* Number of allocations.  */
     255    size_t m_times;
     256    /* Peak allocation in bytes.  */
     257    size_t m_peak;
     258    /* Number of container instances.  */
     259    size_t m_instances;
     260  };
     261  
     262  /* Memory usage pair that connectes memory usage and number
     263     of allocated bytes.  */
     264  template <class T>
     265  class mem_usage_pair
     266  {
     267  public:
     268    mem_usage_pair (T *usage_, size_t allocated_): usage (usage_),
     269    allocated (allocated_) {}
     270  
     271    T *usage;
     272    size_t allocated;
     273  };
     274  
     275  /* Memory allocation description.  */
     276  template <class T>
     277  class mem_alloc_description
     278  {
     279  public:
     280    struct mem_location_hash : nofree_ptr_hash <mem_location>
     281    {
     282      static hashval_t
     283      hash (value_type l)
     284      {
     285        inchash::hash hstate;
     286  
     287        hstate.add_ptr ((const void *)l->m_filename);
     288        hstate.add_ptr (l->m_function);
     289        hstate.add_int (l->m_line);
     290  
     291        return hstate.end ();
     292      }
     293  
     294      static bool
     295      equal (value_type l1, value_type l2)
     296      {
     297        return (l1->m_filename == l2->m_filename
     298  	      && l1->m_function == l2->m_function
     299  	      && l1->m_line == l2->m_line);
     300      }
     301    };
     302  
     303    /* Internal class type definitions.  */
     304    typedef hash_map <mem_location_hash, T *> mem_map_t;
     305    typedef hash_map <const void *, mem_usage_pair<T> > reverse_mem_map_t;
     306    typedef hash_map <const void *, std::pair<T *, size_t> > reverse_object_map_t;
     307    typedef std::pair <mem_location *, T *> mem_list_t;
     308  
     309    /* Default contructor.  */
     310    mem_alloc_description ();
     311  
     312    /* Default destructor.  */
     313    ~mem_alloc_description ();
     314  
     315    /* Returns true if instance PTR is registered by the memory description.  */
     316    bool contains_descriptor_for_instance (const void *ptr);
     317  
     318    /* Return descriptor for instance PTR.  */
     319    T *get_descriptor_for_instance (const void *ptr);
     320  
     321    /* Register memory allocation descriptor for container PTR which is
     322       described by a memory LOCATION.  */
     323    T *register_descriptor (const void *ptr, mem_location *location);
     324  
     325    /* Register memory allocation descriptor for container PTR.  ORIGIN identifies
     326       type of container and GGC identifes if the allocation is handled in GGC
     327       memory.  Each location is identified by file NAME, LINE in source code and
     328       FUNCTION name.  */
     329    T *register_descriptor (const void *ptr, mem_alloc_origin origin,
     330  			  bool ggc, const char *name, int line,
     331  			  const char *function);
     332  
     333    /* Register instance overhead identified by PTR pointer. Allocation takes
     334       SIZE bytes.  */
     335    T *register_instance_overhead (size_t size, const void *ptr);
     336  
     337    /* For containers (and GGC) where we want to track every instance object,
     338       we register allocation of SIZE bytes, identified by PTR pointer, belonging
     339       to USAGE descriptor.  */
     340    void register_object_overhead (T *usage, size_t size, const void *ptr);
     341  
     342    /* Release PTR pointer of SIZE bytes. If REMOVE_FROM_MAP is set to true,
     343       remove the instance from reverse map.  Return memory usage that belongs
     344       to this memory description.  */
     345    T *release_instance_overhead (void *ptr, size_t size,
     346  				bool remove_from_map = false);
     347  
     348    /* Release instance object identified by PTR pointer.  */
     349    void release_object_overhead (void *ptr);
     350  
     351    /* Unregister a memory allocation descriptor registered with
     352       register_descriptor (remove from reverse map), unless it is
     353       unregistered through release_instance_overhead with
     354       REMOVE_FROM_MAP = true.  */
     355    void unregister_descriptor (void *ptr);
     356  
     357    /* Get sum value for ORIGIN type of allocation for the descriptor.  */
     358    T get_sum (mem_alloc_origin origin);
     359  
     360    /* Get all tracked instances registered by the description. Items
     361       are filtered by ORIGIN type, LENGTH is return value where we register
     362       the number of elements in the list. If we want to process custom order,
     363       CMP comparator can be provided.  */
     364    mem_list_t *get_list (mem_alloc_origin origin, unsigned *length);
     365  
     366    /* Dump all tracked instances of type ORIGIN. If we want to process custom
     367       order, CMP comparator can be provided.  */
     368    void dump (mem_alloc_origin origin);
     369  
     370    /* Reverse object map used for every object allocation mapping.  */
     371    reverse_object_map_t *m_reverse_object_map;
     372  
     373  private:
     374    /* Register overhead of SIZE bytes of ORIGIN type. PTR pointer is allocated
     375       in NAME source file, at LINE in source code, in FUNCTION.  */
     376    T *register_overhead (size_t size, mem_alloc_origin origin, const char *name,
     377  			int line, const char *function, const void *ptr);
     378  
     379    /* Allocation location coupled to the description.  */
     380    mem_location m_location;
     381  
     382    /* Location to usage mapping.  */
     383    mem_map_t *m_map;
     384  
     385    /* Reverse pointer to usage mapping.  */
     386    reverse_mem_map_t *m_reverse_map;
     387  };
     388  
     389  /* Returns true if instance PTR is registered by the memory description.  */
     390  
     391  template <class T>
     392  inline bool
     393  mem_alloc_description<T>::contains_descriptor_for_instance (const void *ptr)
     394  {
     395    return m_reverse_map->get (ptr);
     396  }
     397  
     398  /* Return descriptor for instance PTR.  */
     399  
     400  template <class T>
     401  inline T*
     402  mem_alloc_description<T>::get_descriptor_for_instance (const void *ptr)
     403  {
     404    return m_reverse_map->get (ptr) ? (*m_reverse_map->get (ptr)).usage : NULL;
     405  }
     406  
     407  /* Register memory allocation descriptor for container PTR which is
     408     described by a memory LOCATION.  */
     409  
     410  template <class T>
     411  inline T*
     412  mem_alloc_description<T>::register_descriptor (const void *ptr,
     413  					       mem_location *location)
     414  {
     415    T *usage = NULL;
     416  
     417    T **slot = m_map->get (location);
     418    if (slot)
     419      {
     420        delete location;
     421        usage = *slot;
     422        usage->m_instances++;
     423      }
     424    else
     425      {
     426        usage = new T ();
     427        m_map->put (location, usage);
     428      }
     429  
     430    if (!m_reverse_map->get (ptr))
     431      m_reverse_map->put (ptr, mem_usage_pair<T> (usage, 0));
     432  
     433    return usage;
     434  }
     435  
     436  /* Register memory allocation descriptor for container PTR.  ORIGIN identifies
     437     type of container and GGC identifes if the allocation is handled in GGC
     438     memory.  Each location is identified by file NAME, LINE in source code and
     439     FUNCTION name.  */
     440  
     441  template <class T>
     442  inline T*
     443  mem_alloc_description<T>::register_descriptor (const void *ptr,
     444  					       mem_alloc_origin origin,
     445  					       bool ggc,
     446  					       const char *filename,
     447  					       int line,
     448  					       const char *function)
     449  {
     450    mem_location *l = new mem_location (origin, ggc, filename, line, function);
     451    return register_descriptor (ptr, l);
     452  }
     453  
     454  /* Register instance overhead identified by PTR pointer. Allocation takes
     455     SIZE bytes.  */
     456  
     457  template <class T>
     458  inline T*
     459  mem_alloc_description<T>::register_instance_overhead (size_t size,
     460  						      const void *ptr)
     461  {
     462    mem_usage_pair <T> *slot = m_reverse_map->get (ptr);
     463    if (!slot)
     464      {
     465        /* Due to PCH, it can really happen.  */
     466        return NULL;
     467      }
     468  
     469    T *usage = (*slot).usage;
     470    usage->register_overhead (size);
     471  
     472    return usage;
     473  }
     474  
     475  /* For containers (and GGC) where we want to track every instance object,
     476     we register allocation of SIZE bytes, identified by PTR pointer, belonging
     477     to USAGE descriptor.  */
     478  
     479  template <class T>
     480  void
     481  mem_alloc_description<T>::register_object_overhead (T *usage, size_t size,
     482  						    const void *ptr)
     483  {
     484    /* In case of GGC, it is possible to have already occupied the memory
     485       location.  */
     486    m_reverse_object_map->put (ptr, std::pair<T *, size_t> (usage, size));
     487  }
     488  
     489  /* Register overhead of SIZE bytes of ORIGIN type. PTR pointer is allocated
     490     in NAME source file, at LINE in source code, in FUNCTION.  */
     491  
     492  template <class T>
     493  inline T*
     494  mem_alloc_description<T>::register_overhead (size_t size,
     495  					     mem_alloc_origin origin,
     496  					     const char *filename,
     497  					     int line,
     498  					     const char *function,
     499  					     const void *ptr)
     500  {
     501    T *usage = register_descriptor (ptr, origin, filename, line, function);
     502    usage->register_overhead (size);
     503  
     504    return usage;
     505  }
     506  
     507  /* Release PTR pointer of SIZE bytes.  */
     508  
     509  template <class T>
     510  inline T *
     511  mem_alloc_description<T>::release_instance_overhead (void *ptr, size_t size,
     512  						     bool remove_from_map)
     513  {
     514    mem_usage_pair<T> *slot = m_reverse_map->get (ptr);
     515  
     516    if (!slot)
     517      {
     518        /* Due to PCH, it can really happen.  */
     519        return NULL;
     520      }
     521  
     522    T *usage = (*slot).usage;
     523    usage->release_overhead (size);
     524  
     525    if (remove_from_map)
     526      m_reverse_map->remove (ptr);
     527  
     528    return usage;
     529  }
     530  
     531  /* Release instance object identified by PTR pointer.  */
     532  
     533  template <class T>
     534  inline void
     535  mem_alloc_description<T>::release_object_overhead (void *ptr)
     536  {
     537    std::pair <T *, size_t> *entry = m_reverse_object_map->get (ptr);
     538    entry->first->release_overhead (entry->second);
     539    m_reverse_object_map->remove (ptr);
     540  }
     541  
     542  /* Unregister a memory allocation descriptor registered with
     543     register_descriptor (remove from reverse map), unless it is
     544     unregistered through release_instance_overhead with
     545     REMOVE_FROM_MAP = true.  */
     546  template <class T>
     547  inline void
     548  mem_alloc_description<T>::unregister_descriptor (void *ptr)
     549  {
     550    m_reverse_map->remove (ptr);
     551  }
     552  
     553  /* Default contructor.  */
     554  
     555  template <class T>
     556  inline
     557  mem_alloc_description<T>::mem_alloc_description ()
     558  {
     559    m_map = new mem_map_t (13, false, false, false);
     560    m_reverse_map = new reverse_mem_map_t (13, false, false, false);
     561    m_reverse_object_map = new reverse_object_map_t (13, false, false, false);
     562  }
     563  
     564  /* Default destructor.  */
     565  
     566  template <class T>
     567  inline
     568  mem_alloc_description<T>::~mem_alloc_description ()
     569  {
     570    for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end ();
     571         ++it)
     572      {
     573        delete (*it).first;
     574        delete (*it).second;
     575      }
     576  
     577    delete m_map;
     578    delete m_reverse_map;
     579    delete m_reverse_object_map;
     580  }
     581  
     582  /* Get all tracked instances registered by the description. Items are filtered
     583     by ORIGIN type, LENGTH is return value where we register the number of
     584     elements in the list. If we want to process custom order, CMP comparator
     585     can be provided.  */
     586  
     587  template <class T>
     588  inline
     589  typename mem_alloc_description<T>::mem_list_t *
     590  mem_alloc_description<T>::get_list (mem_alloc_origin origin, unsigned *length)
     591  {
     592    /* vec data structure is not used because all vectors generate memory
     593       allocation info a it would create a cycle.  */
     594    size_t element_size = sizeof (mem_list_t);
     595    mem_list_t *list = XCNEWVEC (mem_list_t, m_map->elements ());
     596    unsigned i = 0;
     597  
     598    for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end ();
     599         ++it)
     600      if ((*it).first->m_origin == origin)
     601        list[i++] = std::pair<mem_location*, T*> (*it);
     602  
     603    qsort (list, i, element_size, T::compare);
     604    *length = i;
     605  
     606    return list;
     607  }
     608  
     609  /* Get sum value for ORIGIN type of allocation for the descriptor.  */
     610  
     611  template <class T>
     612  inline T
     613  mem_alloc_description<T>::get_sum (mem_alloc_origin origin)
     614  {
     615    unsigned length;
     616    mem_list_t *list = get_list (origin, &length);
     617    T sum;
     618  
     619    for (unsigned i = 0; i < length; i++)
     620      sum = sum + *list[i].second;
     621  
     622    XDELETEVEC (list);
     623  
     624    return sum;
     625  }
     626  
     627  /* Dump all tracked instances of type ORIGIN. If we want to process custom
     628     order, CMP comparator can be provided.  */
     629  
     630  template <class T>
     631  inline void
     632  mem_alloc_description<T>::dump (mem_alloc_origin origin)
     633  {
     634    unsigned length;
     635  
     636    fprintf (stderr, "\n");
     637  
     638    mem_list_t *list = get_list (origin, &length);
     639    T total = get_sum (origin);
     640  
     641    T::print_dash_line ();
     642    T::dump_header (mem_location::get_origin_name (origin));
     643    T::print_dash_line ();
     644    for (int i = length - 1; i >= 0; i--)
     645      list[i].second->dump (list[i].first, total);
     646    T::print_dash_line ();
     647  
     648    T::dump_header (mem_location::get_origin_name (origin));
     649    T::print_dash_line ();
     650    total.dump_footer ();
     651    T::print_dash_line ();
     652  
     653    XDELETEVEC (list);
     654  
     655    fprintf (stderr, "\n");
     656  }
     657  
     658  #endif // GCC_MEM_STATS_H