Backport from GCC mainline.
[platform/upstream/linaro-gcc.git] / gcc / mem-stats.h
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
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   /* 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
177   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
189   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
205   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
215   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
231   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 {
253   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
268     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
280     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
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*
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*
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*
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*
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
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*
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
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
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
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
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 *
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
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
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