1 /* A memory statistics tracking infrastructure. 2 Copyright (C) 2015-2018 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 /* Equality 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 172 && m_allocated == second.m_allocated); 173 } 174 175 /* Comparison operator. */ 176 inline bool 177 operator< (const mem_usage &second) const 178 { 179 if (*this == second) 180 return false; 181 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); 185 } 186 187 /* Compare wrapper used by qsort method. */ 188 static int 189 compare (const void *first, const void *second) 190 { 191 typedef std::pair<mem_location *, mem_usage *> mem_pair_t; 192 193 const mem_pair_t f = *(const mem_pair_t *)first; 194 const mem_pair_t s = *(const mem_pair_t *)second; 195 196 if (*f.second == *s.second) 197 return 0; 198 199 return *f.second < *s.second ? 1 : -1; 200 } 201 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 205 { 206 char *location_string = loc->to_string (); 207 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"); 214 215 free (location_string); 216 } 217 218 /* Dump footer. */ 219 inline void 220 dump_footer () const 221 { 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 (); 226 } 227 228 /* Return fraction of NOMINATOR and DENOMINATOR in percent. */ 229 static inline float 230 get_percent (size_t nominator, size_t denominator) 231 { 232 return denominator == 0 ? 0.0f : nominator * 100.0 / denominator; 233 } 234 235 /* Print line made of dashes. */ 236 static inline void 237 print_dash_line (size_t count = 140) 238 { 239 while (count--) 240 fputc ('-', stderr); 241 fputc ('\n', stderr); 242 } 243 244 /* Dump header with NAME. */ 245 static inline void 246 dump_header (const char *name) 247 { 248 fprintf (stderr, "%-48s %11s%16s%10s%17s\n", name, "Leak", "Peak", 249 "Times", "Type"); 250 print_dash_line (); 251 } 252 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; 261 }; 262 263 /* Memory usage pair that connectes memory usage and number 264 of allocated bytes. */ 265 template <class T> 266 struct mem_usage_pair 267 { 268 mem_usage_pair (T *usage_, size_t allocated_): usage (usage_), 269 allocated (allocated_) {} 270 271 T *usage; 272 size_t allocated; 273 }; 274 275 /* Memory allocation description. */ 276 template <class T> 277 class mem_alloc_description 278 { 279 public: 280 struct mem_location_hash : nofree_ptr_hash <mem_location> 281 { 282 static hashval_t 283 hash (value_type l) 284 { 285 inchash::hash hstate; 286 287 hstate.add_ptr ((const void *)l->m_filename); 288 hstate.add_ptr (l->m_function); 289 hstate.add_int (l->m_line); 290 291 return hstate.end (); 292 } 293 294 static bool 295 equal (value_type l1, value_type l2) 296 { 297 return l1->m_filename == l2->m_filename 298 && l1->m_function == l2->m_function 299 && l1->m_line == l2->m_line; 300 } 301 }; 302 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; 308 309 /* Default contructor. */ 310 mem_alloc_description (); 311 312 /* Default destructor. */ 313 ~mem_alloc_description (); 314 315 /* Returns true if instance PTR is registered by the memory description. */ 316 bool 317 contains_descriptor_for_instance (const void *ptr); 318 319 /* Return descriptor for instance PTR. */ 320 T * 321 get_descriptor_for_instance (const void *ptr); 322 323 /* Register memory allocation descriptor for container PTR which is 324 described by a memory LOCATION. */ 325 T * 326 register_descriptor (const void *ptr, mem_location *location); 327 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. */ 332 T * 333 register_descriptor (const void *ptr, mem_alloc_origin origin, 334 bool ggc, const char *name, int line, 335 const char *function); 336 337 /* Register instance overhead identified by PTR pointer. Allocation takes 338 SIZE bytes. */ 339 T * 340 register_instance_overhead (size_t size, const void *ptr); 341 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); 347 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); 353 354 /* Release intance object identified by PTR pointer. */ 355 void 356 release_object_overhead (void *ptr); 357 358 /* Get sum value for ORIGIN type of allocation for the descriptor. */ 359 T 360 get_sum (mem_alloc_origin origin); 361 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); 369 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); 374 375 /* Reverse object map used for every object allocation mapping. */ 376 reverse_object_map_t *m_reverse_object_map; 377 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); 383 384 /* Allocation location coupled to the description. */ 385 mem_location m_location; 386 387 /* Location to usage mapping. */ 388 mem_map_t *m_map; 389 390 /* Reverse pointer to usage mapping. */ 391 reverse_mem_map_t *m_reverse_map; 392 }; 393 394 395 /* Returns true if instance PTR is registered by the memory description. */ 396 397 template <class T> 398 inline bool 399 mem_alloc_description<T>::contains_descriptor_for_instance (const void *ptr) 400 { 401 return m_reverse_map->get (ptr); 402 } 403 404 /* Return descriptor for instance PTR. */ 405 406 template <class T> 407 inline T* 408 mem_alloc_description<T>::get_descriptor_for_instance (const void *ptr) 409 { 410 return m_reverse_map->get (ptr) ? (*m_reverse_map->get (ptr)).usage : NULL; 411 } 412 413 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) 420 { 421 T *usage = NULL; 422 423 T **slot = m_map->get (location); 424 if (slot) 425 { 426 delete location; 427 usage = *slot; 428 usage->m_instances++; 429 } 430 else 431 { 432 usage = new T (); 433 m_map->put (location, usage); 434 } 435 436 if (!m_reverse_map->get (ptr)) 437 m_reverse_map->put (ptr, mem_usage_pair<T> (usage, 0)); 438 439 return usage; 440 } 441 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. */ 446 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) 455 { 456 mem_location *l = new mem_location (origin, ggc, filename, line, function); 457 return register_descriptor (ptr, l); 458 } 459 460 /* Register instance overhead identified by PTR pointer. Allocation takes 461 SIZE bytes. */ 462 463 template <class T> 464 inline T* 465 mem_alloc_description<T>::register_instance_overhead (size_t size, 466 const void *ptr) 467 { 468 mem_usage_pair <T> *slot = m_reverse_map->get (ptr); 469 if (!slot) 470 { 471 /* Due to PCH, it can really happen. */ 472 return NULL; 473 } 474 475 T *usage = (*slot).usage; 476 usage->register_overhead (size); 477 478 return usage; 479 } 480 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. */ 484 485 template <class T> 486 void 487 mem_alloc_description<T>::register_object_overhead (T *usage, size_t size, 488 const void *ptr) 489 { 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)); 493 } 494 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. */ 497 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) 506 { 507 T *usage = register_descriptor (ptr, origin, filename, line, function); 508 usage->register_overhead (size); 509 510 return usage; 511 } 512 513 /* Release PTR pointer of SIZE bytes. */ 514 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) 519 { 520 mem_usage_pair<T> *slot = m_reverse_map->get (ptr); 521 522 if (!slot) 523 { 524 /* Due to PCH, it can really happen. */ 525 return; 526 } 527 528 mem_usage_pair<T> usage_pair = *slot; 529 usage_pair.usage->release_overhead (size); 530 531 if (remove_from_map) 532 m_reverse_map->remove (ptr); 533 } 534 535 /* Release intance object identified by PTR pointer. */ 536 537 template <class T> 538 inline void 539 mem_alloc_description<T>::release_object_overhead (void *ptr) 540 { 541 std::pair <T *, size_t> *entry = m_reverse_object_map->get (ptr); 542 if (entry) 543 { 544 entry->first->release_overhead (entry->second); 545 m_reverse_object_map->remove (ptr); 546 } 547 } 548 549 /* Default contructor. */ 550 551 template <class T> 552 inline 553 mem_alloc_description<T>::mem_alloc_description () 554 { 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); 558 } 559 560 /* Default destructor. */ 561 562 template <class T> 563 inline 564 mem_alloc_description<T>::~mem_alloc_description () 565 { 566 for (typename mem_map_t::iterator it = m_map->begin (); it != m_map->end (); 567 ++it) 568 { 569 delete (*it).first; 570 delete (*it).second; 571 } 572 573 delete m_map; 574 delete m_reverse_map; 575 delete m_reverse_object_map; 576 } 577 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. */ 582 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)) 588 { 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; 594 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); 599 600 qsort (list, i, element_size, cmp == NULL ? T::compare : cmp); 601 *length = i; 602 603 return list; 604 } 605 606 /* Get sum value for ORIGIN type of allocation for the descriptor. */ 607 608 template <class T> 609 inline T 610 mem_alloc_description<T>::get_sum (mem_alloc_origin origin) 611 { 612 unsigned length; 613 mem_list_t *list = get_list (origin, &length); 614 T sum; 615 616 for (unsigned i = 0; i < length; i++) 617 sum = sum + *list[i].second; 618 619 XDELETEVEC (list); 620 621 return sum; 622 } 623 624 /* Dump all tracked instances of type ORIGIN. If we want to process custom 625 order, CMP comparator can be provided. */ 626 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)) 632 { 633 unsigned length; 634 635 fprintf (stderr, "\n"); 636 637 mem_list_t *list = get_list (origin, &length, cmp); 638 T total = get_sum (origin); 639 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); 643 644 total.dump_footer (); 645 646 XDELETEVEC (list); 647 648 fprintf (stderr, "\n"); 649 } 650 651 #endif // GCC_MEM_STATS_H 652