xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/mem-stats.h (revision d536862b7d93d77932ef5de7eebdc48d76921b77)
1 /* A memory statistics tracking infrastructure.
2    Copyright (C) 2015-2019 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
38   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):
45     m_filename (filename), m_function (function), m_line (line), m_origin
46     (origin), m_ggc (ggc) {}
47 
48   /* Copy constructor.  */
49   inline
50   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
59   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
72   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 *
80   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 *
92   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 *
108   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.  */
129   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):
133     m_allocated (allocated), m_times (times), m_peak (peak),
134     m_instances (instances) {}
135 
136   /* Register overhead of SIZE bytes.  */
137   inline void
138   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
149   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_times == second.m_times);
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
189   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
204   dump (mem_location *loc, mem_usage &total) const
205   {
206     char *location_string = loc->to_string ();
207 
208     fprintf (stderr, "%-48s " PRsa (9) ":%5.1f%%"
209 	     PRsa (9) PRsa (9) ":%5.1f%%%10s\n",
210 	     location_string, SIZE_AMOUNT (m_allocated),
211 	     get_percent (m_allocated, total.m_allocated),
212 	     SIZE_AMOUNT (m_peak), SIZE_AMOUNT (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
220   dump_footer () const
221   {
222     fprintf (stderr, "%s" PRsa (53) PRsa (26) "\n", "Total",
223 	     SIZE_AMOUNT (m_allocated), SIZE_AMOUNT (m_times));
224   }
225 
226   /* Return fraction of NOMINATOR and DENOMINATOR in percent.  */
227   static inline float
228   get_percent (size_t nominator, size_t denominator)
229   {
230     return denominator == 0 ? 0.0f : nominator * 100.0 / denominator;
231   }
232 
233   /* Print line made of dashes.  */
234   static inline void
235   print_dash_line (size_t count = 140)
236   {
237     while (count--)
238       fputc ('-', stderr);
239     fputc ('\n', stderr);
240   }
241 
242   /* Dump header with NAME.  */
243   static inline void
244   dump_header (const char *name)
245   {
246     fprintf (stderr, "%-48s %11s%16s%10s%17s\n", name, "Leak", "Peak",
247 	     "Times", "Type");
248   }
249 
250   /* Current number of allocated bytes.  */
251   size_t m_allocated;
252   /* Number of allocations.  */
253   size_t m_times;
254   /* Peak allocation in bytes.  */
255   size_t m_peak;
256   /* Number of container instances.  */
257   size_t m_instances;
258 };
259 
260 /* Memory usage pair that connectes memory usage and number
261    of allocated bytes.  */
262 template <class T>
263 struct mem_usage_pair
264 {
265   mem_usage_pair (T *usage_, size_t allocated_): usage (usage_),
266   allocated (allocated_) {}
267 
268   T *usage;
269   size_t allocated;
270 };
271 
272 /* Memory allocation description.  */
273 template <class T>
274 class mem_alloc_description
275 {
276 public:
277   struct mem_location_hash : nofree_ptr_hash <mem_location>
278   {
279     static hashval_t
280     hash (value_type l)
281     {
282       inchash::hash hstate;
283 
284       hstate.add_ptr ((const void *)l->m_filename);
285       hstate.add_ptr (l->m_function);
286       hstate.add_int (l->m_line);
287 
288       return hstate.end ();
289     }
290 
291     static bool
292     equal (value_type l1, value_type l2)
293     {
294       return (l1->m_filename == l2->m_filename
295 	      && l1->m_function == l2->m_function
296 	      && l1->m_line == l2->m_line);
297     }
298   };
299 
300   /* Internal class type definitions.  */
301   typedef hash_map <mem_location_hash, T *> mem_map_t;
302   typedef hash_map <const void *, mem_usage_pair<T> > reverse_mem_map_t;
303   typedef hash_map <const void *, std::pair<T *, size_t> > reverse_object_map_t;
304   typedef std::pair <mem_location *, T *> mem_list_t;
305 
306   /* Default contructor.  */
307   mem_alloc_description ();
308 
309   /* Default destructor.  */
310   ~mem_alloc_description ();
311 
312   /* Returns true if instance PTR is registered by the memory description.  */
313   bool contains_descriptor_for_instance (const void *ptr);
314 
315   /* Return descriptor for instance PTR.  */
316   T *get_descriptor_for_instance (const void *ptr);
317 
318   /* Register memory allocation descriptor for container PTR which is
319      described by a memory LOCATION.  */
320   T *register_descriptor (const void *ptr, mem_location *location);
321 
322   /* Register memory allocation descriptor for container PTR.  ORIGIN identifies
323      type of container and GGC identifes if the allocation is handled in GGC
324      memory.  Each location is identified by file NAME, LINE in source code and
325      FUNCTION name.  */
326   T *register_descriptor (const void *ptr, mem_alloc_origin origin,
327 			  bool ggc, const char *name, int line,
328 			  const char *function);
329 
330   /* Register instance overhead identified by PTR pointer. Allocation takes
331      SIZE bytes.  */
332   T *register_instance_overhead (size_t size, const void *ptr);
333 
334   /* For containers (and GGC) where we want to track every instance object,
335      we register allocation of SIZE bytes, identified by PTR pointer, belonging
336      to USAGE descriptor.  */
337   void register_object_overhead (T *usage, size_t size, const void *ptr);
338 
339   /* Release PTR pointer of SIZE bytes. If REMOVE_FROM_MAP is set to true,
340      remove the instance from reverse map.  Return memory usage that belongs
341      to this memory description.  */
342   T *release_instance_overhead (void *ptr, size_t size,
343 				bool remove_from_map = false);
344 
345   /* Release instance object identified by PTR pointer.  */
346   void release_object_overhead (void *ptr);
347 
348   /* Unregister a memory allocation descriptor registered with
349      register_descriptor (remove from reverse map), unless it is
350      unregistered through release_instance_overhead with
351      REMOVE_FROM_MAP = true.  */
352   void unregister_descriptor (void *ptr);
353 
354   /* Get sum value for ORIGIN type of allocation for the descriptor.  */
355   T get_sum (mem_alloc_origin origin);
356 
357   /* Get all tracked instances registered by the description. Items
358      are filtered by ORIGIN type, LENGTH is return value where we register
359      the number of elements in the list. If we want to process custom order,
360      CMP comparator can be provided.  */
361   mem_list_t *get_list (mem_alloc_origin origin, unsigned *length,
362 			int (*cmp) (const void *first,
363 				    const void *second) = NULL);
364 
365   /* Dump all tracked instances of type ORIGIN. If we want to process custom
366      order, CMP comparator can be provided.  */
367   void dump (mem_alloc_origin origin,
368 	     int (*cmp) (const void *first, const void *second) = NULL);
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   if (entry)
539     {
540       entry->first->release_overhead (entry->second);
541       m_reverse_object_map->remove (ptr);
542     }
543 }
544 
545 /* Unregister a memory allocation descriptor registered with
546    register_descriptor (remove from reverse map), unless it is
547    unregistered through release_instance_overhead with
548    REMOVE_FROM_MAP = true.  */
549 template <class T>
550 inline void
551 mem_alloc_description<T>::unregister_descriptor (void *ptr)
552 {
553   m_reverse_map->remove (ptr);
554 }
555 
556 /* Default contructor.  */
557 
558 template <class T>
559 inline
560 mem_alloc_description<T>::mem_alloc_description ()
561 {
562   m_map = new mem_map_t (13, false, false);
563   m_reverse_map = new reverse_mem_map_t (13, false, false);
564   m_reverse_object_map = new reverse_object_map_t (13, false, false);
565 }
566 
567 /* Default destructor.  */
568 
569 template <class T>
570 inline
571 mem_alloc_description<T>::~mem_alloc_description ()
572 {
573   for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end ();
574        ++it)
575     {
576       delete (*it).first;
577       delete (*it).second;
578     }
579 
580   delete m_map;
581   delete m_reverse_map;
582   delete m_reverse_object_map;
583 }
584 
585 /* Get all tracked instances registered by the description. Items are filtered
586    by ORIGIN type, LENGTH is return value where we register the number of
587    elements in the list. If we want to process custom order, CMP comparator
588    can be provided.  */
589 
590 template <class T>
591 inline
592 typename mem_alloc_description<T>::mem_list_t *
593 mem_alloc_description<T>::get_list (mem_alloc_origin origin, unsigned *length,
594 				    int (*cmp) (const void *first,
595 						const void *second))
596 {
597   /* vec data structure is not used because all vectors generate memory
598      allocation info a it would create a cycle.  */
599   size_t element_size = sizeof (mem_list_t);
600   mem_list_t *list = XCNEWVEC (mem_list_t, m_map->elements ());
601   unsigned i = 0;
602 
603   for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end ();
604        ++it)
605     if ((*it).first->m_origin == origin)
606       list[i++] = std::pair<mem_location*, T*> (*it);
607 
608   qsort (list, i, element_size, cmp == NULL ? T::compare : cmp);
609   *length = i;
610 
611   return list;
612 }
613 
614 /* Get sum value for ORIGIN type of allocation for the descriptor.  */
615 
616 template <class T>
617 inline T
618 mem_alloc_description<T>::get_sum (mem_alloc_origin origin)
619 {
620   unsigned length;
621   mem_list_t *list = get_list (origin, &length);
622   T sum;
623 
624   for (unsigned i = 0; i < length; i++)
625     sum = sum + *list[i].second;
626 
627   XDELETEVEC (list);
628 
629   return sum;
630 }
631 
632 /* Dump all tracked instances of type ORIGIN. If we want to process custom
633    order, CMP comparator can be provided.  */
634 
635 template <class T>
636 inline void
637 mem_alloc_description<T>::dump (mem_alloc_origin origin,
638 				int (*cmp) (const void *first,
639 					    const void *second))
640 {
641   unsigned length;
642 
643   fprintf (stderr, "\n");
644 
645   mem_list_t *list = get_list (origin, &length, cmp);
646   T total = get_sum (origin);
647 
648   T::print_dash_line ();
649   T::dump_header (mem_location::get_origin_name (origin));
650   T::print_dash_line ();
651   for (int i = length - 1; i >= 0; i--)
652     list[i].second->dump (list[i].first, total);
653   T::print_dash_line ();
654 
655   T::dump_header (mem_location::get_origin_name (origin));
656   T::print_dash_line ();
657   total.dump_footer ();
658   T::print_dash_line ();
659 
660   XDELETEVEC (list);
661 
662   fprintf (stderr, "\n");
663 }
664 
665 #endif // GCC_MEM_STATS_H
666