1 /* A memory statistics tracking infrastructure.
2 Copyright (C) 2015-2017 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
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
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
>,
30 #define LOCATION_LINE_EXTRA_SPACE 30
31 #define LOCATION_LINE_WIDTH 48
33 /* Memory allocation location. */
36 /* Default constructor. */
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. */
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
63 hash
.add_ptr (m_filename
);
64 hash
.add_ptr (m_function
);
65 hash
.add_int (m_line
);
70 /* Return true if the memory location is equal to OTHER. */
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. */
80 get_trimmed_filename ()
82 const char *s1
= m_filename
;
85 while ((s2
= strstr (s1
, "gcc/")))
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 (),
101 s
[MIN (LOCATION_LINE_WIDTH
, l
- 1)] = '\0';
106 /* Return display name associated to ORIGIN type. */
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. */
120 mem_alloc_origin m_origin
;
121 /* Flag if used by GGC allocation. */
125 /* Memory usage register to a memory location. */
128 /* Default constructor. */
129 mem_usage (): m_allocated (0), m_times (0), m_peak (0), m_instances (1) {}
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. */
138 register_overhead (size_t size
)
143 if (m_peak
< m_allocated
)
144 m_peak
= m_allocated
;
147 /* Release overhead of SIZE bytes. */
149 release_overhead (size_t size
)
151 gcc_assert (size
<= m_allocated
);
156 /* Sum the usage with SECOND 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 /* Comparison operator. */
168 operator< (const mem_usage
&second
) const
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
);
175 /* Compare wrapper used by qsort method. */
177 compare (const void *first
, const void *second
)
179 typedef std::pair
<mem_location
*, mem_usage
*> mem_pair_t
;
181 const mem_pair_t f
= *(const mem_pair_t
*)first
;
182 const mem_pair_t s
= *(const mem_pair_t
*)second
;
184 return (*f
.second
) < (*s
.second
);
187 /* Dump usage coupled to LOC location, where TOTAL is sum of all rows. */
189 dump (mem_location
*loc
, mem_usage
&total
) const
191 char *location_string
= loc
->to_string ();
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");
200 free (location_string
);
208 fprintf (stderr
, "%s%54" PRIu64
"%27" PRIu64
"\n", "Total",
209 (uint64_t)m_allocated
, (uint64_t)m_times
);
213 /* Return fraction of NOMINATOR and DENOMINATOR in percent. */
215 get_percent (size_t nominator
, size_t denominator
)
217 return denominator
== 0 ? 0.0f
: nominator
* 100.0 / denominator
;
220 /* Print line made of dashes. */
222 print_dash_line (size_t count
= 140)
226 fputc ('\n', stderr
);
229 /* Dump header with NAME. */
231 dump_header (const char *name
)
233 fprintf (stderr
, "%-48s %11s%16s%10s%17s\n", name
, "Leak", "Peak",
238 /* Current number of allocated bytes. */
240 /* Number of allocations. */
242 /* Peak allocation in bytes. */
244 /* Number of container instances. */
248 /* Memory usage pair that connectes memory usage and number
249 of allocated bytes. */
251 struct mem_usage_pair
253 mem_usage_pair (T
*usage_
, size_t allocated_
): usage (usage_
),
254 allocated (allocated_
) {}
260 /* Memory allocation description. */
262 class mem_alloc_description
265 struct mem_location_hash
: nofree_ptr_hash
<mem_location
>
270 inchash::hash hstate
;
272 hstate
.add_ptr ((const void *)l
->m_filename
);
273 hstate
.add_ptr (l
->m_function
);
274 hstate
.add_int (l
->m_line
);
276 return hstate
.end ();
280 equal (value_type l1
, value_type l2
)
282 return l1
->m_filename
== l2
->m_filename
283 && l1
->m_function
== l2
->m_function
284 && l1
->m_line
== l2
->m_line
;
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
;
294 /* Default contructor. */
295 mem_alloc_description ();
297 /* Default destructor. */
298 ~mem_alloc_description ();
300 /* Returns true if instance PTR is registered by the memory description. */
302 contains_descriptor_for_instance (const void *ptr
);
304 /* Return descriptor for instance PTR. */
306 get_descriptor_for_instance (const void *ptr
);
308 /* Register memory allocation descriptor for container PTR which is
309 described by a memory LOCATION. */
311 register_descriptor (const void *ptr
, mem_location
*location
);
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
318 register_descriptor (const void *ptr
, mem_alloc_origin origin
,
319 bool ggc
, const char *name
, int line
,
320 const char *function
);
322 /* Register instance overhead identified by PTR pointer. Allocation takes
325 register_instance_overhead (size_t size
, const void *ptr
);
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. */
331 register_object_overhead (T
*usage
, size_t size
, const void *ptr
);
333 /* Release PTR pointer of SIZE bytes. If REMOVE_FROM_MAP is set to true,
334 remove the instance from reverse map. */
336 release_instance_overhead (void *ptr
, size_t size
,
337 bool remove_from_map
= false);
339 /* Release intance object identified by PTR pointer. */
341 release_object_overhead (void *ptr
);
343 /* Get sum value for ORIGIN type of allocation for the descriptor. */
345 get_sum (mem_alloc_origin origin
);
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. */
352 get_list (mem_alloc_origin origin
, unsigned *length
,
353 int (*cmp
) (const void *first
, const void *second
) = NULL
);
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
);
360 /* Reverse object map used for every object allocation mapping. */
361 reverse_object_map_t
*m_reverse_object_map
;
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
);
369 /* Allocation location coupled to the description. */
370 mem_location m_location
;
372 /* Location to usage mapping. */
375 /* Reverse pointer to usage mapping. */
376 reverse_mem_map_t
*m_reverse_map
;
380 /* Returns true if instance PTR is registered by the memory description. */
384 mem_alloc_description
<T
>::contains_descriptor_for_instance (const void *ptr
)
386 return m_reverse_map
->get (ptr
);
389 /* Return descriptor for instance PTR. */
393 mem_alloc_description
<T
>::get_descriptor_for_instance (const void *ptr
)
395 return m_reverse_map
->get (ptr
) ? (*m_reverse_map
->get (ptr
)).usage
: NULL
;
399 /* Register memory allocation descriptor for container PTR which is
400 described by a memory LOCATION. */
403 mem_alloc_description
<T
>::register_descriptor (const void *ptr
,
404 mem_location
*location
)
408 T
**slot
= m_map
->get (location
);
413 usage
->m_instances
++;
418 m_map
->put (location
, usage
);
421 if (!m_reverse_map
->get (ptr
))
422 m_reverse_map
->put (ptr
, mem_usage_pair
<T
> (usage
, 0));
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
434 mem_alloc_description
<T
>::register_descriptor (const void *ptr
,
435 mem_alloc_origin origin
,
437 const char *filename
,
439 const char *function
)
441 mem_location
*l
= new mem_location (origin
, ggc
, filename
, line
, function
);
442 return register_descriptor (ptr
, l
);
445 /* Register instance overhead identified by PTR pointer. Allocation takes
450 mem_alloc_description
<T
>::register_instance_overhead (size_t size
,
453 mem_usage_pair
<T
> *slot
= m_reverse_map
->get (ptr
);
456 /* Due to PCH, it can really happen. */
460 T
*usage
= (*slot
).usage
;
461 usage
->register_overhead (size
);
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. */
472 mem_alloc_description
<T
>::register_object_overhead (T
*usage
, size_t size
,
475 /* In case of GGC, it is possible to have already occupied the memory
477 m_reverse_object_map
->put (ptr
, std::pair
<T
*, size_t> (usage
, size
));
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. */
485 mem_alloc_description
<T
>::register_overhead (size_t size
,
486 mem_alloc_origin origin
,
487 const char *filename
,
489 const char *function
,
492 T
*usage
= register_descriptor (ptr
, origin
, filename
, line
, function
);
493 usage
->register_overhead (size
);
498 /* Release PTR pointer of SIZE bytes. */
502 mem_alloc_description
<T
>::release_instance_overhead (void *ptr
, size_t size
,
503 bool remove_from_map
)
505 mem_usage_pair
<T
> *slot
= m_reverse_map
->get (ptr
);
509 /* Due to PCH, it can really happen. */
513 mem_usage_pair
<T
> usage_pair
= *slot
;
514 usage_pair
.usage
->release_overhead (size
);
517 m_reverse_map
->remove (ptr
);
520 /* Release intance object identified by PTR pointer. */
524 mem_alloc_description
<T
>::release_object_overhead (void *ptr
)
526 std::pair
<T
*, size_t> *entry
= m_reverse_object_map
->get (ptr
);
529 entry
->first
->release_overhead (entry
->second
);
530 m_reverse_object_map
->remove (ptr
);
534 /* Default contructor. */
538 mem_alloc_description
<T
>::mem_alloc_description ()
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);
545 /* Default destructor. */
549 mem_alloc_description
<T
>::~mem_alloc_description ()
551 for (typename
mem_map_t::iterator it
= m_map
->begin (); it
!= m_map
->end ();
559 delete m_reverse_map
;
560 delete m_reverse_object_map
;
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
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
))
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 ());
580 for (typename
mem_map_t::iterator it
= m_map
->begin (); it
!= m_map
->end ();
582 if ((*it
).first
->m_origin
== origin
)
583 list
[i
++] = std::pair
<mem_location
*, T
*> (*it
);
585 qsort (list
, i
, element_size
, cmp
== NULL
? T::compare
: cmp
);
591 /* Get sum value for ORIGIN type of allocation for the descriptor. */
595 mem_alloc_description
<T
>::get_sum (mem_alloc_origin origin
)
598 mem_list_t
*list
= get_list (origin
, &length
);
601 for (unsigned i
= 0; i
< length
; i
++)
602 sum
= sum
+ *list
[i
].second
;
609 /* Dump all tracked instances of type ORIGIN. If we want to process custom
610 order, CMP comparator can be provided. */
614 mem_alloc_description
<T
>::dump (mem_alloc_origin origin
,
615 int (*cmp
) (const void *first
,
620 fprintf (stderr
, "\n");
622 mem_list_t
*list
= get_list (origin
, &length
, cmp
);
623 T total
= get_sum (origin
);
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
);
629 total
.dump_footer ();
633 fprintf (stderr
, "\n");
636 #endif // GCC_MEM_STATS_H