Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2006, Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 //     * Redistributions of source code must retain the above copyright
      9 // notice, this list of conditions and the following disclaimer.
     10 //     * Redistributions in binary form must reproduce the above
     11 // copyright notice, this list of conditions and the following disclaimer
     12 // in the documentation and/or other materials provided with the
     13 // distribution.
     14 //     * Neither the name of Google Inc. nor the names of its
     15 // contributors may be used to endorse or promote products derived from
     16 // this software without specific prior written permission.
     17 //
     18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 // ---
     31 // Author: Sanjay Ghemawat
     32 //         Maxim Lifantsev (refactoring)
     33 //
     34 
     35 #include <config.h>
     36 
     37 #ifdef HAVE_UNISTD_H
     38 #include <unistd.h>   // for write()
     39 #endif
     40 #include <fcntl.h>    // for open()
     41 #ifdef HAVE_GLOB_H
     42 #include <glob.h>
     43 #ifndef GLOB_NOMATCH  // true on some old cygwins
     44 # define GLOB_NOMATCH 0
     45 #endif
     46 #endif
     47 #ifdef HAVE_INTTYPES_H
     48 #include <inttypes.h> // for PRIxPTR
     49 #endif
     50 #ifdef HAVE_POLL_H
     51 #include <poll.h>
     52 #endif
     53 #include <errno.h>
     54 #include <stdarg.h>
     55 #include <string>
     56 #include <map>
     57 #include <algorithm>  // for sort(), equal(), and copy()
     58 
     59 #include "heap-profile-table.h"
     60 
     61 #include "base/logging.h"
     62 #include "raw_printer.h"
     63 #include "symbolize.h"
     64 #include <gperftools/stacktrace.h>
     65 #include <gperftools/malloc_hook.h>
     66 #include "memory_region_map.h"
     67 #include "base/commandlineflags.h"
     68 #include "base/logging.h"    // for the RawFD I/O commands
     69 #include "base/sysinfo.h"
     70 
     71 using std::sort;
     72 using std::equal;
     73 using std::copy;
     74 using std::string;
     75 using std::map;
     76 
     77 using tcmalloc::FillProcSelfMaps;   // from sysinfo.h
     78 using tcmalloc::DumpProcSelfMaps;   // from sysinfo.h
     79 
     80 //----------------------------------------------------------------------
     81 
     82 DEFINE_bool(cleanup_old_heap_profiles,
     83             EnvToBool("HEAP_PROFILE_CLEANUP", true),
     84             "At initialization time, delete old heap profiles.");
     85 
     86 DEFINE_int32(heap_check_max_leaks,
     87              EnvToInt("HEAP_CHECK_MAX_LEAKS", 20),
     88              "The maximum number of leak reports to print.");
     89 
     90 //----------------------------------------------------------------------
     91 
     92 // header of the dumped heap profile
     93 static const char kProfileHeader[] = "heap profile: ";
     94 static const char kProcSelfMapsHeader[] = "\nMAPPED_LIBRARIES:\n";
     95 #if defined(TYPE_PROFILING)
     96 static const char kTypeProfileStatsHeader[] = "type statistics:\n";
     97 #endif  // defined(TYPE_PROFILING)
     98 
     99 //----------------------------------------------------------------------
    100 
    101 const char HeapProfileTable::kFileExt[] = ".heap";
    102 
    103 //----------------------------------------------------------------------
    104 
    105 static const int kHashTableSize = 179999;   // Size for bucket_table_.
    106 // GCC requires this declaration, but MSVC does not allow it.
    107 #if !defined(COMPILER_MSVC)
    108 /*static*/ const int HeapProfileTable::kMaxStackDepth;
    109 #endif
    110 
    111 //----------------------------------------------------------------------
    112 
    113 // We strip out different number of stack frames in debug mode
    114 // because less inlining happens in that case
    115 #ifdef NDEBUG
    116 static const int kStripFrames = 2;
    117 #else
    118 static const int kStripFrames = 3;
    119 #endif
    120 
    121 // For sorting Stats or Buckets by in-use space
    122 static bool ByAllocatedSpace(HeapProfileTable::Stats* a,
    123                              HeapProfileTable::Stats* b) {
    124   // Return true iff "a" has more allocated space than "b"
    125   return (a->alloc_size - a->free_size) > (b->alloc_size - b->free_size);
    126 }
    127 
    128 //----------------------------------------------------------------------
    129 
    130 HeapProfileTable::HeapProfileTable(Allocator alloc,
    131                                    DeAllocator dealloc,
    132                                    bool profile_mmap)
    133     : alloc_(alloc),
    134       dealloc_(dealloc),
    135       bucket_table_(NULL),
    136       profile_mmap_(profile_mmap),
    137       num_buckets_(0),
    138       address_map_(NULL) {
    139   // Make a hash table for buckets.
    140   const int table_bytes = kHashTableSize * sizeof(*bucket_table_);
    141   bucket_table_ = static_cast<Bucket**>(alloc_(table_bytes));
    142   memset(bucket_table_, 0, table_bytes);
    143 
    144   // Make an allocation map.
    145   address_map_ =
    146       new(alloc_(sizeof(AllocationMap))) AllocationMap(alloc_, dealloc_);
    147 
    148   // Initialize.
    149   memset(&total_, 0, sizeof(total_));
    150   num_buckets_ = 0;
    151 }
    152 
    153 HeapProfileTable::~HeapProfileTable() {
    154   // Free the allocation map.
    155   address_map_->~AllocationMap();
    156   dealloc_(address_map_);
    157   address_map_ = NULL;
    158 
    159   // Free the hash table.
    160   for (int i = 0; i < kHashTableSize; i++) {
    161     for (Bucket* curr = bucket_table_[i]; curr != 0; /**/) {
    162       Bucket* bucket = curr;
    163       curr = curr->next;
    164       dealloc_(bucket->stack);
    165       dealloc_(bucket);
    166     }
    167   }
    168   dealloc_(bucket_table_);
    169   bucket_table_ = NULL;
    170 }
    171 
    172 HeapProfileTable::Bucket* HeapProfileTable::GetBucket(int depth,
    173                                                       const void* const key[]) {
    174   // Make hash-value
    175   uintptr_t h = 0;
    176   for (int i = 0; i < depth; i++) {
    177     h += reinterpret_cast<uintptr_t>(key[i]);
    178     h += h << 10;
    179     h ^= h >> 6;
    180   }
    181   h += h << 3;
    182   h ^= h >> 11;
    183 
    184   // Lookup stack trace in table
    185   unsigned int buck = ((unsigned int) h) % kHashTableSize;
    186   for (Bucket* b = bucket_table_[buck]; b != 0; b = b->next) {
    187     if ((b->hash == h) &&
    188         (b->depth == depth) &&
    189         equal(key, key + depth, b->stack)) {
    190       return b;
    191     }
    192   }
    193 
    194   // Create new bucket
    195   const size_t key_size = sizeof(key[0]) * depth;
    196   const void** kcopy = reinterpret_cast<const void**>(alloc_(key_size));
    197   copy(key, key + depth, kcopy);
    198   Bucket* b = reinterpret_cast<Bucket*>(alloc_(sizeof(Bucket)));
    199   memset(b, 0, sizeof(*b));
    200   b->hash  = h;
    201   b->depth = depth;
    202   b->stack = kcopy;
    203   b->next  = bucket_table_[buck];
    204   bucket_table_[buck] = b;
    205   num_buckets_++;
    206   return b;
    207 }
    208 
    209 int HeapProfileTable::GetCallerStackTrace(
    210     int skip_count, void* stack[kMaxStackDepth]) {
    211   return MallocHook::GetCallerStackTrace(
    212       stack, kMaxStackDepth, kStripFrames + skip_count + 1);
    213 }
    214 
    215 void HeapProfileTable::RecordAlloc(
    216     const void* ptr, size_t bytes, int stack_depth,
    217     const void* const call_stack[]) {
    218   Bucket* b = GetBucket(stack_depth, call_stack);
    219   b->allocs++;
    220   b->alloc_size += bytes;
    221   total_.allocs++;
    222   total_.alloc_size += bytes;
    223 
    224   AllocValue v;
    225   v.set_bucket(b);  // also did set_live(false); set_ignore(false)
    226   v.bytes = bytes;
    227   address_map_->Insert(ptr, v);
    228 }
    229 
    230 void HeapProfileTable::RecordFree(const void* ptr) {
    231   AllocValue v;
    232   if (address_map_->FindAndRemove(ptr, &v)) {
    233     Bucket* b = v.bucket();
    234     b->frees++;
    235     b->free_size += v.bytes;
    236     total_.frees++;
    237     total_.free_size += v.bytes;
    238   }
    239 }
    240 
    241 bool HeapProfileTable::FindAlloc(const void* ptr, size_t* object_size) const {
    242   const AllocValue* alloc_value = address_map_->Find(ptr);
    243   if (alloc_value != NULL) *object_size = alloc_value->bytes;
    244   return alloc_value != NULL;
    245 }
    246 
    247 bool HeapProfileTable::FindAllocDetails(const void* ptr,
    248                                         AllocInfo* info) const {
    249   const AllocValue* alloc_value = address_map_->Find(ptr);
    250   if (alloc_value != NULL) {
    251     info->object_size = alloc_value->bytes;
    252     info->call_stack = alloc_value->bucket()->stack;
    253     info->stack_depth = alloc_value->bucket()->depth;
    254   }
    255   return alloc_value != NULL;
    256 }
    257 
    258 bool HeapProfileTable::FindInsideAlloc(const void* ptr,
    259                                        size_t max_size,
    260                                        const void** object_ptr,
    261                                        size_t* object_size) const {
    262   const AllocValue* alloc_value =
    263     address_map_->FindInside(&AllocValueSize, max_size, ptr, object_ptr);
    264   if (alloc_value != NULL) *object_size = alloc_value->bytes;
    265   return alloc_value != NULL;
    266 }
    267 
    268 bool HeapProfileTable::MarkAsLive(const void* ptr) {
    269   AllocValue* alloc = address_map_->FindMutable(ptr);
    270   if (alloc && !alloc->live()) {
    271     alloc->set_live(true);
    272     return true;
    273   }
    274   return false;
    275 }
    276 
    277 void HeapProfileTable::MarkAsIgnored(const void* ptr) {
    278   AllocValue* alloc = address_map_->FindMutable(ptr);
    279   if (alloc) {
    280     alloc->set_ignore(true);
    281   }
    282 }
    283 
    284 void HeapProfileTable::IterateAllocationAddresses(AddressIterator f,
    285                                                   void* data) {
    286   const AllocationAddressIteratorArgs args(f, data);
    287   address_map_->Iterate<const AllocationAddressIteratorArgs&>(
    288       AllocationAddressesIterator, args);
    289 }
    290 
    291 void HeapProfileTable::MarkCurrentAllocations(AllocationMark mark) {
    292   const MarkArgs args(mark, true);
    293   address_map_->Iterate<const MarkArgs&>(MarkIterator, args);
    294 }
    295 
    296 void HeapProfileTable::MarkUnmarkedAllocations(AllocationMark mark) {
    297   const MarkArgs args(mark, false);
    298   address_map_->Iterate<const MarkArgs&>(MarkIterator, args);
    299 }
    300 
    301 // We'd be happier using snprintfer, but we don't to reduce dependencies.
    302 int HeapProfileTable::UnparseBucket(const Bucket& b,
    303                                     char* buf, int buflen, int bufsize,
    304                                     const char* extra,
    305                                     Stats* profile_stats) {
    306   if (profile_stats != NULL) {
    307     profile_stats->allocs += b.allocs;
    308     profile_stats->alloc_size += b.alloc_size;
    309     profile_stats->frees += b.frees;
    310     profile_stats->free_size += b.free_size;
    311   }
    312   int printed =
    313     snprintf(buf + buflen, bufsize - buflen,
    314              "%6d: %8" PRId64 " [%6d: %8" PRId64 "] @%s",
    315              b.allocs - b.frees,
    316              b.alloc_size - b.free_size,
    317              b.allocs,
    318              b.alloc_size,
    319              extra);
    320   // If it looks like the snprintf failed, ignore the fact we printed anything
    321   if (printed < 0 || printed >= bufsize - buflen) return buflen;
    322   buflen += printed;
    323   for (int d = 0; d < b.depth; d++) {
    324     printed = snprintf(buf + buflen, bufsize - buflen, " 0x%08" PRIxPTR,
    325                        reinterpret_cast<uintptr_t>(b.stack[d]));
    326     if (printed < 0 || printed >= bufsize - buflen) return buflen;
    327     buflen += printed;
    328   }
    329   printed = snprintf(buf + buflen, bufsize - buflen, "\n");
    330   if (printed < 0 || printed >= bufsize - buflen) return buflen;
    331   buflen += printed;
    332   return buflen;
    333 }
    334 
    335 HeapProfileTable::Bucket**
    336 HeapProfileTable::MakeSortedBucketList() const {
    337   Bucket** list = static_cast<Bucket**>(alloc_(sizeof(Bucket) * num_buckets_));
    338 
    339   int bucket_count = 0;
    340   for (int i = 0; i < kHashTableSize; i++) {
    341     for (Bucket* curr = bucket_table_[i]; curr != 0; curr = curr->next) {
    342       list[bucket_count++] = curr;
    343     }
    344   }
    345   RAW_DCHECK(bucket_count == num_buckets_, "");
    346 
    347   sort(list, list + num_buckets_, ByAllocatedSpace);
    348 
    349   return list;
    350 }
    351 
    352 void HeapProfileTable::DumpMarkedObjects(AllocationMark mark,
    353                                          const char* file_name) {
    354   RawFD fd = RawOpenForWriting(file_name);
    355   if (fd == kIllegalRawFD) {
    356     RAW_LOG(ERROR, "Failed dumping live objects to %s", file_name);
    357     return;
    358   }
    359   const DumpMarkedArgs args(fd, mark);
    360   address_map_->Iterate<const DumpMarkedArgs&>(DumpMarkedIterator, args);
    361   RawClose(fd);
    362 }
    363 
    364 #if defined(TYPE_PROFILING)
    365 void HeapProfileTable::DumpTypeStatistics(const char* file_name) const {
    366   RawFD fd = RawOpenForWriting(file_name);
    367   if (fd == kIllegalRawFD) {
    368     RAW_LOG(ERROR, "Failed dumping type statistics to %s", file_name);
    369     return;
    370   }
    371 
    372   AddressMap<TypeCount>* type_size_map;
    373   type_size_map = new(alloc_(sizeof(AddressMap<TypeCount>)))
    374       AddressMap<TypeCount>(alloc_, dealloc_);
    375   address_map_->Iterate(TallyTypesItererator, type_size_map);
    376 
    377   RawWrite(fd, kTypeProfileStatsHeader, strlen(kTypeProfileStatsHeader));
    378   const DumpArgs args(fd, NULL);
    379   type_size_map->Iterate<const DumpArgs&>(DumpTypesIterator, args);
    380   RawClose(fd);
    381 
    382   type_size_map->~AddressMap<TypeCount>();
    383   dealloc_(type_size_map);
    384 }
    385 #endif  // defined(TYPE_PROFILING)
    386 
    387 void HeapProfileTable::IterateOrderedAllocContexts(
    388     AllocContextIterator callback) const {
    389   Bucket** list = MakeSortedBucketList();
    390   AllocContextInfo info;
    391   for (int i = 0; i < num_buckets_; ++i) {
    392     *static_cast<Stats*>(&info) = *static_cast<Stats*>(list[i]);
    393     info.stack_depth = list[i]->depth;
    394     info.call_stack = list[i]->stack;
    395     callback(info);
    396   }
    397   dealloc_(list);
    398 }
    399 
    400 int HeapProfileTable::FillOrderedProfile(char buf[], int size) const {
    401   Bucket** list = MakeSortedBucketList();
    402 
    403   // Our file format is "bucket, bucket, ..., bucket, proc_self_maps_info".
    404   // In the cases buf is too small, we'd rather leave out the last
    405   // buckets than leave out the /proc/self/maps info.  To ensure that,
    406   // we actually print the /proc/self/maps info first, then move it to
    407   // the end of the buffer, then write the bucket info into whatever
    408   // is remaining, and then move the maps info one last time to close
    409   // any gaps.  Whew!
    410   int map_length = snprintf(buf, size, "%s", kProcSelfMapsHeader);
    411   if (map_length < 0 || map_length >= size) return 0;
    412   bool dummy;   // "wrote_all" -- did /proc/self/maps fit in its entirety?
    413   map_length += FillProcSelfMaps(buf + map_length, size - map_length, &dummy);
    414   RAW_DCHECK(map_length <= size, "");
    415   char* const map_start = buf + size - map_length;      // move to end
    416   memmove(map_start, buf, map_length);
    417   size -= map_length;
    418 
    419   Stats stats;
    420   memset(&stats, 0, sizeof(stats));
    421   int bucket_length = snprintf(buf, size, "%s", kProfileHeader);
    422   if (bucket_length < 0 || bucket_length >= size) return 0;
    423   bucket_length = UnparseBucket(total_, buf, bucket_length, size,
    424                                 " heapprofile", &stats);
    425 
    426   // Dump the mmap list first.
    427   if (profile_mmap_) {
    428     BufferArgs buffer(buf, bucket_length, size);
    429     MemoryRegionMap::IterateBuckets<BufferArgs*>(DumpBucketIterator, &buffer);
    430     bucket_length = buffer.buflen;
    431   }
    432 
    433   for (int i = 0; i < num_buckets_; i++) {
    434     bucket_length = UnparseBucket(*list[i], buf, bucket_length, size, "",
    435                                   &stats);
    436   }
    437   RAW_DCHECK(bucket_length < size, "");
    438 
    439   dealloc_(list);
    440 
    441   RAW_DCHECK(buf + bucket_length <= map_start, "");
    442   memmove(buf + bucket_length, map_start, map_length);  // close the gap
    443 
    444   return bucket_length + map_length;
    445 }
    446 
    447 // static
    448 void HeapProfileTable::DumpBucketIterator(const Bucket* bucket,
    449                                           BufferArgs* args) {
    450   args->buflen = UnparseBucket(*bucket, args->buf, args->buflen, args->bufsize,
    451                                "", NULL);
    452 }
    453 
    454 #if defined(TYPE_PROFILING)
    455 // static
    456 void HeapProfileTable::TallyTypesItererator(
    457     const void* ptr,
    458     AllocValue* value,
    459     AddressMap<TypeCount>* type_size_map) {
    460   const std::type_info* type = LookupType(ptr);
    461 
    462   const void* key = NULL;
    463   if (type)
    464     key = type->name();
    465 
    466   TypeCount* count = type_size_map->FindMutable(key);
    467   if (count) {
    468     count->bytes += value->bytes;
    469     ++count->objects;
    470   } else {
    471     type_size_map->Insert(key, TypeCount(value->bytes, 1));
    472   }
    473 }
    474 
    475 // static
    476 void HeapProfileTable::DumpTypesIterator(const void* ptr,
    477                                          TypeCount* count,
    478                                          const DumpArgs& args) {
    479   char buf[1024];
    480   int len;
    481   const char* mangled_type_name = static_cast<const char*>(ptr);
    482   len = snprintf(buf, sizeof(buf), "%6d: %8" PRId64 " @ %s\n",
    483                  count->objects, count->bytes,
    484                  mangled_type_name ? mangled_type_name : "(no_typeinfo)");
    485   RawWrite(args.fd, buf, len);
    486 }
    487 #endif  // defined(TYPE_PROFILING)
    488 
    489 inline
    490 void HeapProfileTable::DumpNonLiveIterator(const void* ptr, AllocValue* v,
    491                                            const DumpArgs& args) {
    492   if (v->live()) {
    493     v->set_live(false);
    494     return;
    495   }
    496   if (v->ignore()) {
    497     return;
    498   }
    499   Bucket b;
    500   memset(&b, 0, sizeof(b));
    501   b.allocs = 1;
    502   b.alloc_size = v->bytes;
    503   b.depth = v->bucket()->depth;
    504   b.stack = v->bucket()->stack;
    505   char buf[1024];
    506   int len = UnparseBucket(b, buf, 0, sizeof(buf), "", args.profile_stats);
    507   RawWrite(args.fd, buf, len);
    508 }
    509 
    510 inline
    511 void HeapProfileTable::DumpMarkedIterator(const void* ptr, AllocValue* v,
    512                                           const DumpMarkedArgs& args) {
    513   if (v->mark() != args.mark)
    514     return;
    515   Bucket b;
    516   memset(&b, 0, sizeof(b));
    517   b.allocs = 1;
    518   b.alloc_size = v->bytes;
    519   b.depth = v->bucket()->depth;
    520   b.stack = v->bucket()->stack;
    521   char addr[16];
    522   snprintf(addr, 16, "0x%08" PRIxPTR, ptr);
    523   char buf[1024];
    524   int len = UnparseBucket(b, buf, 0, sizeof(buf), addr, NULL);
    525   RawWrite(args.fd, buf, len);
    526 }
    527 
    528 inline
    529 void HeapProfileTable::AllocationAddressesIterator(
    530     const void* ptr,
    531     AllocValue* v,
    532     const AllocationAddressIteratorArgs& args) {
    533   args.callback(args.data, ptr);
    534 }
    535 
    536 inline
    537 void HeapProfileTable::MarkIterator(const void* ptr, AllocValue* v,
    538                                     const MarkArgs& args) {
    539   if (!args.mark_all && v->mark() != UNMARKED)
    540     return;
    541   v->set_mark(args.mark);
    542 }
    543 
    544 // Callback from NonLiveSnapshot; adds entry to arg->dest
    545 // if not the entry is not live and is not present in arg->base.
    546 void HeapProfileTable::AddIfNonLive(const void* ptr, AllocValue* v,
    547                                     AddNonLiveArgs* arg) {
    548   if (v->live()) {
    549     v->set_live(false);
    550   } else {
    551     if (arg->base != NULL && arg->base->map_.Find(ptr) != NULL) {
    552       // Present in arg->base, so do not save
    553     } else {
    554       arg->dest->Add(ptr, *v);
    555     }
    556   }
    557 }
    558 
    559 bool HeapProfileTable::WriteProfile(const char* file_name,
    560                                     const Bucket& total,
    561                                     AllocationMap* allocations) {
    562   RAW_VLOG(1, "Dumping non-live heap profile to %s", file_name);
    563   RawFD fd = RawOpenForWriting(file_name);
    564   if (fd == kIllegalRawFD) {
    565     RAW_LOG(ERROR, "Failed dumping filtered heap profile to %s", file_name);
    566     return false;
    567   }
    568   RawWrite(fd, kProfileHeader, strlen(kProfileHeader));
    569   char buf[512];
    570   int len = UnparseBucket(total, buf, 0, sizeof(buf), " heapprofile",
    571                           NULL);
    572   RawWrite(fd, buf, len);
    573   const DumpArgs args(fd, NULL);
    574   allocations->Iterate<const DumpArgs&>(DumpNonLiveIterator, args);
    575   RawWrite(fd, kProcSelfMapsHeader, strlen(kProcSelfMapsHeader));
    576   DumpProcSelfMaps(fd);
    577   RawClose(fd);
    578   return true;
    579 }
    580 
    581 void HeapProfileTable::CleanupOldProfiles(const char* prefix) {
    582   if (!FLAGS_cleanup_old_heap_profiles)
    583     return;
    584   char buf[1000];
    585   snprintf(buf, 1000,"%s.%05d.", prefix, getpid());
    586   string pattern = string(buf) + ".*" + kFileExt;
    587 
    588 #if defined(HAVE_GLOB_H)
    589   glob_t g;
    590   const int r = glob(pattern.c_str(), GLOB_ERR, NULL, &g);
    591   if (r == 0 || r == GLOB_NOMATCH) {
    592     const int prefix_length = strlen(prefix);
    593     for (int i = 0; i < g.gl_pathc; i++) {
    594       const char* fname = g.gl_pathv[i];
    595       if ((strlen(fname) >= prefix_length) &&
    596           (memcmp(fname, prefix, prefix_length) == 0)) {
    597         RAW_VLOG(1, "Removing old heap profile %s", fname);
    598         unlink(fname);
    599       }
    600     }
    601   }
    602   globfree(&g);
    603 #else   /* HAVE_GLOB_H */
    604   RAW_LOG(WARNING, "Unable to remove old heap profiles (can't run glob())");
    605 #endif
    606 }
    607 
    608 HeapProfileTable::Snapshot* HeapProfileTable::TakeSnapshot() {
    609   Snapshot* s = new (alloc_(sizeof(Snapshot))) Snapshot(alloc_, dealloc_);
    610   address_map_->Iterate(AddToSnapshot, s);
    611   return s;
    612 }
    613 
    614 void HeapProfileTable::ReleaseSnapshot(Snapshot* s) {
    615   s->~Snapshot();
    616   dealloc_(s);
    617 }
    618 
    619 // Callback from TakeSnapshot; adds a single entry to snapshot
    620 void HeapProfileTable::AddToSnapshot(const void* ptr, AllocValue* v,
    621                                      Snapshot* snapshot) {
    622   snapshot->Add(ptr, *v);
    623 }
    624 
    625 HeapProfileTable::Snapshot* HeapProfileTable::NonLiveSnapshot(
    626     Snapshot* base) {
    627   RAW_VLOG(2, "NonLiveSnapshot input: %d %d\n",
    628            int(total_.allocs - total_.frees),
    629            int(total_.alloc_size - total_.free_size));
    630 
    631   Snapshot* s = new (alloc_(sizeof(Snapshot))) Snapshot(alloc_, dealloc_);
    632   AddNonLiveArgs args;
    633   args.dest = s;
    634   args.base = base;
    635   address_map_->Iterate<AddNonLiveArgs*>(AddIfNonLive, &args);
    636   RAW_VLOG(2, "NonLiveSnapshot output: %d %d\n",
    637            int(s->total_.allocs - s->total_.frees),
    638            int(s->total_.alloc_size - s->total_.free_size));
    639   return s;
    640 }
    641 
    642 // Information kept per unique bucket seen
    643 struct HeapProfileTable::Snapshot::Entry {
    644   int count;
    645   int bytes;
    646   Bucket* bucket;
    647   Entry() : count(0), bytes(0) { }
    648 
    649   // Order by decreasing bytes
    650   bool operator<(const Entry& x) const {
    651     return this->bytes > x.bytes;
    652   }
    653 };
    654 
    655 // State used to generate leak report.  We keep a mapping from Bucket pointer
    656 // the collected stats for that bucket.
    657 struct HeapProfileTable::Snapshot::ReportState {
    658   map<Bucket*, Entry> buckets_;
    659 };
    660 
    661 // Callback from ReportLeaks; updates ReportState.
    662 void HeapProfileTable::Snapshot::ReportCallback(const void* ptr,
    663                                                 AllocValue* v,
    664                                                 ReportState* state) {
    665   Entry* e = &state->buckets_[v->bucket()]; // Creates empty Entry first time
    666   e->bucket = v->bucket();
    667   e->count++;
    668   e->bytes += v->bytes;
    669 }
    670 
    671 void HeapProfileTable::Snapshot::ReportLeaks(const char* checker_name,
    672                                              const char* filename,
    673                                              bool should_symbolize) {
    674   // This is only used by the heap leak checker, but is intimately
    675   // tied to the allocation map that belongs in this module and is
    676   // therefore placed here.
    677   RAW_LOG(ERROR, "Leak check %s detected leaks of %" PRIuS " bytes "
    678           "in %" PRIuS " objects",
    679           checker_name,
    680           size_t(total_.alloc_size),
    681           size_t(total_.allocs));
    682 
    683   // Group objects by Bucket
    684   ReportState state;
    685   map_.Iterate(&ReportCallback, &state);
    686 
    687   // Sort buckets by decreasing leaked size
    688   const int n = state.buckets_.size();
    689   Entry* entries = new Entry[n];
    690   int dst = 0;
    691   for (map<Bucket*,Entry>::const_iterator iter = state.buckets_.begin();
    692        iter != state.buckets_.end();
    693        ++iter) {
    694     entries[dst++] = iter->second;
    695   }
    696   sort(entries, entries + n);
    697 
    698   // Report a bounded number of leaks to keep the leak report from
    699   // growing too long.
    700   const int to_report =
    701       (FLAGS_heap_check_max_leaks > 0 &&
    702        n > FLAGS_heap_check_max_leaks) ? FLAGS_heap_check_max_leaks : n;
    703   RAW_LOG(ERROR, "The %d largest leaks:", to_report);
    704 
    705   // Print
    706   SymbolTable symbolization_table;
    707   for (int i = 0; i < to_report; i++) {
    708     const Entry& e = entries[i];
    709     for (int j = 0; j < e.bucket->depth; j++) {
    710       symbolization_table.Add(e.bucket->stack[j]);
    711     }
    712   }
    713   static const int kBufSize = 2<<10;
    714   char buffer[kBufSize];
    715   if (should_symbolize)
    716     symbolization_table.Symbolize();
    717   for (int i = 0; i < to_report; i++) {
    718     const Entry& e = entries[i];
    719     base::RawPrinter printer(buffer, kBufSize);
    720     printer.Printf("Leak of %d bytes in %d objects allocated from:\n",
    721                    e.bytes, e.count);
    722     for (int j = 0; j < e.bucket->depth; j++) {
    723       const void* pc = e.bucket->stack[j];
    724       printer.Printf("\t@ %" PRIxPTR " %s\n",
    725           reinterpret_cast<uintptr_t>(pc), symbolization_table.GetSymbol(pc));
    726     }
    727     RAW_LOG(ERROR, "%s", buffer);
    728   }
    729 
    730   if (to_report < n) {
    731     RAW_LOG(ERROR, "Skipping leaks numbered %d..%d",
    732             to_report, n-1);
    733   }
    734   delete[] entries;
    735 
    736   // TODO: Dump the sorted Entry list instead of dumping raw data?
    737   // (should be much shorter)
    738   if (!HeapProfileTable::WriteProfile(filename, total_, &map_)) {
    739     RAW_LOG(ERROR, "Could not write pprof profile to %s", filename);
    740   }
    741 }
    742 
    743 void HeapProfileTable::Snapshot::ReportObject(const void* ptr,
    744                                               AllocValue* v,
    745                                               char* unused) {
    746   // Perhaps also log the allocation stack trace (unsymbolized)
    747   // on this line in case somebody finds it useful.
    748   RAW_LOG(ERROR, "leaked %" PRIuS " byte object %p", v->bytes, ptr);
    749 }
    750 
    751 void HeapProfileTable::Snapshot::ReportIndividualObjects() {
    752   char unused;
    753   map_.Iterate(ReportObject, &unused);
    754 }
    755