1/* A memory statistics tracking infrastructure.
2 Copyright (C) 2015-2017 Free Software Foundation, Inc.
3 Contributed by Martin Liska <mliska@suse.cz>
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along 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. */
25template<typename Key, typename Value,
26 typename Traits = simple_hashmap_traits<default_hash_traits<Key>,
27 Value> >
28class hash_map;
29
30#define LOCATION_LINE_EXTRA_SPACE 30
31#define LOCATION_LINE_WIDTH 48
32
33/* Memory allocation location. */
34struct 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. */
126struct 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. */
250template <class T>
251struct 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. */
261template <class T>
262class mem_alloc_description
263{
264public:
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
363private:
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
382template <class T>
383inline bool
384mem_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
391template <class T>
392inline T*
393mem_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. */
401template <class T>
402inline T*
403mem_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
432template <class T>
433inline T*
434mem_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
448template <class T>
449inline T*
450mem_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
470template <class T>
471void
472mem_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
483template <class T>
484inline T*
485mem_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
500template <class T>
501inline void
502mem_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
522template <class T>
523inline void
524mem_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
536template <class T>
537inline
538mem_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
547template <class T>
548inline
549mem_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
568template <class T>
569inline
570typename mem_alloc_description<T>::mem_list_t *
571mem_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
593template <class T>
594inline T
595mem_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
612template <class T>
613inline void
614mem_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