1 /* A memory statistics tracking infrastructure.
2 Copyright (C) 2015-2024 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. */
37 /* Default constructor. */
43 mem_location (mem_alloc_origin origin
, bool ggc
,
44 const char *filename
= NULL
, int line
= 0,
45 const char *function
= NULL
):
46 m_filename (filename
), m_function (function
), m_line (line
), m_origin
47 (origin
), m_ggc (ggc
) {}
49 /* Copy constructor. */
51 mem_location (mem_location
&other
): m_filename (other
.m_filename
),
52 m_function (other
.m_function
), m_line (other
.m_line
),
53 m_origin (other
.m_origin
), m_ggc (other
.m_ggc
) {}
55 /* Compute hash value based on file name, function name and line in
56 source code. As there is just a single pointer registered for every
57 constant that points to e.g. the same file name, we can use hash
64 hash
.add_ptr (m_filename
);
65 hash
.add_ptr (m_function
);
66 hash
.add_int (m_line
);
71 /* Return true if the memory location is equal to OTHER. */
73 equal (const mem_location
&other
)
75 return m_filename
== other
.m_filename
&& m_function
== other
.m_function
76 && m_line
== other
.m_line
;
79 /* Return trimmed filename for the location. */
81 get_trimmed_filename ()
83 const char *s1
= m_filename
;
86 while ((s2
= strstr (s1
, "gcc/")))
95 unsigned l
= strlen (get_trimmed_filename ()) + strlen (m_function
)
96 + LOCATION_LINE_EXTRA_SPACE
;
98 char *s
= XNEWVEC (char, l
);
99 sprintf (s
, "%s:%i (%s)", get_trimmed_filename (),
102 s
[MIN (LOCATION_LINE_WIDTH
, l
- 1)] = '\0';
107 /* Return display name associated to ORIGIN type. */
109 get_origin_name (mem_alloc_origin origin
)
111 return mem_alloc_origin_names
[(unsigned) origin
];
114 /* File name of source code. */
115 const char *m_filename
;
116 /* Funcation name. */
117 const char *m_function
;
118 /* Line number in source code. */
121 mem_alloc_origin m_origin
;
122 /* Flag if used by GGC allocation. */
126 /* Memory usage register to a memory location. */
130 /* Default constructor. */
131 mem_usage (): m_allocated (0), m_times (0), m_peak (0), m_instances (1) {}
134 mem_usage (size_t allocated
, size_t times
, size_t peak
, size_t instances
= 0):
135 m_allocated (allocated
), m_times (times
), m_peak (peak
),
136 m_instances (instances
) {}
138 /* Register overhead of SIZE bytes. */
140 register_overhead (size_t size
)
145 if (m_peak
< m_allocated
)
146 m_peak
= m_allocated
;
149 /* Release overhead of SIZE bytes. */
151 release_overhead (size_t size
)
153 gcc_assert (size
<= m_allocated
);
158 /* Sum the usage with SECOND usage. */
160 operator+ (const mem_usage
&second
)
162 return mem_usage (m_allocated
+ second
.m_allocated
,
163 m_times
+ second
.m_times
,
164 m_peak
+ second
.m_peak
,
165 m_instances
+ second
.m_instances
);
168 /* Equality operator. */
170 operator== (const mem_usage
&second
) const
172 return (m_allocated
== second
.m_allocated
173 && m_peak
== second
.m_peak
174 && m_times
== second
.m_times
);
177 /* Comparison operator. */
179 operator< (const mem_usage
&second
) const
184 return (m_allocated
== second
.m_allocated
?
185 (m_peak
== second
.m_peak
? m_times
< second
.m_times
186 : m_peak
< second
.m_peak
) : m_allocated
< second
.m_allocated
);
189 /* Compare wrapper used by qsort method. */
191 compare (const void *first
, const void *second
)
193 typedef std::pair
<mem_location
*, mem_usage
*> mem_pair_t
;
195 const mem_pair_t f
= *(const mem_pair_t
*)first
;
196 const mem_pair_t s
= *(const mem_pair_t
*)second
;
198 if (*f
.second
== *s
.second
)
201 return *f
.second
< *s
.second
? 1 : -1;
204 /* Dump usage coupled to LOC location, where TOTAL is sum of all rows. */
206 dump (mem_location
*loc
, const mem_usage
&total
) const
208 char *location_string
= loc
->to_string ();
210 fprintf (stderr
, "%-48s " PRsa (9) ":%5.1f%%"
211 PRsa (9) PRsa (9) ":%5.1f%%%10s\n",
212 location_string
, SIZE_AMOUNT (m_allocated
),
213 get_percent (m_allocated
, total
.m_allocated
),
214 SIZE_AMOUNT (m_peak
), SIZE_AMOUNT (m_times
),
215 get_percent (m_times
, total
.m_times
), loc
->m_ggc
? "ggc" : "heap");
217 free (location_string
);
224 fprintf (stderr
, "%s" PRsa (53) PRsa (26) "\n", "Total",
225 SIZE_AMOUNT (m_allocated
), SIZE_AMOUNT (m_times
));
228 /* Return fraction of NOMINATOR and DENOMINATOR in percent. */
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. */
237 print_dash_line (size_t count
= 140)
241 fputc ('\n', stderr
);
244 /* Dump header with NAME. */
246 dump_header (const char *name
)
248 fprintf (stderr
, "%-48s %11s%16s%10s%17s\n", name
, "Leak", "Peak",
252 /* Current number of allocated bytes. */
254 /* Number of allocations. */
256 /* Peak allocation in bytes. */
258 /* Number of container instances. */
262 /* Memory usage pair that connectes memory usage and number
263 of allocated bytes. */
268 mem_usage_pair (T
*usage_
, size_t allocated_
): usage (usage_
),
269 allocated (allocated_
) {}
275 /* Memory allocation description. */
277 class mem_alloc_description
280 struct mem_location_hash
: nofree_ptr_hash
<mem_location
>
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 ();
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 contains_descriptor_for_instance (const void *ptr
);
318 /* Return descriptor for instance PTR. */
319 T
*get_descriptor_for_instance (const void *ptr
);
321 /* Register memory allocation descriptor for container PTR which is
322 described by a memory LOCATION. */
323 T
*register_descriptor (const void *ptr
, mem_location
*location
);
325 /* Register memory allocation descriptor for container PTR. ORIGIN identifies
326 type of container and GGC identifes if the allocation is handled in GGC
327 memory. Each location is identified by file NAME, LINE in source code and
329 T
*register_descriptor (const void *ptr
, mem_alloc_origin origin
,
330 bool ggc
, const char *name
, int line
,
331 const char *function
);
333 /* Register instance overhead identified by PTR pointer. Allocation takes
335 T
*register_instance_overhead (size_t size
, const void *ptr
);
337 /* For containers (and GGC) where we want to track every instance object,
338 we register allocation of SIZE bytes, identified by PTR pointer, belonging
339 to USAGE descriptor. */
340 void register_object_overhead (T
*usage
, size_t size
, const void *ptr
);
342 /* Release PTR pointer of SIZE bytes. If REMOVE_FROM_MAP is set to true,
343 remove the instance from reverse map. Return memory usage that belongs
344 to this memory description. */
345 T
*release_instance_overhead (void *ptr
, size_t size
,
346 bool remove_from_map
= false);
348 /* Release instance object identified by PTR pointer. */
349 void release_object_overhead (void *ptr
);
351 /* Unregister a memory allocation descriptor registered with
352 register_descriptor (remove from reverse map), unless it is
353 unregistered through release_instance_overhead with
354 REMOVE_FROM_MAP = true. */
355 void unregister_descriptor (void *ptr
);
357 /* Get sum value for ORIGIN type of allocation for the descriptor. */
358 T
get_sum (mem_alloc_origin origin
);
360 /* Get all tracked instances registered by the description. Items
361 are filtered by ORIGIN type, LENGTH is return value where we register
362 the number of elements in the list. If we want to process custom order,
363 CMP comparator can be provided. */
364 mem_list_t
*get_list (mem_alloc_origin origin
, unsigned *length
);
366 /* Dump all tracked instances of type ORIGIN. If we want to process custom
367 order, CMP comparator can be provided. */
368 void dump (mem_alloc_origin origin
);
370 /* Reverse object map used for every object allocation mapping. */
371 reverse_object_map_t
*m_reverse_object_map
;
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
);
379 /* Allocation location coupled to the description. */
380 mem_location m_location
;
382 /* Location to usage mapping. */
385 /* Reverse pointer to usage mapping. */
386 reverse_mem_map_t
*m_reverse_map
;
389 /* Returns true if instance PTR is registered by the memory description. */
393 mem_alloc_description
<T
>::contains_descriptor_for_instance (const void *ptr
)
395 return m_reverse_map
->get (ptr
);
398 /* Return descriptor for instance PTR. */
402 mem_alloc_description
<T
>::get_descriptor_for_instance (const void *ptr
)
404 return m_reverse_map
->get (ptr
) ? (*m_reverse_map
->get (ptr
)).usage
: NULL
;
407 /* Register memory allocation descriptor for container PTR which is
408 described by a memory LOCATION. */
412 mem_alloc_description
<T
>::register_descriptor (const void *ptr
,
413 mem_location
*location
)
417 T
**slot
= m_map
->get (location
);
422 usage
->m_instances
++;
427 m_map
->put (location
, usage
);
430 if (!m_reverse_map
->get (ptr
))
431 m_reverse_map
->put (ptr
, mem_usage_pair
<T
> (usage
, 0));
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
443 mem_alloc_description
<T
>::register_descriptor (const void *ptr
,
444 mem_alloc_origin origin
,
446 const char *filename
,
448 const char *function
)
450 mem_location
*l
= new mem_location (origin
, ggc
, filename
, line
, function
);
451 return register_descriptor (ptr
, l
);
454 /* Register instance overhead identified by PTR pointer. Allocation takes
459 mem_alloc_description
<T
>::register_instance_overhead (size_t size
,
462 mem_usage_pair
<T
> *slot
= m_reverse_map
->get (ptr
);
465 /* Due to PCH, it can really happen. */
469 T
*usage
= (*slot
).usage
;
470 usage
->register_overhead (size
);
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. */
481 mem_alloc_description
<T
>::register_object_overhead (T
*usage
, size_t size
,
484 /* In case of GGC, it is possible to have already occupied the memory
486 m_reverse_object_map
->put (ptr
, std::pair
<T
*, size_t> (usage
, size
));
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. */
494 mem_alloc_description
<T
>::register_overhead (size_t size
,
495 mem_alloc_origin origin
,
496 const char *filename
,
498 const char *function
,
501 T
*usage
= register_descriptor (ptr
, origin
, filename
, line
, function
);
502 usage
->register_overhead (size
);
507 /* Release PTR pointer of SIZE bytes. */
511 mem_alloc_description
<T
>::release_instance_overhead (void *ptr
, size_t size
,
512 bool remove_from_map
)
514 mem_usage_pair
<T
> *slot
= m_reverse_map
->get (ptr
);
518 /* Due to PCH, it can really happen. */
522 T
*usage
= (*slot
).usage
;
523 usage
->release_overhead (size
);
526 m_reverse_map
->remove (ptr
);
531 /* Release instance object identified by PTR pointer. */
535 mem_alloc_description
<T
>::release_object_overhead (void *ptr
)
537 std::pair
<T
*, size_t> *entry
= m_reverse_object_map
->get (ptr
);
538 entry
->first
->release_overhead (entry
->second
);
539 m_reverse_object_map
->remove (ptr
);
542 /* Unregister a memory allocation descriptor registered with
543 register_descriptor (remove from reverse map), unless it is
544 unregistered through release_instance_overhead with
545 REMOVE_FROM_MAP = true. */
548 mem_alloc_description
<T
>::unregister_descriptor (void *ptr
)
550 m_reverse_map
->remove (ptr
);
553 /* Default contructor. */
557 mem_alloc_description
<T
>::mem_alloc_description ()
559 m_map
= new mem_map_t (13, false, false, false);
560 m_reverse_map
= new reverse_mem_map_t (13, false, false, false);
561 m_reverse_object_map
= new reverse_object_map_t (13, false, false, false);
564 /* Default destructor. */
568 mem_alloc_description
<T
>::~mem_alloc_description ()
570 for (typename
mem_map_t::iterator it
= m_map
->begin (); it
!= m_map
->end ();
578 delete m_reverse_map
;
579 delete m_reverse_object_map
;
582 /* Get all tracked instances registered by the description. Items are filtered
583 by ORIGIN type, LENGTH is return value where we register the number of
584 elements in the list. If we want to process custom order, CMP comparator
589 typename mem_alloc_description
<T
>::mem_list_t
*
590 mem_alloc_description
<T
>::get_list (mem_alloc_origin origin
, unsigned *length
)
592 /* vec data structure is not used because all vectors generate memory
593 allocation info a it would create a cycle. */
594 size_t element_size
= sizeof (mem_list_t
);
595 mem_list_t
*list
= XCNEWVEC (mem_list_t
, m_map
->elements ());
598 for (typename
mem_map_t::iterator it
= m_map
->begin (); it
!= m_map
->end ();
600 if ((*it
).first
->m_origin
== origin
)
601 list
[i
++] = std::pair
<mem_location
*, T
*> (*it
);
603 qsort (list
, i
, element_size
, T::compare
);
609 /* Get sum value for ORIGIN type of allocation for the descriptor. */
613 mem_alloc_description
<T
>::get_sum (mem_alloc_origin origin
)
616 mem_list_t
*list
= get_list (origin
, &length
);
619 for (unsigned i
= 0; i
< length
; i
++)
620 sum
= sum
+ *list
[i
].second
;
627 /* Dump all tracked instances of type ORIGIN. If we want to process custom
628 order, CMP comparator can be provided. */
632 mem_alloc_description
<T
>::dump (mem_alloc_origin origin
)
636 fprintf (stderr
, "\n");
638 mem_list_t
*list
= get_list (origin
, &length
);
639 T total
= get_sum (origin
);
641 T::print_dash_line ();
642 T::dump_header (mem_location::get_origin_name (origin
));
643 T::print_dash_line ();
644 for (int i
= length
- 1; i
>= 0; i
--)
645 list
[i
].second
->dump (list
[i
].first
, total
);
646 T::print_dash_line ();
648 T::dump_header (mem_location::get_origin_name (origin
));
649 T::print_dash_line ();
650 total
.dump_footer ();
651 T::print_dash_line ();
655 fprintf (stderr
, "\n");
658 #endif // GCC_MEM_STATS_H