1 /* A memory statistics tracking infrastructure.
2    Copyright (C) 2015-2016 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   /* Comparison 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 ? m_times < second.m_times
172 	     : m_peak < second.m_peak) : m_allocated < second.m_allocated);
173   }
174 
175   /* Compare wrapper used by qsort method.  */
176   static int
comparemem_usage177   compare (const void *first, const void *second)
178   {
179     typedef std::pair<mem_location *, mem_usage *> mem_pair_t;
180 
181     const mem_pair_t f = *(const mem_pair_t *)first;
182     const mem_pair_t s = *(const mem_pair_t *)second;
183 
184     return (*f.second) < (*s.second);
185   }
186 
187   /* Dump usage coupled to LOC location, where TOTAL is sum of all rows.  */
188   inline void
dumpmem_usage189   dump (mem_location *loc, mem_usage &total) const
190   {
191     char *location_string = loc->to_string ();
192 
193     fprintf (stderr, "%-48s %10" PRIu64 ":%5.1f%%"
194 	     "%10" PRIu64 "%10" PRIu64 ":%5.1f%%%10s\n",
195 	     location_string, (uint64_t)m_allocated,
196 	     get_percent (m_allocated, total.m_allocated),
197 	     (uint64_t)m_peak, (uint64_t)m_times,
198 	     get_percent (m_times, total.m_times), loc->m_ggc ? "ggc" : "heap");
199 
200     free (location_string);
201   }
202 
203   /* Dump footer.  */
204   inline void
dump_footermem_usage205   dump_footer () const
206   {
207     print_dash_line ();
208     fprintf (stderr, "%s%54" PRIu64 "%27" PRIu64 "\n", "Total",
209 	     (uint64_t)m_allocated, (uint64_t)m_times);
210     print_dash_line ();
211   }
212 
213   /* Return fraction of NOMINATOR and DENOMINATOR in percent.  */
214   static inline float
get_percentmem_usage215   get_percent (size_t nominator, size_t denominator)
216   {
217     return denominator == 0 ? 0.0f : nominator * 100.0 / denominator;
218   }
219 
220   /* Print line made of dashes.  */
221   static inline void
222   print_dash_line (size_t count = 140)
223   {
224     while (count--)
225       fputc ('-', stderr);
226     fputc ('\n', stderr);
227   }
228 
229   /* Dump header with NAME.  */
230   static inline void
dump_headermem_usage231   dump_header (const char *name)
232   {
233     fprintf (stderr, "%-48s %11s%16s%10s%17s\n", name, "Leak", "Peak",
234 	     "Times", "Type");
235     print_dash_line ();
236   }
237 
238   /* Current number of allocated bytes.  */
239   size_t m_allocated;
240   /* Number of allocations.  */
241   size_t m_times;
242   /* Peak allocation in bytes.  */
243   size_t m_peak;
244   /* Number of container instances.  */
245   size_t m_instances;
246 };
247 
248 /* Memory usage pair that connectes memory usage and number
249    of allocated bytes.  */
250 template <class T>
251 struct mem_usage_pair
252 {
mem_usage_pairmem_usage_pair253   mem_usage_pair (T *usage_, size_t allocated_): usage (usage_),
254   allocated (allocated_) {}
255 
256   T *usage;
257   size_t allocated;
258 };
259 
260 /* Memory allocation description.  */
261 template <class T>
262 class mem_alloc_description
263 {
264 public:
265   struct mem_location_hash : nofree_ptr_hash <mem_location>
266   {
267     static hashval_t
hashmem_location_hash268     hash (value_type l)
269     {
270 	inchash::hash hstate;
271 
272 	hstate.add_ptr ((const void *)l->m_filename);
273 	hstate.add_ptr (l->m_function);
274 	hstate.add_int (l->m_line);
275 
276 	return hstate.end ();
277     }
278 
279     static bool
equalmem_location_hash280     equal (value_type l1, value_type l2)
281     {
282       return l1->m_filename == l2->m_filename
283 	&& l1->m_function == l2->m_function
284 	&& l1->m_line == l2->m_line;
285     }
286   };
287 
288   /* Internal class type definitions.  */
289   typedef hash_map <mem_location_hash, T *> mem_map_t;
290   typedef hash_map <const void *, mem_usage_pair<T> > reverse_mem_map_t;
291   typedef hash_map <const void *, std::pair<T *, size_t> > reverse_object_map_t;
292   typedef std::pair <mem_location *, T *> mem_list_t;
293 
294   /* Default contructor.  */
295   mem_alloc_description ();
296 
297   /* Default destructor.  */
298   ~mem_alloc_description ();
299 
300   /* Returns true if instance PTR is registered by the memory description.  */
301   bool
302   contains_descriptor_for_instance (const void *ptr);
303 
304   /* Return descriptor for instance PTR.  */
305   T *
306   get_descriptor_for_instance (const void *ptr);
307 
308   /* Register memory allocation descriptor for container PTR which is
309      described by a memory LOCATION.  */
310   T *
311   register_descriptor (const void *ptr, mem_location *location);
312 
313   /* Register memory allocation descriptor for container PTR.  ORIGIN identifies
314      type of container and GGC identifes if the allocation is handled in GGC
315      memory.  Each location is identified by file NAME, LINE in source code and
316      FUNCTION name.  */
317   T *
318   register_descriptor (const void *ptr, mem_alloc_origin origin,
319 			  bool ggc, const char *name, int line,
320 			  const char *function);
321 
322   /* Register instance overhead identified by PTR pointer. Allocation takes
323      SIZE bytes.  */
324   T *
325   register_instance_overhead (size_t size, const void *ptr);
326 
327   /* For containers (and GGC) where we want to track every instance object,
328      we register allocation of SIZE bytes, identified by PTR pointer, belonging
329      to USAGE descriptor.  */
330   void
331   register_object_overhead (T *usage, size_t size, const void *ptr);
332 
333   /* Release PTR pointer of SIZE bytes. If REMOVE_FROM_MAP is set to true,
334      remove the instance from reverse map.  */
335   void
336   release_instance_overhead (void *ptr, size_t size,
337 				  bool remove_from_map = false);
338 
339   /* Release intance object identified by PTR pointer.  */
340   void
341   release_object_overhead (void *ptr);
342 
343   /* Get sum value for ORIGIN type of allocation for the descriptor.  */
344   T
345   get_sum (mem_alloc_origin origin);
346 
347   /* Get all tracked instances registered by the description. Items
348      are filtered by ORIGIN type, LENGTH is return value where we register
349      the number of elements in the list. If we want to process custom order,
350      CMP comparator can be provided.  */
351   mem_list_t *
352   get_list (mem_alloc_origin origin, unsigned *length,
353 	    int (*cmp) (const void *first, const void *second) = NULL);
354 
355   /* Dump all tracked instances of type ORIGIN. If we want to process custom
356      order, CMP comparator can be provided.  */
357   void dump (mem_alloc_origin origin,
358 	     int (*cmp) (const void *first, const void *second) = NULL);
359 
360   /* Reverse object map used for every object allocation mapping.  */
361   reverse_object_map_t *m_reverse_object_map;
362 
363 private:
364   /* Register overhead of SIZE bytes of ORIGIN type. PTR pointer is allocated
365      in NAME source file, at LINE in source code, in FUNCTION.  */
366   T *register_overhead (size_t size, mem_alloc_origin origin, const char *name,
367 			int line, const char *function, const void *ptr);
368 
369   /* Allocation location coupled to the description.  */
370   mem_location m_location;
371 
372   /* Location to usage mapping.  */
373   mem_map_t *m_map;
374 
375   /* Reverse pointer to usage mapping.  */
376   reverse_mem_map_t *m_reverse_map;
377 };
378 
379 
380 /* Returns true if instance PTR is registered by the memory description.  */
381 
382 template <class T>
383 inline bool
contains_descriptor_for_instance(const void * ptr)384 mem_alloc_description<T>::contains_descriptor_for_instance (const void *ptr)
385 {
386   return m_reverse_map->get (ptr);
387 }
388 
389 /* Return descriptor for instance PTR.  */
390 
391 template <class T>
392 inline T*
get_descriptor_for_instance(const void * ptr)393 mem_alloc_description<T>::get_descriptor_for_instance (const void *ptr)
394 {
395   return m_reverse_map->get (ptr) ? (*m_reverse_map->get (ptr)).usage : NULL;
396 }
397 
398 
399   /* Register memory allocation descriptor for container PTR which is
400      described by a memory LOCATION.  */
401 template <class T>
402 inline T*
register_descriptor(const void * ptr,mem_location * location)403 mem_alloc_description<T>::register_descriptor (const void *ptr,
404 					       mem_location *location)
405 {
406   T *usage = NULL;
407 
408   T **slot = m_map->get (location);
409   if (slot)
410     {
411       delete location;
412       usage = *slot;
413       usage->m_instances++;
414     }
415   else
416     {
417       usage = new T ();
418       m_map->put (location, usage);
419     }
420 
421   if (!m_reverse_map->get (ptr))
422     m_reverse_map->put (ptr, mem_usage_pair<T> (usage, 0));
423 
424   return usage;
425 }
426 
427 /* Register memory allocation descriptor for container PTR.  ORIGIN identifies
428    type of container and GGC identifes if the allocation is handled in GGC
429    memory.  Each location is identified by file NAME, LINE in source code and
430    FUNCTION name.  */
431 
432 template <class T>
433 inline T*
register_descriptor(const void * ptr,mem_alloc_origin origin,bool ggc,const char * filename,int line,const char * function)434 mem_alloc_description<T>::register_descriptor (const void *ptr,
435 					       mem_alloc_origin origin,
436 					       bool ggc,
437 					       const char *filename,
438 					       int line,
439 					       const char *function)
440 {
441   mem_location *l = new mem_location (origin, ggc, filename, line, function);
442   return register_descriptor (ptr, l);
443 }
444 
445 /* Register instance overhead identified by PTR pointer. Allocation takes
446    SIZE bytes.  */
447 
448 template <class T>
449 inline T*
register_instance_overhead(size_t size,const void * ptr)450 mem_alloc_description<T>::register_instance_overhead (size_t size,
451 						      const void *ptr)
452 {
453   mem_usage_pair <T> *slot = m_reverse_map->get (ptr);
454   if (!slot)
455     {
456       /* Due to PCH, it can really happen.  */
457       return NULL;
458     }
459 
460   T *usage = (*slot).usage;
461   usage->register_overhead (size);
462 
463   return usage;
464 }
465 
466 /* For containers (and GGC) where we want to track every instance object,
467    we register allocation of SIZE bytes, identified by PTR pointer, belonging
468    to USAGE descriptor.  */
469 
470 template <class T>
471 void
register_object_overhead(T * usage,size_t size,const void * ptr)472 mem_alloc_description<T>::register_object_overhead (T *usage, size_t size,
473 						    const void *ptr)
474 {
475   /* In case of GGC, it is possible to have already occupied the memory
476      location.  */
477   m_reverse_object_map->put (ptr, std::pair<T *, size_t> (usage, size));
478 }
479 
480 /* Register overhead of SIZE bytes of ORIGIN type. PTR pointer is allocated
481    in NAME source file, at LINE in source code, in FUNCTION.  */
482 
483 template <class T>
484 inline T*
register_overhead(size_t size,mem_alloc_origin origin,const char * filename,int line,const char * function,const void * ptr)485 mem_alloc_description<T>::register_overhead (size_t size,
486 					     mem_alloc_origin origin,
487 					     const char *filename,
488 					     int line,
489 					     const char *function,
490 					     const void *ptr)
491 {
492   T *usage = register_descriptor (ptr, origin, filename, line, function);
493   usage->register_overhead (size);
494 
495   return usage;
496 }
497 
498 /* Release PTR pointer of SIZE bytes.  */
499 
500 template <class T>
501 inline void
release_instance_overhead(void * ptr,size_t size,bool remove_from_map)502 mem_alloc_description<T>::release_instance_overhead (void *ptr, size_t size,
503 						     bool remove_from_map)
504 {
505   mem_usage_pair<T> *slot = m_reverse_map->get (ptr);
506 
507   if (!slot)
508     {
509       /* Due to PCH, it can really happen.  */
510       return;
511     }
512 
513   mem_usage_pair<T> usage_pair = *slot;
514   usage_pair.usage->release_overhead (size);
515 
516   if (remove_from_map)
517     m_reverse_map->remove (ptr);
518 }
519 
520 /* Release intance object identified by PTR pointer.  */
521 
522 template <class T>
523 inline void
release_object_overhead(void * ptr)524 mem_alloc_description<T>::release_object_overhead (void *ptr)
525 {
526   std::pair <T *, size_t> *entry = m_reverse_object_map->get (ptr);
527   if (entry)
528     {
529       entry->first->release_overhead (entry->second);
530       m_reverse_object_map->remove (ptr);
531     }
532 }
533 
534 /* Default contructor.  */
535 
536 template <class T>
537 inline
mem_alloc_description()538 mem_alloc_description<T>::mem_alloc_description ()
539 {
540   m_map = new mem_map_t (13, false, false);
541   m_reverse_map = new reverse_mem_map_t (13, false, false);
542   m_reverse_object_map = new reverse_object_map_t (13, false, false);
543 }
544 
545 /* Default destructor.  */
546 
547 template <class T>
548 inline
~mem_alloc_description()549 mem_alloc_description<T>::~mem_alloc_description ()
550 {
551   for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end ();
552        ++it)
553     {
554       delete (*it).first;
555       delete (*it).second;
556     }
557 
558   delete m_map;
559   delete m_reverse_map;
560   delete m_reverse_object_map;
561 }
562 
563 /* Get all tracked instances registered by the description. Items are filtered
564    by ORIGIN type, LENGTH is return value where we register the number of
565    elements in the list. If we want to process custom order, CMP comparator
566    can be provided.  */
567 
568 template <class T>
569 inline
570 typename mem_alloc_description<T>::mem_list_t *
get_list(mem_alloc_origin origin,unsigned * length,int (* cmp)(const void * first,const void * second))571 mem_alloc_description<T>::get_list (mem_alloc_origin origin, unsigned *length,
572 			int (*cmp) (const void *first, const void *second))
573 {
574   /* vec data structure is not used because all vectors generate memory
575      allocation info a it would create a cycle.  */
576   size_t element_size = sizeof (mem_list_t);
577   mem_list_t *list = XCNEWVEC (mem_list_t, m_map->elements ());
578   unsigned i = 0;
579 
580   for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end ();
581        ++it)
582     if ((*it).first->m_origin == origin)
583       list[i++] = std::pair<mem_location*, T*> (*it);
584 
585   qsort (list, i, element_size, cmp == NULL ? T::compare : cmp);
586   *length = i;
587 
588   return list;
589 }
590 
591 /* Get sum value for ORIGIN type of allocation for the descriptor.  */
592 
593 template <class T>
594 inline T
get_sum(mem_alloc_origin origin)595 mem_alloc_description<T>::get_sum (mem_alloc_origin origin)
596 {
597   unsigned length;
598   mem_list_t *list = get_list (origin, &length);
599   T sum;
600 
601   for (unsigned i = 0; i < length; i++)
602     sum = sum + *list[i].second;
603 
604   XDELETEVEC (list);
605 
606   return sum;
607 }
608 
609 /* Dump all tracked instances of type ORIGIN. If we want to process custom
610    order, CMP comparator can be provided.  */
611 
612 template <class T>
613 inline void
dump(mem_alloc_origin origin,int (* cmp)(const void * first,const void * second))614 mem_alloc_description<T>::dump (mem_alloc_origin origin,
615 				int (*cmp) (const void *first,
616 					    const void *second))
617 {
618   unsigned length;
619 
620   fprintf (stderr, "\n");
621 
622   mem_list_t *list = get_list (origin, &length, cmp);
623   T total = get_sum (origin);
624 
625   T::dump_header (mem_location::get_origin_name (origin));
626   for (int i = length - 1; i >= 0; i--)
627     list[i].second->dump (list[i].first, total);
628 
629   total.dump_footer ();
630 
631   XDELETEVEC (list);
632 
633   fprintf (stderr, "\n");
634 }
635 
636 #endif // GCC_MEM_STATS_H
637