- add sources.
[platform/framework/web/crosswalk.git] / src / third_party / tcmalloc / chromium / src / heap-profile-table.cc
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 /*static*/ const int HeapProfileTable::kMaxStackDepth;
107
108 //----------------------------------------------------------------------
109
110 // We strip out different number of stack frames in debug mode
111 // because less inlining happens in that case
112 #ifdef NDEBUG
113 static const int kStripFrames = 2;
114 #else
115 static const int kStripFrames = 3;
116 #endif
117
118 // For sorting Stats or Buckets by in-use space
119 static bool ByAllocatedSpace(HeapProfileTable::Stats* a,
120                              HeapProfileTable::Stats* b) {
121   // Return true iff "a" has more allocated space than "b"
122   return (a->alloc_size - a->free_size) > (b->alloc_size - b->free_size);
123 }
124
125 //----------------------------------------------------------------------
126
127 HeapProfileTable::HeapProfileTable(Allocator alloc,
128                                    DeAllocator dealloc,
129                                    bool profile_mmap)
130     : alloc_(alloc),
131       dealloc_(dealloc),
132       bucket_table_(NULL),
133       profile_mmap_(profile_mmap),
134       num_buckets_(0),
135       address_map_(NULL) {
136   // Make a hash table for buckets.
137   const int table_bytes = kHashTableSize * sizeof(*bucket_table_);
138   bucket_table_ = static_cast<Bucket**>(alloc_(table_bytes));
139   memset(bucket_table_, 0, table_bytes);
140
141   // Make an allocation map.
142   address_map_ =
143       new(alloc_(sizeof(AllocationMap))) AllocationMap(alloc_, dealloc_);
144
145   // Initialize.
146   memset(&total_, 0, sizeof(total_));
147   num_buckets_ = 0;
148 }
149
150 HeapProfileTable::~HeapProfileTable() {
151   // Free the allocation map.
152   address_map_->~AllocationMap();
153   dealloc_(address_map_);
154   address_map_ = NULL;
155
156   // Free the hash table.
157   for (int i = 0; i < kHashTableSize; i++) {
158     for (Bucket* curr = bucket_table_[i]; curr != 0; /**/) {
159       Bucket* bucket = curr;
160       curr = curr->next;
161       dealloc_(bucket->stack);
162       dealloc_(bucket);
163     }
164   }
165   dealloc_(bucket_table_);
166   bucket_table_ = NULL;
167 }
168
169 HeapProfileTable::Bucket* HeapProfileTable::GetBucket(int depth,
170                                                       const void* const key[]) {
171   // Make hash-value
172   uintptr_t h = 0;
173   for (int i = 0; i < depth; i++) {
174     h += reinterpret_cast<uintptr_t>(key[i]);
175     h += h << 10;
176     h ^= h >> 6;
177   }
178   h += h << 3;
179   h ^= h >> 11;
180
181   // Lookup stack trace in table
182   unsigned int buck = ((unsigned int) h) % kHashTableSize;
183   for (Bucket* b = bucket_table_[buck]; b != 0; b = b->next) {
184     if ((b->hash == h) &&
185         (b->depth == depth) &&
186         equal(key, key + depth, b->stack)) {
187       return b;
188     }
189   }
190
191   // Create new bucket
192   const size_t key_size = sizeof(key[0]) * depth;
193   const void** kcopy = reinterpret_cast<const void**>(alloc_(key_size));
194   copy(key, key + depth, kcopy);
195   Bucket* b = reinterpret_cast<Bucket*>(alloc_(sizeof(Bucket)));
196   memset(b, 0, sizeof(*b));
197   b->hash  = h;
198   b->depth = depth;
199   b->stack = kcopy;
200   b->next  = bucket_table_[buck];
201   bucket_table_[buck] = b;
202   num_buckets_++;
203   return b;
204 }
205
206 int HeapProfileTable::GetCallerStackTrace(
207     int skip_count, void* stack[kMaxStackDepth]) {
208   return MallocHook::GetCallerStackTrace(
209       stack, kMaxStackDepth, kStripFrames + skip_count + 1);
210 }
211
212 void HeapProfileTable::RecordAlloc(
213     const void* ptr, size_t bytes, int stack_depth,
214     const void* const call_stack[]) {
215   Bucket* b = GetBucket(stack_depth, call_stack);
216   b->allocs++;
217   b->alloc_size += bytes;
218   total_.allocs++;
219   total_.alloc_size += bytes;
220
221   AllocValue v;
222   v.set_bucket(b);  // also did set_live(false); set_ignore(false)
223   v.bytes = bytes;
224   address_map_->Insert(ptr, v);
225 }
226
227 void HeapProfileTable::RecordFree(const void* ptr) {
228   AllocValue v;
229   if (address_map_->FindAndRemove(ptr, &v)) {
230     Bucket* b = v.bucket();
231     b->frees++;
232     b->free_size += v.bytes;
233     total_.frees++;
234     total_.free_size += v.bytes;
235   }
236 }
237
238 bool HeapProfileTable::FindAlloc(const void* ptr, size_t* object_size) const {
239   const AllocValue* alloc_value = address_map_->Find(ptr);
240   if (alloc_value != NULL) *object_size = alloc_value->bytes;
241   return alloc_value != NULL;
242 }
243
244 bool HeapProfileTable::FindAllocDetails(const void* ptr,
245                                         AllocInfo* info) const {
246   const AllocValue* alloc_value = address_map_->Find(ptr);
247   if (alloc_value != NULL) {
248     info->object_size = alloc_value->bytes;
249     info->call_stack = alloc_value->bucket()->stack;
250     info->stack_depth = alloc_value->bucket()->depth;
251   }
252   return alloc_value != NULL;
253 }
254
255 bool HeapProfileTable::FindInsideAlloc(const void* ptr,
256                                        size_t max_size,
257                                        const void** object_ptr,
258                                        size_t* object_size) const {
259   const AllocValue* alloc_value =
260     address_map_->FindInside(&AllocValueSize, max_size, ptr, object_ptr);
261   if (alloc_value != NULL) *object_size = alloc_value->bytes;
262   return alloc_value != NULL;
263 }
264
265 bool HeapProfileTable::MarkAsLive(const void* ptr) {
266   AllocValue* alloc = address_map_->FindMutable(ptr);
267   if (alloc && !alloc->live()) {
268     alloc->set_live(true);
269     return true;
270   }
271   return false;
272 }
273
274 void HeapProfileTable::MarkAsIgnored(const void* ptr) {
275   AllocValue* alloc = address_map_->FindMutable(ptr);
276   if (alloc) {
277     alloc->set_ignore(true);
278   }
279 }
280
281 void HeapProfileTable::IterateAllocationAddresses(AddressIterator f,
282                                                   void* data) {
283   const AllocationAddressIteratorArgs args(f, data);
284   address_map_->Iterate<const AllocationAddressIteratorArgs&>(
285       AllocationAddressesIterator, args);
286 }
287
288 void HeapProfileTable::MarkCurrentAllocations(AllocationMark mark) {
289   const MarkArgs args(mark, true);
290   address_map_->Iterate<const MarkArgs&>(MarkIterator, args);
291 }
292
293 void HeapProfileTable::MarkUnmarkedAllocations(AllocationMark mark) {
294   const MarkArgs args(mark, true);
295   address_map_->Iterate<const MarkArgs&>(MarkIterator, args);
296 }
297
298 // We'd be happier using snprintfer, but we don't to reduce dependencies.
299 int HeapProfileTable::UnparseBucket(const Bucket& b,
300                                     char* buf, int buflen, int bufsize,
301                                     const char* extra,
302                                     Stats* profile_stats) {
303   if (profile_stats != NULL) {
304     profile_stats->allocs += b.allocs;
305     profile_stats->alloc_size += b.alloc_size;
306     profile_stats->frees += b.frees;
307     profile_stats->free_size += b.free_size;
308   }
309   int printed =
310     snprintf(buf + buflen, bufsize - buflen,
311              "%6d: %8" PRId64 " [%6d: %8" PRId64 "] @%s",
312              b.allocs - b.frees,
313              b.alloc_size - b.free_size,
314              b.allocs,
315              b.alloc_size,
316              extra);
317   // If it looks like the snprintf failed, ignore the fact we printed anything
318   if (printed < 0 || printed >= bufsize - buflen) return buflen;
319   buflen += printed;
320   for (int d = 0; d < b.depth; d++) {
321     printed = snprintf(buf + buflen, bufsize - buflen, " 0x%08" PRIxPTR,
322                        reinterpret_cast<uintptr_t>(b.stack[d]));
323     if (printed < 0 || printed >= bufsize - buflen) return buflen;
324     buflen += printed;
325   }
326   printed = snprintf(buf + buflen, bufsize - buflen, "\n");
327   if (printed < 0 || printed >= bufsize - buflen) return buflen;
328   buflen += printed;
329   return buflen;
330 }
331
332 HeapProfileTable::Bucket**
333 HeapProfileTable::MakeSortedBucketList() const {
334   Bucket** list = static_cast<Bucket**>(alloc_(sizeof(Bucket) * num_buckets_));
335
336   int bucket_count = 0;
337   for (int i = 0; i < kHashTableSize; i++) {
338     for (Bucket* curr = bucket_table_[i]; curr != 0; curr = curr->next) {
339       list[bucket_count++] = curr;
340     }
341   }
342   RAW_DCHECK(bucket_count == num_buckets_, "");
343
344   sort(list, list + num_buckets_, ByAllocatedSpace);
345
346   return list;
347 }
348
349 void HeapProfileTable::DumpMarkedObjects(AllocationMark mark,
350                                          const char* file_name) {
351   RawFD fd = RawOpenForWriting(file_name);
352   if (fd == kIllegalRawFD) {
353     RAW_LOG(ERROR, "Failed dumping live objects to %s", file_name);
354     return;
355   }
356   const DumpMarkedArgs args(fd, mark);
357   address_map_->Iterate<const DumpMarkedArgs&>(DumpMarkedIterator, args);
358   RawClose(fd);
359 }
360
361 #if defined(TYPE_PROFILING)
362 void HeapProfileTable::DumpTypeStatistics(const char* file_name) const {
363   RawFD fd = RawOpenForWriting(file_name);
364   if (fd == kIllegalRawFD) {
365     RAW_LOG(ERROR, "Failed dumping type statistics to %s", file_name);
366     return;
367   }
368
369   AddressMap<TypeCount>* type_size_map;
370   type_size_map = new(alloc_(sizeof(AddressMap<TypeCount>)))
371       AddressMap<TypeCount>(alloc_, dealloc_);
372   address_map_->Iterate(TallyTypesItererator, type_size_map);
373
374   RawWrite(fd, kTypeProfileStatsHeader, strlen(kTypeProfileStatsHeader));
375   const DumpArgs args(fd, NULL);
376   type_size_map->Iterate<const DumpArgs&>(DumpTypesIterator, args);
377   RawClose(fd);
378
379   type_size_map->~AddressMap<TypeCount>();
380   dealloc_(type_size_map);
381 }
382 #endif  // defined(TYPE_PROFILING)
383
384 void HeapProfileTable::IterateOrderedAllocContexts(
385     AllocContextIterator callback) const {
386   Bucket** list = MakeSortedBucketList();
387   AllocContextInfo info;
388   for (int i = 0; i < num_buckets_; ++i) {
389     *static_cast<Stats*>(&info) = *static_cast<Stats*>(list[i]);
390     info.stack_depth = list[i]->depth;
391     info.call_stack = list[i]->stack;
392     callback(info);
393   }
394   dealloc_(list);
395 }
396
397 int HeapProfileTable::FillOrderedProfile(char buf[], int size) const {
398   Bucket** list = MakeSortedBucketList();
399
400   // Our file format is "bucket, bucket, ..., bucket, proc_self_maps_info".
401   // In the cases buf is too small, we'd rather leave out the last
402   // buckets than leave out the /proc/self/maps info.  To ensure that,
403   // we actually print the /proc/self/maps info first, then move it to
404   // the end of the buffer, then write the bucket info into whatever
405   // is remaining, and then move the maps info one last time to close
406   // any gaps.  Whew!
407   int map_length = snprintf(buf, size, "%s", kProcSelfMapsHeader);
408   if (map_length < 0 || map_length >= size) return 0;
409   bool dummy;   // "wrote_all" -- did /proc/self/maps fit in its entirety?
410   map_length += FillProcSelfMaps(buf + map_length, size - map_length, &dummy);
411   RAW_DCHECK(map_length <= size, "");
412   char* const map_start = buf + size - map_length;      // move to end
413   memmove(map_start, buf, map_length);
414   size -= map_length;
415
416   Stats stats;
417   memset(&stats, 0, sizeof(stats));
418   int bucket_length = snprintf(buf, size, "%s", kProfileHeader);
419   if (bucket_length < 0 || bucket_length >= size) return 0;
420   bucket_length = UnparseBucket(total_, buf, bucket_length, size,
421                                 " heapprofile", &stats);
422
423   // Dump the mmap list first.
424   if (profile_mmap_) {
425     BufferArgs buffer(buf, bucket_length, size);
426     MemoryRegionMap::IterateBuckets<BufferArgs*>(DumpBucketIterator, &buffer);
427     bucket_length = buffer.buflen;
428   }
429
430   for (int i = 0; i < num_buckets_; i++) {
431     bucket_length = UnparseBucket(*list[i], buf, bucket_length, size, "",
432                                   &stats);
433   }
434   RAW_DCHECK(bucket_length < size, "");
435
436   dealloc_(list);
437
438   RAW_DCHECK(buf + bucket_length <= map_start, "");
439   memmove(buf + bucket_length, map_start, map_length);  // close the gap
440
441   return bucket_length + map_length;
442 }
443
444 // static
445 void HeapProfileTable::DumpBucketIterator(const Bucket* bucket,
446                                           BufferArgs* args) {
447   args->buflen = UnparseBucket(*bucket, args->buf, args->buflen, args->bufsize,
448                                "", NULL);
449 }
450
451 #if defined(TYPE_PROFILING)
452 // static
453 void HeapProfileTable::TallyTypesItererator(
454     const void* ptr,
455     AllocValue* value,
456     AddressMap<TypeCount>* type_size_map) {
457   const std::type_info* type = LookupType(ptr);
458
459   const void* key = NULL;
460   if (type)
461     key = type->name();
462
463   TypeCount* count = type_size_map->FindMutable(key);
464   if (count) {
465     count->bytes += value->bytes;
466     ++count->objects;
467   } else {
468     type_size_map->Insert(key, TypeCount(value->bytes, 1));
469   }
470 }
471
472 // static
473 void HeapProfileTable::DumpTypesIterator(const void* ptr,
474                                          TypeCount* count,
475                                          const DumpArgs& args) {
476   char buf[1024];
477   int len;
478   const char* mangled_type_name = static_cast<const char*>(ptr);
479   len = snprintf(buf, sizeof(buf), "%6d: %8" PRId64 " @ %s\n",
480                  count->objects, count->bytes,
481                  mangled_type_name ? mangled_type_name : "(no_typeinfo)");
482   RawWrite(args.fd, buf, len);
483 }
484 #endif  // defined(TYPE_PROFILING)
485
486 inline
487 void HeapProfileTable::DumpNonLiveIterator(const void* ptr, AllocValue* v,
488                                            const DumpArgs& args) {
489   if (v->live()) {
490     v->set_live(false);
491     return;
492   }
493   if (v->ignore()) {
494     return;
495   }
496   Bucket b;
497   memset(&b, 0, sizeof(b));
498   b.allocs = 1;
499   b.alloc_size = v->bytes;
500   b.depth = v->bucket()->depth;
501   b.stack = v->bucket()->stack;
502   char buf[1024];
503   int len = UnparseBucket(b, buf, 0, sizeof(buf), "", args.profile_stats);
504   RawWrite(args.fd, buf, len);
505 }
506
507 inline
508 void HeapProfileTable::DumpMarkedIterator(const void* ptr, AllocValue* v,
509                                           const DumpMarkedArgs& args) {
510   if (v->mark() != args.mark)
511     return;
512   Bucket b;
513   memset(&b, 0, sizeof(b));
514   b.allocs = 1;
515   b.alloc_size = v->bytes;
516   b.depth = v->bucket()->depth;
517   b.stack = v->bucket()->stack;
518   char addr[16];
519   snprintf(addr, 16, "0x%08" PRIxPTR, ptr);
520   char buf[1024];
521   int len = UnparseBucket(b, buf, 0, sizeof(buf), addr, NULL);
522   RawWrite(args.fd, buf, len);
523 }
524
525 inline
526 void HeapProfileTable::AllocationAddressesIterator(
527     const void* ptr,
528     AllocValue* v,
529     const AllocationAddressIteratorArgs& args) {
530   args.callback(args.data, ptr);
531 }
532
533 inline
534 void HeapProfileTable::MarkIterator(const void* ptr, AllocValue* v,
535                                     const MarkArgs& args) {
536   if (!args.mark_all && v->mark() != UNMARKED)
537     return;
538   v->set_mark(args.mark);
539 }
540
541 // Callback from NonLiveSnapshot; adds entry to arg->dest
542 // if not the entry is not live and is not present in arg->base.
543 void HeapProfileTable::AddIfNonLive(const void* ptr, AllocValue* v,
544                                     AddNonLiveArgs* arg) {
545   if (v->live()) {
546     v->set_live(false);
547   } else {
548     if (arg->base != NULL && arg->base->map_.Find(ptr) != NULL) {
549       // Present in arg->base, so do not save
550     } else {
551       arg->dest->Add(ptr, *v);
552     }
553   }
554 }
555
556 bool HeapProfileTable::WriteProfile(const char* file_name,
557                                     const Bucket& total,
558                                     AllocationMap* allocations) {
559   RAW_VLOG(1, "Dumping non-live heap profile to %s", file_name);
560   RawFD fd = RawOpenForWriting(file_name);
561   if (fd == kIllegalRawFD) {
562     RAW_LOG(ERROR, "Failed dumping filtered heap profile to %s", file_name);
563     return false;
564   }
565   RawWrite(fd, kProfileHeader, strlen(kProfileHeader));
566   char buf[512];
567   int len = UnparseBucket(total, buf, 0, sizeof(buf), " heapprofile",
568                           NULL);
569   RawWrite(fd, buf, len);
570   const DumpArgs args(fd, NULL);
571   allocations->Iterate<const DumpArgs&>(DumpNonLiveIterator, args);
572   RawWrite(fd, kProcSelfMapsHeader, strlen(kProcSelfMapsHeader));
573   DumpProcSelfMaps(fd);
574   RawClose(fd);
575   return true;
576 }
577
578 void HeapProfileTable::CleanupOldProfiles(const char* prefix) {
579   if (!FLAGS_cleanup_old_heap_profiles)
580     return;
581   char buf[1000];
582   snprintf(buf, 1000,"%s.%05d.", prefix, getpid());
583   string pattern = string(buf) + ".*" + kFileExt;
584
585 #if defined(HAVE_GLOB_H)
586   glob_t g;
587   const int r = glob(pattern.c_str(), GLOB_ERR, NULL, &g);
588   if (r == 0 || r == GLOB_NOMATCH) {
589     const int prefix_length = strlen(prefix);
590     for (int i = 0; i < g.gl_pathc; i++) {
591       const char* fname = g.gl_pathv[i];
592       if ((strlen(fname) >= prefix_length) &&
593           (memcmp(fname, prefix, prefix_length) == 0)) {
594         RAW_VLOG(1, "Removing old heap profile %s", fname);
595         unlink(fname);
596       }
597     }
598   }
599   globfree(&g);
600 #else   /* HAVE_GLOB_H */
601   RAW_LOG(WARNING, "Unable to remove old heap profiles (can't run glob())");
602 #endif
603 }
604
605 HeapProfileTable::Snapshot* HeapProfileTable::TakeSnapshot() {
606   Snapshot* s = new (alloc_(sizeof(Snapshot))) Snapshot(alloc_, dealloc_);
607   address_map_->Iterate(AddToSnapshot, s);
608   return s;
609 }
610
611 void HeapProfileTable::ReleaseSnapshot(Snapshot* s) {
612   s->~Snapshot();
613   dealloc_(s);
614 }
615
616 // Callback from TakeSnapshot; adds a single entry to snapshot
617 void HeapProfileTable::AddToSnapshot(const void* ptr, AllocValue* v,
618                                      Snapshot* snapshot) {
619   snapshot->Add(ptr, *v);
620 }
621
622 HeapProfileTable::Snapshot* HeapProfileTable::NonLiveSnapshot(
623     Snapshot* base) {
624   RAW_VLOG(2, "NonLiveSnapshot input: %d %d\n",
625            int(total_.allocs - total_.frees),
626            int(total_.alloc_size - total_.free_size));
627
628   Snapshot* s = new (alloc_(sizeof(Snapshot))) Snapshot(alloc_, dealloc_);
629   AddNonLiveArgs args;
630   args.dest = s;
631   args.base = base;
632   address_map_->Iterate<AddNonLiveArgs*>(AddIfNonLive, &args);
633   RAW_VLOG(2, "NonLiveSnapshot output: %d %d\n",
634            int(s->total_.allocs - s->total_.frees),
635            int(s->total_.alloc_size - s->total_.free_size));
636   return s;
637 }
638
639 // Information kept per unique bucket seen
640 struct HeapProfileTable::Snapshot::Entry {
641   int count;
642   int bytes;
643   Bucket* bucket;
644   Entry() : count(0), bytes(0) { }
645
646   // Order by decreasing bytes
647   bool operator<(const Entry& x) const {
648     return this->bytes > x.bytes;
649   }
650 };
651
652 // State used to generate leak report.  We keep a mapping from Bucket pointer
653 // the collected stats for that bucket.
654 struct HeapProfileTable::Snapshot::ReportState {
655   map<Bucket*, Entry> buckets_;
656 };
657
658 // Callback from ReportLeaks; updates ReportState.
659 void HeapProfileTable::Snapshot::ReportCallback(const void* ptr,
660                                                 AllocValue* v,
661                                                 ReportState* state) {
662   Entry* e = &state->buckets_[v->bucket()]; // Creates empty Entry first time
663   e->bucket = v->bucket();
664   e->count++;
665   e->bytes += v->bytes;
666 }
667
668 void HeapProfileTable::Snapshot::ReportLeaks(const char* checker_name,
669                                              const char* filename,
670                                              bool should_symbolize) {
671   // This is only used by the heap leak checker, but is intimately
672   // tied to the allocation map that belongs in this module and is
673   // therefore placed here.
674   RAW_LOG(ERROR, "Leak check %s detected leaks of %" PRIuS " bytes "
675           "in %" PRIuS " objects",
676           checker_name,
677           size_t(total_.alloc_size),
678           size_t(total_.allocs));
679
680   // Group objects by Bucket
681   ReportState state;
682   map_.Iterate(&ReportCallback, &state);
683
684   // Sort buckets by decreasing leaked size
685   const int n = state.buckets_.size();
686   Entry* entries = new Entry[n];
687   int dst = 0;
688   for (map<Bucket*,Entry>::const_iterator iter = state.buckets_.begin();
689        iter != state.buckets_.end();
690        ++iter) {
691     entries[dst++] = iter->second;
692   }
693   sort(entries, entries + n);
694
695   // Report a bounded number of leaks to keep the leak report from
696   // growing too long.
697   const int to_report =
698       (FLAGS_heap_check_max_leaks > 0 &&
699        n > FLAGS_heap_check_max_leaks) ? FLAGS_heap_check_max_leaks : n;
700   RAW_LOG(ERROR, "The %d largest leaks:", to_report);
701
702   // Print
703   SymbolTable symbolization_table;
704   for (int i = 0; i < to_report; i++) {
705     const Entry& e = entries[i];
706     for (int j = 0; j < e.bucket->depth; j++) {
707       symbolization_table.Add(e.bucket->stack[j]);
708     }
709   }
710   static const int kBufSize = 2<<10;
711   char buffer[kBufSize];
712   if (should_symbolize)
713     symbolization_table.Symbolize();
714   for (int i = 0; i < to_report; i++) {
715     const Entry& e = entries[i];
716     base::RawPrinter printer(buffer, kBufSize);
717     printer.Printf("Leak of %d bytes in %d objects allocated from:\n",
718                    e.bytes, e.count);
719     for (int j = 0; j < e.bucket->depth; j++) {
720       const void* pc = e.bucket->stack[j];
721       printer.Printf("\t@ %" PRIxPTR " %s\n",
722           reinterpret_cast<uintptr_t>(pc), symbolization_table.GetSymbol(pc));
723     }
724     RAW_LOG(ERROR, "%s", buffer);
725   }
726
727   if (to_report < n) {
728     RAW_LOG(ERROR, "Skipping leaks numbered %d..%d",
729             to_report, n-1);
730   }
731   delete[] entries;
732
733   // TODO: Dump the sorted Entry list instead of dumping raw data?
734   // (should be much shorter)
735   if (!HeapProfileTable::WriteProfile(filename, total_, &map_)) {
736     RAW_LOG(ERROR, "Could not write pprof profile to %s", filename);
737   }
738 }
739
740 void HeapProfileTable::Snapshot::ReportObject(const void* ptr,
741                                               AllocValue* v,
742                                               char* unused) {
743   // Perhaps also log the allocation stack trace (unsymbolized)
744   // on this line in case somebody finds it useful.
745   RAW_LOG(ERROR, "leaked %" PRIuS " byte object %p", v->bytes, ptr);
746 }
747
748 void HeapProfileTable::Snapshot::ReportIndividualObjects() {
749   char unused;
750   map_.Iterate(ReportObject, &unused);
751 }