1 /* A memory statistics tracking infrastructure.
2    Copyright (C) 2015-2018 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 struct mem_location
35 {
36   /* Default constructor.  */
37   inline
mem_locationmem_location38   mem_location () {}
39 
40   /* Constructor.  */
41   inline
42   mem_location (mem_alloc_origin origin, bool ggc,
43 		const char *filename = NULL, int line = 0,
44 		const char *function = NULL):
m_filenamemem_location45     m_filename (filename), m_function (function), m_line (line), m_origin
46     (origin), m_ggc (ggc) {}
47 
48   /* Copy constructor.  */
49   inline
mem_locationmem_location50   mem_location (mem_location &other): m_filename (other.m_filename),
51     m_function (other.m_function), m_line (other.m_line),
52     m_origin (other.m_origin), m_ggc (other.m_ggc) {}
53 
54   /* Compute hash value based on file name, function name and line in
55      source code. As there is just a single pointer registered for every
56      constant that points to e.g. the same file name, we can use hash
57      of the pointer.  */
58   hashval_t
hashmem_location59   hash ()
60   {
61     inchash::hash hash;
62 
63     hash.add_ptr (m_filename);
64     hash.add_ptr (m_function);
65     hash.add_int (m_line);
66 
67     return hash.end ();
68   }
69 
70   /* Return true if the memory location is equal to OTHER.  */
71   int
equalmem_location72   equal (mem_location &other)
73   {
74     return m_filename == other.m_filename && m_function == other.m_function
75       && m_line == other.m_line;
76   }
77 
78   /* Return trimmed filename for the location.  */
79   inline const char *
get_trimmed_filenamemem_location80   get_trimmed_filename ()
81   {
82     const char *s1 = m_filename;
83     const char *s2;
84 
85     while ((s2 = strstr (s1, "gcc/")))
86       s1 = s2 + 4;
87 
88     return s1;
89   }
90 
91   inline char *
to_stringmem_location92   to_string ()
93   {
94     unsigned l = strlen (get_trimmed_filename ()) + strlen (m_function)
95       + LOCATION_LINE_EXTRA_SPACE;
96 
97     char *s = XNEWVEC (char, l);
98     sprintf (s, "%s:%i (%s)", get_trimmed_filename (),
99 	     m_line, m_function);
100 
101     s[MIN (LOCATION_LINE_WIDTH, l - 1)] = '\0';
102 
103     return s;
104   }
105 
106   /* Return display name associated to ORIGIN type.  */
107   static const char *
get_origin_namemem_location108   get_origin_name (mem_alloc_origin origin)
109   {
110     return mem_alloc_origin_names[(unsigned) origin];
111   }
112 
113   /* File name of source code.  */
114   const char *m_filename;
115   /* Funcation name.  */
116   const char *m_function;
117   /* Line number in source code.  */
118   int m_line;
119   /* Origin type.  */
120   mem_alloc_origin m_origin;
121   /* Flag if used by GGC allocation.  */
122   bool m_ggc;
123 };
124 
125 /* Memory usage register to a memory location.  */
126 struct mem_usage
127 {
128   /* Default constructor.  */
mem_usagemem_usage129   mem_usage (): m_allocated (0), m_times (0), m_peak (0), m_instances (1) {}
130 
131   /* Constructor.  */
132   mem_usage (size_t allocated, size_t times, size_t peak, size_t instances = 0):
m_allocatedmem_usage133     m_allocated (allocated), m_times (times), m_peak (peak),
134     m_instances (instances) {}
135 
136   /* Register overhead of SIZE bytes.  */
137   inline void
register_overheadmem_usage138   register_overhead (size_t size)
139   {
140     m_allocated += size;
141     m_times++;
142 
143     if (m_peak < m_allocated)
144       m_peak = m_allocated;
145   }
146 
147   /* Release overhead of SIZE bytes.  */
148   inline void
release_overheadmem_usage149   release_overhead (size_t size)
150   {
151     gcc_assert (size <= m_allocated);
152 
153     m_allocated -= size;
154   }
155 
156   /* Sum the usage with SECOND usage.  */
157   mem_usage
158   operator+ (const mem_usage &second)
159   {
160     return mem_usage (m_allocated + second.m_allocated,
161 		      m_times + second.m_times,
162 		      m_peak + second.m_peak,
163 		      m_instances + second.m_instances);
164   }
165 
166   /* Equality operator.  */
167   inline bool
168   operator== (const mem_usage &second) const
169   {
170     return (m_allocated == second.m_allocated
171 	    && m_peak == second.m_peak
172 	    && m_allocated == second.m_allocated);
173   }
174 
175   /* Comparison operator.  */
176   inline bool
177   operator< (const mem_usage &second) const
178   {
179     if (*this == second)
180       return false;
181 
182     return (m_allocated == second.m_allocated ?
183 	    (m_peak == second.m_peak ? m_times < second.m_times
184 	     : m_peak < second.m_peak) : m_allocated < second.m_allocated);
185   }
186 
187   /* Compare wrapper used by qsort method.  */
188   static int
comparemem_usage189   compare (const void *first, const void *second)
190   {
191     typedef std::pair<mem_location *, mem_usage *> mem_pair_t;
192 
193     const mem_pair_t f = *(const mem_pair_t *)first;
194     const mem_pair_t s = *(const mem_pair_t *)second;
195 
196     if (*f.second == *s.second)
197       return 0;
198 
199     return *f.second < *s.second ? 1 : -1;
200   }
201 
202   /* Dump usage coupled to LOC location, where TOTAL is sum of all rows.  */
203   inline void
dumpmem_usage204   dump (mem_location *loc, mem_usage &total) const
205   {
206     char *location_string = loc->to_string ();
207 
208     fprintf (stderr, "%-48s %10" PRIu64 ":%5.1f%%"
209 	     "%10" PRIu64 "%10" PRIu64 ":%5.1f%%%10s\n",
210 	     location_string, (uint64_t)m_allocated,
211 	     get_percent (m_allocated, total.m_allocated),
212 	     (uint64_t)m_peak, (uint64_t)m_times,
213 	     get_percent (m_times, total.m_times), loc->m_ggc ? "ggc" : "heap");
214 
215     free (location_string);
216   }
217 
218   /* Dump footer.  */
219   inline void
dump_footermem_usage220   dump_footer () const
221   {
222     print_dash_line ();
223     fprintf (stderr, "%s%54" PRIu64 "%27" PRIu64 "\n", "Total",
224 	     (uint64_t)m_allocated, (uint64_t)m_times);
225     print_dash_line ();
226   }
227 
228   /* Return fraction of NOMINATOR and DENOMINATOR in percent.  */
229   static inline float
get_percentmem_usage230   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
dump_headermem_usage246   dump_header (const char *name)
247   {
248     fprintf (stderr, "%-48s %11s%16s%10s%17s\n", name, "Leak", "Peak",
249 	     "Times", "Type");
250     print_dash_line ();
251   }
252 
253   /* Current number of allocated bytes.  */
254   size_t m_allocated;
255   /* Number of allocations.  */
256   size_t m_times;
257   /* Peak allocation in bytes.  */
258   size_t m_peak;
259   /* Number of container instances.  */
260   size_t m_instances;
261 };
262 
263 /* Memory usage pair that connectes memory usage and number
264    of allocated bytes.  */
265 template <class T>
266 struct mem_usage_pair
267 {
mem_usage_pairmem_usage_pair268   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
hashmem_location_hash283     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
equalmem_location_hash295     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
317   contains_descriptor_for_instance (const void *ptr);
318 
319   /* Return descriptor for instance PTR.  */
320   T *
321   get_descriptor_for_instance (const void *ptr);
322 
323   /* Register memory allocation descriptor for container PTR which is
324      described by a memory LOCATION.  */
325   T *
326   register_descriptor (const void *ptr, mem_location *location);
327 
328   /* Register memory allocation descriptor for container PTR.  ORIGIN identifies
329      type of container and GGC identifes if the allocation is handled in GGC
330      memory.  Each location is identified by file NAME, LINE in source code and
331      FUNCTION name.  */
332   T *
333   register_descriptor (const void *ptr, mem_alloc_origin origin,
334 			  bool ggc, const char *name, int line,
335 			  const char *function);
336 
337   /* Register instance overhead identified by PTR pointer. Allocation takes
338      SIZE bytes.  */
339   T *
340   register_instance_overhead (size_t size, const void *ptr);
341 
342   /* For containers (and GGC) where we want to track every instance object,
343      we register allocation of SIZE bytes, identified by PTR pointer, belonging
344      to USAGE descriptor.  */
345   void
346   register_object_overhead (T *usage, size_t size, const void *ptr);
347 
348   /* Release PTR pointer of SIZE bytes. If REMOVE_FROM_MAP is set to true,
349      remove the instance from reverse map.  */
350   void
351   release_instance_overhead (void *ptr, size_t size,
352 				  bool remove_from_map = false);
353 
354   /* Release intance object identified by PTR pointer.  */
355   void
356   release_object_overhead (void *ptr);
357 
358   /* Get sum value for ORIGIN type of allocation for the descriptor.  */
359   T
360   get_sum (mem_alloc_origin origin);
361 
362   /* Get all tracked instances registered by the description. Items
363      are filtered by ORIGIN type, LENGTH is return value where we register
364      the number of elements in the list. If we want to process custom order,
365      CMP comparator can be provided.  */
366   mem_list_t *
367   get_list (mem_alloc_origin origin, unsigned *length,
368 	    int (*cmp) (const void *first, const void *second) = NULL);
369 
370   /* Dump all tracked instances of type ORIGIN. If we want to process custom
371      order, CMP comparator can be provided.  */
372   void dump (mem_alloc_origin origin,
373 	     int (*cmp) (const void *first, const void *second) = NULL);
374 
375   /* Reverse object map used for every object allocation mapping.  */
376   reverse_object_map_t *m_reverse_object_map;
377 
378 private:
379   /* Register overhead of SIZE bytes of ORIGIN type. PTR pointer is allocated
380      in NAME source file, at LINE in source code, in FUNCTION.  */
381   T *register_overhead (size_t size, mem_alloc_origin origin, const char *name,
382 			int line, const char *function, const void *ptr);
383 
384   /* Allocation location coupled to the description.  */
385   mem_location m_location;
386 
387   /* Location to usage mapping.  */
388   mem_map_t *m_map;
389 
390   /* Reverse pointer to usage mapping.  */
391   reverse_mem_map_t *m_reverse_map;
392 };
393 
394 
395 /* Returns true if instance PTR is registered by the memory description.  */
396 
397 template <class T>
398 inline bool
contains_descriptor_for_instance(const void * ptr)399 mem_alloc_description<T>::contains_descriptor_for_instance (const void *ptr)
400 {
401   return m_reverse_map->get (ptr);
402 }
403 
404 /* Return descriptor for instance PTR.  */
405 
406 template <class T>
407 inline T*
get_descriptor_for_instance(const void * ptr)408 mem_alloc_description<T>::get_descriptor_for_instance (const void *ptr)
409 {
410   return m_reverse_map->get (ptr) ? (*m_reverse_map->get (ptr)).usage : NULL;
411 }
412 
413 
414   /* Register memory allocation descriptor for container PTR which is
415      described by a memory LOCATION.  */
416 template <class T>
417 inline T*
register_descriptor(const void * ptr,mem_location * location)418 mem_alloc_description<T>::register_descriptor (const void *ptr,
419 					       mem_location *location)
420 {
421   T *usage = NULL;
422 
423   T **slot = m_map->get (location);
424   if (slot)
425     {
426       delete location;
427       usage = *slot;
428       usage->m_instances++;
429     }
430   else
431     {
432       usage = new T ();
433       m_map->put (location, usage);
434     }
435 
436   if (!m_reverse_map->get (ptr))
437     m_reverse_map->put (ptr, mem_usage_pair<T> (usage, 0));
438 
439   return usage;
440 }
441 
442 /* Register memory allocation descriptor for container PTR.  ORIGIN identifies
443    type of container and GGC identifes if the allocation is handled in GGC
444    memory.  Each location is identified by file NAME, LINE in source code and
445    FUNCTION name.  */
446 
447 template <class T>
448 inline T*
register_descriptor(const void * ptr,mem_alloc_origin origin,bool ggc,const char * filename,int line,const char * function)449 mem_alloc_description<T>::register_descriptor (const void *ptr,
450 					       mem_alloc_origin origin,
451 					       bool ggc,
452 					       const char *filename,
453 					       int line,
454 					       const char *function)
455 {
456   mem_location *l = new mem_location (origin, ggc, filename, line, function);
457   return register_descriptor (ptr, l);
458 }
459 
460 /* Register instance overhead identified by PTR pointer. Allocation takes
461    SIZE bytes.  */
462 
463 template <class T>
464 inline T*
register_instance_overhead(size_t size,const void * ptr)465 mem_alloc_description<T>::register_instance_overhead (size_t size,
466 						      const void *ptr)
467 {
468   mem_usage_pair <T> *slot = m_reverse_map->get (ptr);
469   if (!slot)
470     {
471       /* Due to PCH, it can really happen.  */
472       return NULL;
473     }
474 
475   T *usage = (*slot).usage;
476   usage->register_overhead (size);
477 
478   return usage;
479 }
480 
481 /* For containers (and GGC) where we want to track every instance object,
482    we register allocation of SIZE bytes, identified by PTR pointer, belonging
483    to USAGE descriptor.  */
484 
485 template <class T>
486 void
register_object_overhead(T * usage,size_t size,const void * ptr)487 mem_alloc_description<T>::register_object_overhead (T *usage, size_t size,
488 						    const void *ptr)
489 {
490   /* In case of GGC, it is possible to have already occupied the memory
491      location.  */
492   m_reverse_object_map->put (ptr, std::pair<T *, size_t> (usage, size));
493 }
494 
495 /* Register overhead of SIZE bytes of ORIGIN type. PTR pointer is allocated
496    in NAME source file, at LINE in source code, in FUNCTION.  */
497 
498 template <class T>
499 inline T*
register_overhead(size_t size,mem_alloc_origin origin,const char * filename,int line,const char * function,const void * ptr)500 mem_alloc_description<T>::register_overhead (size_t size,
501 					     mem_alloc_origin origin,
502 					     const char *filename,
503 					     int line,
504 					     const char *function,
505 					     const void *ptr)
506 {
507   T *usage = register_descriptor (ptr, origin, filename, line, function);
508   usage->register_overhead (size);
509 
510   return usage;
511 }
512 
513 /* Release PTR pointer of SIZE bytes.  */
514 
515 template <class T>
516 inline void
release_instance_overhead(void * ptr,size_t size,bool remove_from_map)517 mem_alloc_description<T>::release_instance_overhead (void *ptr, size_t size,
518 						     bool remove_from_map)
519 {
520   mem_usage_pair<T> *slot = m_reverse_map->get (ptr);
521 
522   if (!slot)
523     {
524       /* Due to PCH, it can really happen.  */
525       return;
526     }
527 
528   mem_usage_pair<T> usage_pair = *slot;
529   usage_pair.usage->release_overhead (size);
530 
531   if (remove_from_map)
532     m_reverse_map->remove (ptr);
533 }
534 
535 /* Release intance object identified by PTR pointer.  */
536 
537 template <class T>
538 inline void
release_object_overhead(void * ptr)539 mem_alloc_description<T>::release_object_overhead (void *ptr)
540 {
541   std::pair <T *, size_t> *entry = m_reverse_object_map->get (ptr);
542   if (entry)
543     {
544       entry->first->release_overhead (entry->second);
545       m_reverse_object_map->remove (ptr);
546     }
547 }
548 
549 /* Default contructor.  */
550 
551 template <class T>
552 inline
mem_alloc_description()553 mem_alloc_description<T>::mem_alloc_description ()
554 {
555   m_map = new mem_map_t (13, false, false);
556   m_reverse_map = new reverse_mem_map_t (13, false, false);
557   m_reverse_object_map = new reverse_object_map_t (13, false, false);
558 }
559 
560 /* Default destructor.  */
561 
562 template <class T>
563 inline
~mem_alloc_description()564 mem_alloc_description<T>::~mem_alloc_description ()
565 {
566   for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end ();
567        ++it)
568     {
569       delete (*it).first;
570       delete (*it).second;
571     }
572 
573   delete m_map;
574   delete m_reverse_map;
575   delete m_reverse_object_map;
576 }
577 
578 /* Get all tracked instances registered by the description. Items are filtered
579    by ORIGIN type, LENGTH is return value where we register the number of
580    elements in the list. If we want to process custom order, CMP comparator
581    can be provided.  */
582 
583 template <class T>
584 inline
585 typename mem_alloc_description<T>::mem_list_t *
get_list(mem_alloc_origin origin,unsigned * length,int (* cmp)(const void * first,const void * second))586 mem_alloc_description<T>::get_list (mem_alloc_origin origin, unsigned *length,
587 			int (*cmp) (const void *first, const void *second))
588 {
589   /* vec data structure is not used because all vectors generate memory
590      allocation info a it would create a cycle.  */
591   size_t element_size = sizeof (mem_list_t);
592   mem_list_t *list = XCNEWVEC (mem_list_t, m_map->elements ());
593   unsigned i = 0;
594 
595   for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end ();
596        ++it)
597     if ((*it).first->m_origin == origin)
598       list[i++] = std::pair<mem_location*, T*> (*it);
599 
600   qsort (list, i, element_size, cmp == NULL ? T::compare : cmp);
601   *length = i;
602 
603   return list;
604 }
605 
606 /* Get sum value for ORIGIN type of allocation for the descriptor.  */
607 
608 template <class T>
609 inline T
get_sum(mem_alloc_origin origin)610 mem_alloc_description<T>::get_sum (mem_alloc_origin origin)
611 {
612   unsigned length;
613   mem_list_t *list = get_list (origin, &length);
614   T sum;
615 
616   for (unsigned i = 0; i < length; i++)
617     sum = sum + *list[i].second;
618 
619   XDELETEVEC (list);
620 
621   return sum;
622 }
623 
624 /* Dump all tracked instances of type ORIGIN. If we want to process custom
625    order, CMP comparator can be provided.  */
626 
627 template <class T>
628 inline void
dump(mem_alloc_origin origin,int (* cmp)(const void * first,const void * second))629 mem_alloc_description<T>::dump (mem_alloc_origin origin,
630 				int (*cmp) (const void *first,
631 					    const void *second))
632 {
633   unsigned length;
634 
635   fprintf (stderr, "\n");
636 
637   mem_list_t *list = get_list (origin, &length, cmp);
638   T total = get_sum (origin);
639 
640   T::dump_header (mem_location::get_origin_name (origin));
641   for (int i = length - 1; i >= 0; i--)
642     list[i].second->dump (list[i].first, total);
643 
644   total.dump_footer ();
645 
646   XDELETEVEC (list);
647 
648   fprintf (stderr, "\n");
649 }
650 
651 #endif // GCC_MEM_STATS_H
652