re PR fortran/87318 (gfortran.dg/dtio_1.f90 is invalid)
[official-gcc.git] / gcc / mem-stats.h
blob741c07301d993dc18aa593925666da2e43c2f0e4
1 /* A memory statistics tracking infrastructure.
2 Copyright (C) 2015-2018 Free Software Foundation, Inc.
3 Contributed by Martin Liska <mliska@suse.cz>
5 This file is part of GCC.
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.
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.
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/>. */
21 #ifndef GCC_MEM_STATS_H
22 #define GCC_MEM_STATS_H
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;
30 #define LOCATION_LINE_EXTRA_SPACE 30
31 #define LOCATION_LINE_WIDTH 48
33 /* Memory allocation location. */
34 struct mem_location
36 /* Default constructor. */
37 inline
38 mem_location () {}
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) {}
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) {}
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 ()
61 inchash::hash hash;
63 hash.add_ptr (m_filename);
64 hash.add_ptr (m_function);
65 hash.add_int (m_line);
67 return hash.end ();
70 /* Return true if the memory location is equal to OTHER. */
71 int
72 equal (mem_location &other)
74 return m_filename == other.m_filename && m_function == other.m_function
75 && m_line == other.m_line;
78 /* Return trimmed filename for the location. */
79 inline const char *
80 get_trimmed_filename ()
82 const char *s1 = m_filename;
83 const char *s2;
85 while ((s2 = strstr (s1, "gcc/")))
86 s1 = s2 + 4;
88 return s1;
91 inline char *
92 to_string ()
94 unsigned l = strlen (get_trimmed_filename ()) + strlen (m_function)
95 + LOCATION_LINE_EXTRA_SPACE;
97 char *s = XNEWVEC (char, l);
98 sprintf (s, "%s:%i (%s)", get_trimmed_filename (),
99 m_line, m_function);
101 s[MIN (LOCATION_LINE_WIDTH, l - 1)] = '\0';
103 return s;
106 /* Return display name associated to ORIGIN type. */
107 static const char *
108 get_origin_name (mem_alloc_origin origin)
110 return mem_alloc_origin_names[(unsigned) origin];
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;
125 /* Memory usage register to a memory location. */
126 struct mem_usage
128 /* Default constructor. */
129 mem_usage (): m_allocated (0), m_times (0), m_peak (0), m_instances (1) {}
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) {}
136 /* Register overhead of SIZE bytes. */
137 inline void
138 register_overhead (size_t size)
140 m_allocated += size;
141 m_times++;
143 if (m_peak < m_allocated)
144 m_peak = m_allocated;
147 /* Release overhead of SIZE bytes. */
148 inline void
149 release_overhead (size_t size)
151 gcc_assert (size <= m_allocated);
153 m_allocated -= size;
156 /* Sum the usage with SECOND usage. */
157 mem_usage
158 operator+ (const mem_usage &second)
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);
166 /* Equality operator. */
167 inline bool
168 operator== (const mem_usage &second) const
170 return (m_allocated == second.m_allocated
171 && m_peak == second.m_peak
172 && m_allocated == second.m_allocated);
175 /* Comparison operator. */
176 inline bool
177 operator< (const mem_usage &second) const
179 if (*this == second)
180 return false;
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);
187 /* Compare wrapper used by qsort method. */
188 static int
189 compare (const void *first, const void *second)
191 typedef std::pair<mem_location *, mem_usage *> mem_pair_t;
193 const mem_pair_t f = *(const mem_pair_t *)first;
194 const mem_pair_t s = *(const mem_pair_t *)second;
196 if (*f.second == *s.second)
197 return 0;
199 return *f.second < *s.second ? 1 : -1;
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
206 char *location_string = loc->to_string ();
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");
215 free (location_string);
218 /* Dump footer. */
219 inline void
220 dump_footer () const
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 ();
228 /* Return fraction of NOMINATOR and DENOMINATOR in percent. */
229 static inline float
230 get_percent (size_t nominator, size_t denominator)
232 return denominator == 0 ? 0.0f : nominator * 100.0 / denominator;
235 /* Print line made of dashes. */
236 static inline void
237 print_dash_line (size_t count = 140)
239 while (count--)
240 fputc ('-', stderr);
241 fputc ('\n', stderr);
244 /* Dump header with NAME. */
245 static inline void
246 dump_header (const char *name)
248 fprintf (stderr, "%-48s %11s%16s%10s%17s\n", name, "Leak", "Peak",
249 "Times", "Type");
250 print_dash_line ();
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;
263 /* Memory usage pair that connectes memory usage and number
264 of allocated bytes. */
265 template <class T>
266 struct mem_usage_pair
268 mem_usage_pair (T *usage_, size_t allocated_): usage (usage_),
269 allocated (allocated_) {}
271 T *usage;
272 size_t allocated;
275 /* Memory allocation description. */
276 template <class T>
277 class mem_alloc_description
279 public:
280 struct mem_location_hash : nofree_ptr_hash <mem_location>
282 static hashval_t
283 hash (value_type l)
285 inchash::hash hstate;
287 hstate.add_ptr ((const void *)l->m_filename);
288 hstate.add_ptr (l->m_function);
289 hstate.add_int (l->m_line);
291 return hstate.end ();
294 static bool
295 equal (value_type l1, value_type l2)
297 return l1->m_filename == l2->m_filename
298 && l1->m_function == l2->m_function
299 && l1->m_line == l2->m_line;
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;
309 /* Default contructor. */
310 mem_alloc_description ();
312 /* Default destructor. */
313 ~mem_alloc_description ();
315 /* Returns true if instance PTR is registered by the memory description. */
316 bool
317 contains_descriptor_for_instance (const void *ptr);
319 /* Return descriptor for instance PTR. */
321 get_descriptor_for_instance (const void *ptr);
323 /* Register memory allocation descriptor for container PTR which is
324 described by a memory LOCATION. */
326 register_descriptor (const void *ptr, mem_location *location);
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. */
333 register_descriptor (const void *ptr, mem_alloc_origin origin,
334 bool ggc, const char *name, int line,
335 const char *function);
337 /* Register instance overhead identified by PTR pointer. Allocation takes
338 SIZE bytes. */
340 register_instance_overhead (size_t size, const void *ptr);
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);
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);
354 /* Release intance object identified by PTR pointer. */
355 void
356 release_object_overhead (void *ptr);
358 /* Get sum value for ORIGIN type of allocation for the descriptor. */
360 get_sum (mem_alloc_origin origin);
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);
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);
375 /* Reverse object map used for every object allocation mapping. */
376 reverse_object_map_t *m_reverse_object_map;
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);
384 /* Allocation location coupled to the description. */
385 mem_location m_location;
387 /* Location to usage mapping. */
388 mem_map_t *m_map;
390 /* Reverse pointer to usage mapping. */
391 reverse_mem_map_t *m_reverse_map;
395 /* Returns true if instance PTR is registered by the memory description. */
397 template <class T>
398 inline bool
399 mem_alloc_description<T>::contains_descriptor_for_instance (const void *ptr)
401 return m_reverse_map->get (ptr);
404 /* Return descriptor for instance PTR. */
406 template <class T>
407 inline T*
408 mem_alloc_description<T>::get_descriptor_for_instance (const void *ptr)
410 return m_reverse_map->get (ptr) ? (*m_reverse_map->get (ptr)).usage : NULL;
414 /* Register memory allocation descriptor for container PTR which is
415 described by a memory LOCATION. */
416 template <class T>
417 inline T*
418 mem_alloc_description<T>::register_descriptor (const void *ptr,
419 mem_location *location)
421 T *usage = NULL;
423 T **slot = m_map->get (location);
424 if (slot)
426 delete location;
427 usage = *slot;
428 usage->m_instances++;
430 else
432 usage = new T ();
433 m_map->put (location, usage);
436 if (!m_reverse_map->get (ptr))
437 m_reverse_map->put (ptr, mem_usage_pair<T> (usage, 0));
439 return usage;
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. */
447 template <class T>
448 inline T*
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)
456 mem_location *l = new mem_location (origin, ggc, filename, line, function);
457 return register_descriptor (ptr, l);
460 /* Register instance overhead identified by PTR pointer. Allocation takes
461 SIZE bytes. */
463 template <class T>
464 inline T*
465 mem_alloc_description<T>::register_instance_overhead (size_t size,
466 const void *ptr)
468 mem_usage_pair <T> *slot = m_reverse_map->get (ptr);
469 if (!slot)
471 /* Due to PCH, it can really happen. */
472 return NULL;
475 T *usage = (*slot).usage;
476 usage->register_overhead (size);
478 return usage;
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. */
485 template <class T>
486 void
487 mem_alloc_description<T>::register_object_overhead (T *usage, size_t size,
488 const void *ptr)
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));
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. */
498 template <class T>
499 inline T*
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)
507 T *usage = register_descriptor (ptr, origin, filename, line, function);
508 usage->register_overhead (size);
510 return usage;
513 /* Release PTR pointer of SIZE bytes. */
515 template <class T>
516 inline void
517 mem_alloc_description<T>::release_instance_overhead (void *ptr, size_t size,
518 bool remove_from_map)
520 mem_usage_pair<T> *slot = m_reverse_map->get (ptr);
522 if (!slot)
524 /* Due to PCH, it can really happen. */
525 return;
528 mem_usage_pair<T> usage_pair = *slot;
529 usage_pair.usage->release_overhead (size);
531 if (remove_from_map)
532 m_reverse_map->remove (ptr);
535 /* Release intance object identified by PTR pointer. */
537 template <class T>
538 inline void
539 mem_alloc_description<T>::release_object_overhead (void *ptr)
541 std::pair <T *, size_t> *entry = m_reverse_object_map->get (ptr);
542 if (entry)
544 entry->first->release_overhead (entry->second);
545 m_reverse_object_map->remove (ptr);
549 /* Default contructor. */
551 template <class T>
552 inline
553 mem_alloc_description<T>::mem_alloc_description ()
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);
560 /* Default destructor. */
562 template <class T>
563 inline
564 mem_alloc_description<T>::~mem_alloc_description ()
566 for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end ();
567 ++it)
569 delete (*it).first;
570 delete (*it).second;
573 delete m_map;
574 delete m_reverse_map;
575 delete m_reverse_object_map;
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. */
583 template <class T>
584 inline
585 typename mem_alloc_description<T>::mem_list_t *
586 mem_alloc_description<T>::get_list (mem_alloc_origin origin, unsigned *length,
587 int (*cmp) (const void *first, const void *second))
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;
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);
600 qsort (list, i, element_size, cmp == NULL ? T::compare : cmp);
601 *length = i;
603 return list;
606 /* Get sum value for ORIGIN type of allocation for the descriptor. */
608 template <class T>
609 inline T
610 mem_alloc_description<T>::get_sum (mem_alloc_origin origin)
612 unsigned length;
613 mem_list_t *list = get_list (origin, &length);
614 T sum;
616 for (unsigned i = 0; i < length; i++)
617 sum = sum + *list[i].second;
619 XDELETEVEC (list);
621 return sum;
624 /* Dump all tracked instances of type ORIGIN. If we want to process custom
625 order, CMP comparator can be provided. */
627 template <class T>
628 inline void
629 mem_alloc_description<T>::dump (mem_alloc_origin origin,
630 int (*cmp) (const void *first,
631 const void *second))
633 unsigned length;
635 fprintf (stderr, "\n");
637 mem_list_t *list = get_list (origin, &length, cmp);
638 T total = get_sum (origin);
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);
644 total.dump_footer ();
646 XDELETEVEC (list);
648 fprintf (stderr, "\n");
651 #endif // GCC_MEM_STATS_H