• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // ---
6 // Author: Sainbayar Sukhbaatar
7 //         Dai Mikurube
8 //
9 
10 #include "deep-heap-profile.h"
11 
12 #ifdef USE_DEEP_HEAP_PROFILE
13 #include <algorithm>
14 #include <fcntl.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <time.h>
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>  // for getpagesize and getpid
20 #endif  // HAVE_UNISTD_H
21 
22 #if defined(__linux__)
23 #include <endian.h>
24 #if !defined(__LITTLE_ENDIAN__) and !defined(__BIG_ENDIAN__)
25 #if __BYTE_ORDER == __BIG_ENDIAN
26 #define __BIG_ENDIAN__
27 #endif  // __BYTE_ORDER == __BIG_ENDIAN
28 #endif  // !defined(__LITTLE_ENDIAN__) and !defined(__BIG_ENDIAN__)
29 #if defined(__BIG_ENDIAN__)
30 #include <byteswap.h>
31 #endif  // defined(__BIG_ENDIAN__)
32 #endif  // defined(__linux__)
33 #if defined(COMPILER_MSVC)
34 #include <Winsock2.h>  // for gethostname
35 #endif  // defined(COMPILER_MSVC)
36 
37 #include "base/cycleclock.h"
38 #include "base/sysinfo.h"
39 #include "internal_logging.h"  // for ASSERT, etc
40 
41 static const int kProfilerBufferSize = 1 << 20;
42 static const int kHashTableSize = 179999;  // Same as heap-profile-table.cc.
43 
44 static const int PAGEMAP_BYTES = 8;
45 static const int KPAGECOUNT_BYTES = 8;
46 static const uint64 MAX_ADDRESS = kuint64max;
47 
48 // Tag strings in heap profile dumps.
49 static const char kProfileHeader[] = "heap profile: ";
50 static const char kProfileVersion[] = "DUMP_DEEP_6";
51 static const char kMetaInformationHeader[] = "META:\n";
52 static const char kMMapListHeader[] = "MMAP_LIST:\n";
53 static const char kGlobalStatsHeader[] = "GLOBAL_STATS:\n";
54 static const char kStacktraceHeader[] = "STACKTRACES:\n";
55 static const char kProcSelfMapsHeader[] = "\nMAPPED_LIBRARIES:\n";
56 
57 static const char kVirtualLabel[] = "virtual";
58 static const char kCommittedLabel[] = "committed";
59 
60 #if defined(__linux__)
61 #define OS_NAME "linux"
62 #elif defined(_WIN32) || defined(_WIN64)
63 #define OS_NAME "windows"
64 #else
65 #define OS_NAME "unknown-os"
66 #endif
67 
AppendCommandLine(TextBuffer * buffer)68 bool DeepHeapProfile::AppendCommandLine(TextBuffer* buffer) {
69 #if defined(__linux__)
70   RawFD fd;
71   char filename[100];
72   char cmdline[4096];
73   snprintf(filename, sizeof(filename), "/proc/%d/cmdline",
74            static_cast<int>(getpid()));
75   fd = open(filename, O_RDONLY);
76   if (fd == kIllegalRawFD) {
77     RAW_VLOG(0, "Failed to open /proc/self/cmdline");
78     return false;
79   }
80 
81   size_t length = read(fd, cmdline, sizeof(cmdline) - 1);
82   close(fd);
83 
84   for (int i = 0; i < length; ++i)
85     if (cmdline[i] == '\0')
86       cmdline[i] = ' ';
87   cmdline[length] = '\0';
88 
89   buffer->AppendString("CommandLine: ", 0);
90   buffer->AppendString(cmdline, 0);
91   buffer->AppendChar('\n');
92 
93   return true;
94 #else
95   return false;
96 #endif
97 }
98 
99 #if defined(_WIN32) || defined(_WIN64)
100 
101 // TODO(peria): Implement this function.
Initialize()102 void DeepHeapProfile::MemoryInfoGetterWindows::Initialize() {
103 }
104 
105 // TODO(peria): Implement this function.
CommittedSize(uint64 first_address,uint64 last_address,TextBuffer * buffer) const106 size_t DeepHeapProfile::MemoryInfoGetterWindows::CommittedSize(
107     uint64 first_address,
108     uint64 last_address,
109     TextBuffer* buffer) const {
110   return 0;
111 }
112 
113 // TODO(peria): Implement this function.
IsPageCountAvailable() const114 bool DeepHeapProfile::MemoryInfoGetterWindows::IsPageCountAvailable() const {
115   return false;
116 }
117 
118 #endif  // defined(_WIN32) || defined(_WIN64)
119 
120 #if defined(__linux__)
121 
Initialize()122 void DeepHeapProfile::MemoryInfoGetterLinux::Initialize() {
123   char filename[100];
124   snprintf(filename, sizeof(filename), "/proc/%d/pagemap",
125            static_cast<int>(getpid()));
126   pagemap_fd_ = open(filename, O_RDONLY);
127   RAW_CHECK(pagemap_fd_ != -1, "Failed to open /proc/self/pagemap");
128 
129   if (pageframe_type_ == DUMP_PAGECOUNT) {
130     snprintf(filename, sizeof(filename), "/proc/kpagecount");
131     kpagecount_fd_ = open(filename, O_RDONLY);
132     if (kpagecount_fd_ == -1)
133       RAW_VLOG(0, "Failed to open /proc/kpagecount");
134   }
135 }
136 
CommittedSize(uint64 first_address,uint64 last_address,DeepHeapProfile::TextBuffer * buffer) const137 size_t DeepHeapProfile::MemoryInfoGetterLinux::CommittedSize(
138     uint64 first_address,
139     uint64 last_address,
140     DeepHeapProfile::TextBuffer* buffer) const {
141   int page_size = getpagesize();
142   uint64 page_address = (first_address / page_size) * page_size;
143   size_t committed_size = 0;
144   size_t pageframe_list_length = 0;
145 
146   Seek(first_address);
147 
148   // Check every page on which the allocation resides.
149   while (page_address <= last_address) {
150     // Read corresponding physical page.
151     State state;
152     // TODO(dmikurube): Read pagemap in bulk for speed.
153     // TODO(dmikurube): Consider using mincore(2).
154     if (Read(&state, pageframe_type_ != DUMP_NO_PAGEFRAME) == false) {
155       // We can't read the last region (e.g vsyscall).
156 #ifndef NDEBUG
157       RAW_VLOG(0, "pagemap read failed @ %#llx %" PRId64 " bytes",
158               first_address, last_address - first_address + 1);
159 #endif
160       return 0;
161     }
162 
163     // Dump pageframes of resident pages.  Non-resident pages are just skipped.
164     if (pageframe_type_ != DUMP_NO_PAGEFRAME &&
165         buffer != NULL && state.pfn != 0) {
166       if (pageframe_list_length == 0) {
167         buffer->AppendString("  PF:", 0);
168         pageframe_list_length = 5;
169       }
170       buffer->AppendChar(' ');
171       if (page_address < first_address)
172         buffer->AppendChar('<');
173       buffer->AppendBase64(state.pfn, 4);
174       pageframe_list_length += 5;
175       if (pageframe_type_ == DUMP_PAGECOUNT && IsPageCountAvailable()) {
176         uint64 pagecount = ReadPageCount(state.pfn);
177         // Assume pagecount == 63 if the pageframe is mapped more than 63 times.
178         if (pagecount > 63)
179           pagecount = 63;
180         buffer->AppendChar('#');
181         buffer->AppendBase64(pagecount, 1);
182         pageframe_list_length += 2;
183       }
184       if (last_address < page_address - 1 + page_size)
185         buffer->AppendChar('>');
186       // Begins a new line every 94 characters.
187       if (pageframe_list_length > 94) {
188         buffer->AppendChar('\n');
189         pageframe_list_length = 0;
190       }
191     }
192 
193     if (state.is_committed) {
194       // Calculate the size of the allocation part in this page.
195       size_t bytes = page_size;
196 
197       // If looking at the last page in a given region.
198       if (last_address <= page_address - 1 + page_size) {
199         bytes = last_address - page_address + 1;
200       }
201 
202       // If looking at the first page in a given region.
203       if (page_address < first_address) {
204         bytes -= first_address - page_address;
205       }
206 
207       committed_size += bytes;
208     }
209     if (page_address > MAX_ADDRESS - page_size) {
210       break;
211     }
212     page_address += page_size;
213   }
214 
215   if (pageframe_type_ != DUMP_NO_PAGEFRAME &&
216       buffer != NULL && pageframe_list_length != 0) {
217     buffer->AppendChar('\n');
218   }
219 
220   return committed_size;
221 }
222 
ReadPageCount(uint64 pfn) const223 uint64 DeepHeapProfile::MemoryInfoGetterLinux::ReadPageCount(uint64 pfn) const {
224   int64 index = pfn * KPAGECOUNT_BYTES;
225   int64 offset = lseek64(kpagecount_fd_, index, SEEK_SET);
226   RAW_DCHECK(offset == index, "Failed in seeking in kpagecount.");
227 
228   uint64 kpagecount_value;
229   int result = read(kpagecount_fd_, &kpagecount_value, KPAGECOUNT_BYTES);
230   if (result != KPAGECOUNT_BYTES)
231     return 0;
232 
233   return kpagecount_value;
234 }
235 
Seek(uint64 address) const236 bool DeepHeapProfile::MemoryInfoGetterLinux::Seek(uint64 address) const {
237   int64 index = (address / getpagesize()) * PAGEMAP_BYTES;
238   RAW_DCHECK(pagemap_fd_ != -1, "Failed to seek in /proc/self/pagemap");
239   int64 offset = lseek64(pagemap_fd_, index, SEEK_SET);
240   RAW_DCHECK(offset == index, "Failed in seeking.");
241   return offset >= 0;
242 }
243 
Read(State * state,bool get_pfn) const244 bool DeepHeapProfile::MemoryInfoGetterLinux::Read(
245     State* state, bool get_pfn) const {
246   static const uint64 U64_1 = 1;
247   static const uint64 PFN_FILTER = (U64_1 << 55) - U64_1;
248   static const uint64 PAGE_PRESENT = U64_1 << 63;
249   static const uint64 PAGE_SWAP = U64_1 << 62;
250   static const uint64 PAGE_RESERVED = U64_1 << 61;
251   static const uint64 FLAG_NOPAGE = U64_1 << 20;
252   static const uint64 FLAG_KSM = U64_1 << 21;
253   static const uint64 FLAG_MMAP = U64_1 << 11;
254 
255   uint64 pagemap_value;
256   RAW_DCHECK(pagemap_fd_ != -1, "Failed to read from /proc/self/pagemap");
257   int result = read(pagemap_fd_, &pagemap_value, PAGEMAP_BYTES);
258   if (result != PAGEMAP_BYTES) {
259     return false;
260   }
261 
262   // Check if the page is committed.
263   state->is_committed = (pagemap_value & (PAGE_PRESENT | PAGE_SWAP));
264 
265   state->is_present = (pagemap_value & PAGE_PRESENT);
266   state->is_swapped = (pagemap_value & PAGE_SWAP);
267   state->is_shared = false;
268 
269   if (get_pfn && state->is_present && !state->is_swapped)
270     state->pfn = (pagemap_value & PFN_FILTER);
271   else
272     state->pfn = 0;
273 
274   return true;
275 }
276 
IsPageCountAvailable() const277 bool DeepHeapProfile::MemoryInfoGetterLinux::IsPageCountAvailable() const {
278   return kpagecount_fd_ != -1;
279 }
280 
281 #endif  // defined(__linux__)
282 
283 DeepHeapProfile::MemoryResidenceInfoGetterInterface::
MemoryResidenceInfoGetterInterface()284     MemoryResidenceInfoGetterInterface() {}
285 
286 DeepHeapProfile::MemoryResidenceInfoGetterInterface::
~MemoryResidenceInfoGetterInterface()287     ~MemoryResidenceInfoGetterInterface() {}
288 
289 DeepHeapProfile::MemoryResidenceInfoGetterInterface*
Create(PageFrameType pageframe_type)290     DeepHeapProfile::MemoryResidenceInfoGetterInterface::Create(
291         PageFrameType pageframe_type) {
292 #if defined(_WIN32) || defined(_WIN64)
293   return new MemoryInfoGetterWindows(pageframe_type);
294 #elif defined(__linux__)
295   return new MemoryInfoGetterLinux(pageframe_type);
296 #else
297   return NULL;
298 #endif
299 }
300 
DeepHeapProfile(HeapProfileTable * heap_profile,const char * prefix,enum PageFrameType pageframe_type)301 DeepHeapProfile::DeepHeapProfile(HeapProfileTable* heap_profile,
302                                  const char* prefix,
303                                  enum PageFrameType pageframe_type)
304     : memory_residence_info_getter_(
305           MemoryResidenceInfoGetterInterface::Create(pageframe_type)),
306       most_recent_pid_(-1),
307       stats_(),
308       dump_count_(0),
309       filename_prefix_(NULL),
310       deep_table_(kHashTableSize, heap_profile->alloc_, heap_profile->dealloc_),
311       pageframe_type_(pageframe_type),
312       heap_profile_(heap_profile) {
313   // Copy filename prefix.
314   const int prefix_length = strlen(prefix);
315   filename_prefix_ =
316       reinterpret_cast<char*>(heap_profile_->alloc_(prefix_length + 1));
317   memcpy(filename_prefix_, prefix, prefix_length);
318   filename_prefix_[prefix_length] = '\0';
319 
320   strncpy(run_id_, "undetermined-run-id", sizeof(run_id_));
321 }
322 
~DeepHeapProfile()323 DeepHeapProfile::~DeepHeapProfile() {
324   heap_profile_->dealloc_(filename_prefix_);
325   delete memory_residence_info_getter_;
326 }
327 
328 // Global malloc() should not be used in this function.
329 // Use LowLevelAlloc if required.
DumpOrderedProfile(const char * reason,char raw_buffer[],int buffer_size,RawFD fd)330 void DeepHeapProfile::DumpOrderedProfile(const char* reason,
331                                          char raw_buffer[],
332                                          int buffer_size,
333                                          RawFD fd) {
334   TextBuffer buffer(raw_buffer, buffer_size, fd);
335 
336 #ifndef NDEBUG
337   int64 starting_cycles = CycleClock::Now();
338 #endif
339 
340   // Get the time before starting snapshot.
341   // TODO(dmikurube): Consider gettimeofday if available.
342   time_t time_value = time(NULL);
343 
344   ++dump_count_;
345 
346   // Re-open files in /proc/pid/ if the process is newly forked one.
347   if (most_recent_pid_ != getpid()) {
348     char hostname[64];
349     if (0 == gethostname(hostname, sizeof(hostname))) {
350       char* dot = strchr(hostname, '.');
351       if (dot != NULL)
352         *dot = '\0';
353     } else {
354       strcpy(hostname, "unknown");
355     }
356 
357     most_recent_pid_ = getpid();
358 
359     snprintf(run_id_, sizeof(run_id_), "%s-" OS_NAME "-%d-%lu",
360              hostname, most_recent_pid_, time(NULL));
361 
362     if (memory_residence_info_getter_)
363       memory_residence_info_getter_->Initialize();
364     deep_table_.ResetIsLogged();
365 
366     // Write maps into "|filename_prefix_|.<pid>.maps".
367     WriteProcMaps(filename_prefix_, raw_buffer, buffer_size);
368   }
369 
370   // Reset committed sizes of buckets.
371   deep_table_.ResetCommittedSize();
372 
373   // Record committed sizes.
374   stats_.SnapshotAllocations(this);
375 
376   // TODO(dmikurube): Eliminate dynamic memory allocation caused by snprintf.
377   // glibc's snprintf internally allocates memory by alloca normally, but it
378   // allocates memory by malloc if large memory is required.
379 
380   buffer.AppendString(kProfileHeader, 0);
381   buffer.AppendString(kProfileVersion, 0);
382   buffer.AppendString("\n", 0);
383 
384   // Fill buffer with meta information.
385   buffer.AppendString(kMetaInformationHeader, 0);
386 
387   buffer.AppendString("Time: ", 0);
388   buffer.AppendUnsignedLong(time_value, 0);
389   buffer.AppendChar('\n');
390 
391   if (reason != NULL) {
392     buffer.AppendString("Reason: ", 0);
393     buffer.AppendString(reason, 0);
394     buffer.AppendChar('\n');
395   }
396 
397   AppendCommandLine(&buffer);
398 
399   buffer.AppendString("RunID: ", 0);
400   buffer.AppendString(run_id_, 0);
401   buffer.AppendChar('\n');
402 
403   buffer.AppendString("PageSize: ", 0);
404   buffer.AppendInt(getpagesize(), 0, 0);
405   buffer.AppendChar('\n');
406 
407   // Assumes the physical memory <= 64GB (PFN < 2^24).
408   if (pageframe_type_ == DUMP_PAGECOUNT && memory_residence_info_getter_ &&
409       memory_residence_info_getter_->IsPageCountAvailable()) {
410     buffer.AppendString("PageFrame: 24,Base64,PageCount", 0);
411     buffer.AppendChar('\n');
412   } else if (pageframe_type_ != DUMP_NO_PAGEFRAME) {
413     buffer.AppendString("PageFrame: 24,Base64", 0);
414     buffer.AppendChar('\n');
415   }
416 
417   // Fill buffer with the global stats.
418   buffer.AppendString(kMMapListHeader, 0);
419 
420   stats_.SnapshotMaps(memory_residence_info_getter_, this, &buffer);
421 
422   // Fill buffer with the global stats.
423   buffer.AppendString(kGlobalStatsHeader, 0);
424 
425   stats_.Unparse(&buffer);
426 
427   buffer.AppendString(kStacktraceHeader, 0);
428   buffer.AppendString(kVirtualLabel, 10);
429   buffer.AppendChar(' ');
430   buffer.AppendString(kCommittedLabel, 10);
431   buffer.AppendString("\n", 0);
432 
433   // Fill buffer.
434   deep_table_.UnparseForStats(&buffer);
435 
436   buffer.Flush();
437 
438   // Write the bucket listing into a .bucket file.
439   deep_table_.WriteForBucketFile(
440       filename_prefix_, dump_count_, raw_buffer, buffer_size);
441 
442 #ifndef NDEBUG
443   int64 elapsed_cycles = CycleClock::Now() - starting_cycles;
444   double elapsed_seconds = elapsed_cycles / CyclesPerSecond();
445   RAW_VLOG(0, "Time spent on DeepProfiler: %.3f sec\n", elapsed_seconds);
446 #endif
447 }
448 
Size()449 int DeepHeapProfile::TextBuffer::Size() {
450   return size_;
451 }
452 
FilledBytes()453 int DeepHeapProfile::TextBuffer::FilledBytes() {
454   return cursor_;
455 }
456 
Clear()457 void DeepHeapProfile::TextBuffer::Clear() {
458   cursor_ = 0;
459 }
460 
Flush()461 void DeepHeapProfile::TextBuffer::Flush() {
462   RawWrite(fd_, buffer_, cursor_);
463   cursor_ = 0;
464 }
465 
466 // TODO(dmikurube): These Append* functions should not use snprintf.
AppendChar(char value)467 bool DeepHeapProfile::TextBuffer::AppendChar(char value) {
468   return ForwardCursor(snprintf(buffer_ + cursor_, size_ - cursor_,
469                                 "%c", value));
470 }
471 
AppendString(const char * value,int width)472 bool DeepHeapProfile::TextBuffer::AppendString(const char* value, int width) {
473   char* position = buffer_ + cursor_;
474   int available = size_ - cursor_;
475   int appended;
476   if (width == 0)
477     appended = snprintf(position, available, "%s", value);
478   else
479     appended = snprintf(position, available, "%*s",
480                         width, value);
481   return ForwardCursor(appended);
482 }
483 
AppendInt(int value,int width,bool leading_zero)484 bool DeepHeapProfile::TextBuffer::AppendInt(int value, int width,
485                                             bool leading_zero) {
486   char* position = buffer_ + cursor_;
487   int available = size_ - cursor_;
488   int appended;
489   if (width == 0)
490     appended = snprintf(position, available, "%d", value);
491   else if (leading_zero)
492     appended = snprintf(position, available, "%0*d", width, value);
493   else
494     appended = snprintf(position, available, "%*d", width, value);
495   return ForwardCursor(appended);
496 }
497 
AppendLong(long value,int width)498 bool DeepHeapProfile::TextBuffer::AppendLong(long value, int width) {
499   char* position = buffer_ + cursor_;
500   int available = size_ - cursor_;
501   int appended;
502   if (width == 0)
503     appended = snprintf(position, available, "%ld", value);
504   else
505     appended = snprintf(position, available, "%*ld", width, value);
506   return ForwardCursor(appended);
507 }
508 
AppendUnsignedLong(unsigned long value,int width)509 bool DeepHeapProfile::TextBuffer::AppendUnsignedLong(unsigned long value,
510                                                      int width) {
511   char* position = buffer_ + cursor_;
512   int available = size_ - cursor_;
513   int appended;
514   if (width == 0)
515     appended = snprintf(position, available, "%lu", value);
516   else
517     appended = snprintf(position, available, "%*lu", width, value);
518   return ForwardCursor(appended);
519 }
520 
AppendInt64(int64 value,int width)521 bool DeepHeapProfile::TextBuffer::AppendInt64(int64 value, int width) {
522   char* position = buffer_ + cursor_;
523   int available = size_ - cursor_;
524   int appended;
525   if (width == 0)
526     appended = snprintf(position, available, "%" PRId64, value);
527   else
528     appended = snprintf(position, available, "%*" PRId64, width, value);
529   return ForwardCursor(appended);
530 }
531 
AppendPtr(uint64 value,int width)532 bool DeepHeapProfile::TextBuffer::AppendPtr(uint64 value, int width) {
533   char* position = buffer_ + cursor_;
534   int available = size_ - cursor_;
535   int appended;
536   if (width == 0)
537     appended = snprintf(position, available, "%" PRIx64, value);
538   else
539     appended = snprintf(position, available, "%0*" PRIx64, width, value);
540   return ForwardCursor(appended);
541 }
542 
AppendBase64(uint64 value,int width)543 bool DeepHeapProfile::TextBuffer::AppendBase64(uint64 value, int width) {
544   static const char base64[65] =
545       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
546 #if defined(__BIG_ENDIAN__)
547   value = bswap_64(value);
548 #endif
549   for (int shift = (width - 1) * 6; shift >= 0; shift -= 6) {
550     if (!AppendChar(base64[(value >> shift) & 0x3f]))
551       return false;
552   }
553   return true;
554 }
555 
ForwardCursor(int appended)556 bool DeepHeapProfile::TextBuffer::ForwardCursor(int appended) {
557   if (appended < 0 || appended >= size_ - cursor_)
558     return false;
559   cursor_ += appended;
560   if (cursor_ > size_ * 4 / 5)
561     Flush();
562   return true;
563 }
564 
UnparseForStats(TextBuffer * buffer)565 void DeepHeapProfile::DeepBucket::UnparseForStats(TextBuffer* buffer) {
566   buffer->AppendInt64(bucket->alloc_size - bucket->free_size, 10);
567   buffer->AppendChar(' ');
568   buffer->AppendInt64(committed_size, 10);
569   buffer->AppendChar(' ');
570   buffer->AppendInt(bucket->allocs, 6, false);
571   buffer->AppendChar(' ');
572   buffer->AppendInt(bucket->frees, 6, false);
573   buffer->AppendString(" @ ", 0);
574   buffer->AppendInt(id, 0, false);
575   buffer->AppendString("\n", 0);
576 }
577 
UnparseForBucketFile(TextBuffer * buffer)578 void DeepHeapProfile::DeepBucket::UnparseForBucketFile(TextBuffer* buffer) {
579   buffer->AppendInt(id, 0, false);
580   buffer->AppendChar(' ');
581   buffer->AppendString(is_mmap ? "mmap" : "malloc", 0);
582 
583 #if defined(TYPE_PROFILING)
584   buffer->AppendString(" t0x", 0);
585   buffer->AppendPtr(reinterpret_cast<uintptr_t>(type), 0);
586   if (type == NULL) {
587     buffer->AppendString(" nno_typeinfo", 0);
588   } else {
589     buffer->AppendString(" n", 0);
590     buffer->AppendString(type->name(), 0);
591   }
592 #endif
593 
594   for (int depth = 0; depth < bucket->depth; depth++) {
595     buffer->AppendString(" 0x", 0);
596     buffer->AppendPtr(reinterpret_cast<uintptr_t>(bucket->stack[depth]), 8);
597   }
598   buffer->AppendString("\n", 0);
599 }
600 
DeepBucketTable(int table_size,HeapProfileTable::Allocator alloc,HeapProfileTable::DeAllocator dealloc)601 DeepHeapProfile::DeepBucketTable::DeepBucketTable(
602     int table_size,
603     HeapProfileTable::Allocator alloc,
604     HeapProfileTable::DeAllocator dealloc)
605     : table_(NULL),
606       table_size_(table_size),
607       alloc_(alloc),
608       dealloc_(dealloc),
609       bucket_id_(0) {
610   const int bytes = table_size * sizeof(DeepBucket*);
611   table_ = reinterpret_cast<DeepBucket**>(alloc(bytes));
612   memset(table_, 0, bytes);
613 }
614 
~DeepBucketTable()615 DeepHeapProfile::DeepBucketTable::~DeepBucketTable() {
616   ASSERT(table_ != NULL);
617   for (int db = 0; db < table_size_; db++) {
618     for (DeepBucket* x = table_[db]; x != 0; /**/) {
619       DeepBucket* db = x;
620       x = x->next;
621       dealloc_(db);
622     }
623   }
624   dealloc_(table_);
625 }
626 
Lookup(Bucket * bucket,const std::type_info * type,bool is_mmap)627 DeepHeapProfile::DeepBucket* DeepHeapProfile::DeepBucketTable::Lookup(
628     Bucket* bucket,
629 #if defined(TYPE_PROFILING)
630     const std::type_info* type,
631 #endif
632     bool is_mmap) {
633   // Make hash-value
634   uintptr_t h = 0;
635 
636   AddToHashValue(reinterpret_cast<uintptr_t>(bucket), &h);
637   if (is_mmap) {
638     AddToHashValue(1, &h);
639   } else {
640     AddToHashValue(0, &h);
641   }
642 
643 #if defined(TYPE_PROFILING)
644   if (type == NULL) {
645     AddToHashValue(0, &h);
646   } else {
647     AddToHashValue(reinterpret_cast<uintptr_t>(type->name()), &h);
648   }
649 #endif
650 
651   FinishHashValue(&h);
652 
653   // Lookup stack trace in table
654   unsigned int buck = ((unsigned int) h) % table_size_;
655   for (DeepBucket* db = table_[buck]; db != 0; db = db->next) {
656     if (db->bucket == bucket) {
657       return db;
658     }
659   }
660 
661   // Create a new bucket
662   DeepBucket* db = reinterpret_cast<DeepBucket*>(alloc_(sizeof(DeepBucket)));
663   memset(db, 0, sizeof(*db));
664   db->bucket         = bucket;
665 #if defined(TYPE_PROFILING)
666   db->type           = type;
667 #endif
668   db->committed_size = 0;
669   db->is_mmap        = is_mmap;
670   db->id             = (bucket_id_++);
671   db->is_logged      = false;
672   db->next           = table_[buck];
673   table_[buck] = db;
674   return db;
675 }
676 
677 // TODO(dmikurube): Eliminate dynamic memory allocation caused by snprintf.
UnparseForStats(TextBuffer * buffer)678 void DeepHeapProfile::DeepBucketTable::UnparseForStats(TextBuffer* buffer) {
679   for (int i = 0; i < table_size_; i++) {
680     for (DeepBucket* deep_bucket = table_[i];
681          deep_bucket != NULL;
682          deep_bucket = deep_bucket->next) {
683       Bucket* bucket = deep_bucket->bucket;
684       if (bucket->alloc_size - bucket->free_size == 0) {
685         continue;  // Skip empty buckets.
686       }
687       deep_bucket->UnparseForStats(buffer);
688     }
689   }
690 }
691 
WriteForBucketFile(const char * prefix,int dump_count,char raw_buffer[],int buffer_size)692 void DeepHeapProfile::DeepBucketTable::WriteForBucketFile(
693     const char* prefix, int dump_count, char raw_buffer[], int buffer_size) {
694   char filename[100];
695   snprintf(filename, sizeof(filename),
696            "%s.%05d.%04d.buckets", prefix, getpid(), dump_count);
697   RawFD fd = RawOpenForWriting(filename);
698   RAW_DCHECK(fd != kIllegalRawFD, "");
699 
700   TextBuffer buffer(raw_buffer, buffer_size, fd);
701 
702   for (int i = 0; i < table_size_; i++) {
703     for (DeepBucket* deep_bucket = table_[i];
704          deep_bucket != NULL;
705          deep_bucket = deep_bucket->next) {
706       Bucket* bucket = deep_bucket->bucket;
707       if (deep_bucket->is_logged) {
708         continue;  // Skip the bucket if it is already logged.
709       }
710       if (!deep_bucket->is_mmap &&
711           bucket->alloc_size - bucket->free_size <= 64) {
712         continue;  // Skip small malloc buckets.
713       }
714 
715       deep_bucket->UnparseForBucketFile(&buffer);
716       deep_bucket->is_logged = true;
717     }
718   }
719 
720   buffer.Flush();
721   RawClose(fd);
722 }
723 
ResetCommittedSize()724 void DeepHeapProfile::DeepBucketTable::ResetCommittedSize() {
725   for (int i = 0; i < table_size_; i++) {
726     for (DeepBucket* deep_bucket = table_[i];
727          deep_bucket != NULL;
728          deep_bucket = deep_bucket->next) {
729       deep_bucket->committed_size = 0;
730     }
731   }
732 }
733 
ResetIsLogged()734 void DeepHeapProfile::DeepBucketTable::ResetIsLogged() {
735   for (int i = 0; i < table_size_; i++) {
736     for (DeepBucket* deep_bucket = table_[i];
737          deep_bucket != NULL;
738          deep_bucket = deep_bucket->next) {
739       deep_bucket->is_logged = false;
740     }
741   }
742 }
743 
744 // This hash function is from HeapProfileTable::GetBucket.
745 // static
AddToHashValue(uintptr_t add,uintptr_t * hash_value)746 void DeepHeapProfile::DeepBucketTable::AddToHashValue(
747     uintptr_t add, uintptr_t* hash_value) {
748   *hash_value += add;
749   *hash_value += *hash_value << 10;
750   *hash_value ^= *hash_value >> 6;
751 }
752 
753 // This hash function is from HeapProfileTable::GetBucket.
754 // static
FinishHashValue(uintptr_t * hash_value)755 void DeepHeapProfile::DeepBucketTable::FinishHashValue(uintptr_t* hash_value) {
756   *hash_value += *hash_value << 3;
757   *hash_value ^= *hash_value >> 11;
758 }
759 
Initialize()760 void DeepHeapProfile::RegionStats::Initialize() {
761   virtual_bytes_ = 0;
762   committed_bytes_ = 0;
763 }
764 
Record(const MemoryResidenceInfoGetterInterface * memory_residence_info_getter,uint64 first_address,uint64 last_address,TextBuffer * buffer)765 uint64 DeepHeapProfile::RegionStats::Record(
766     const MemoryResidenceInfoGetterInterface* memory_residence_info_getter,
767     uint64 first_address,
768     uint64 last_address,
769     TextBuffer* buffer) {
770   uint64 committed = 0;
771   virtual_bytes_ += static_cast<size_t>(last_address - first_address + 1);
772   if (memory_residence_info_getter)
773     committed = memory_residence_info_getter->CommittedSize(first_address,
774                                                             last_address,
775                                                             buffer);
776   committed_bytes_ += committed;
777   return committed;
778 }
779 
Unparse(const char * name,TextBuffer * buffer)780 void DeepHeapProfile::RegionStats::Unparse(const char* name,
781                                            TextBuffer* buffer) {
782   buffer->AppendString(name, 25);
783   buffer->AppendChar(' ');
784   buffer->AppendLong(virtual_bytes_, 12);
785   buffer->AppendChar(' ');
786   buffer->AppendLong(committed_bytes_, 12);
787   buffer->AppendString("\n", 0);
788 }
789 
790 // Snapshots all virtual memory mapping stats by merging mmap(2) records from
791 // MemoryRegionMap and /proc/maps, the OS-level memory mapping information.
792 // Memory regions described in /proc/maps, but which are not created by mmap,
793 // are accounted as "unhooked" memory regions.
794 //
795 // This function assumes that every memory region created by mmap is covered
796 // by VMA(s) described in /proc/maps except for http://crbug.com/189114.
797 // Note that memory regions created with mmap don't align with borders of VMAs
798 // in /proc/maps.  In other words, a memory region by mmap can cut across many
799 // VMAs.  Also, of course a VMA can include many memory regions by mmap.
800 // It means that the following situation happens:
801 //
802 // => Virtual address
803 // <----- VMA #1 -----><----- VMA #2 ----->...<----- VMA #3 -----><- VMA #4 ->
804 // ..< mmap #1 >.<- mmap #2 -><- mmap #3 ->...<- mmap #4 ->..<-- mmap #5 -->..
805 //
806 // It can happen easily as permission can be changed by mprotect(2) for a part
807 // of a memory region.  A change in permission splits VMA(s).
808 //
809 // To deal with the situation, this function iterates over MemoryRegionMap and
810 // /proc/maps independently.  The iterator for MemoryRegionMap is initialized
811 // at the top outside the loop for /proc/maps, and it goes forward inside the
812 // loop while comparing their addresses.
813 //
814 // TODO(dmikurube): Eliminate dynamic memory allocation caused by snprintf.
SnapshotMaps(const MemoryResidenceInfoGetterInterface * memory_residence_info_getter,DeepHeapProfile * deep_profile,TextBuffer * mmap_dump_buffer)815 void DeepHeapProfile::GlobalStats::SnapshotMaps(
816     const MemoryResidenceInfoGetterInterface* memory_residence_info_getter,
817     DeepHeapProfile* deep_profile,
818     TextBuffer* mmap_dump_buffer) {
819   MemoryRegionMap::LockHolder lock_holder;
820   ProcMapsIterator::Buffer procmaps_iter_buffer;
821   ProcMapsIterator procmaps_iter(0, &procmaps_iter_buffer);
822   uint64 vma_start_addr, vma_last_addr, offset;
823   int64 inode;
824   char* flags;
825   char* filename;
826   enum MapsRegionType type;
827 
828   for (int i = 0; i < NUMBER_OF_MAPS_REGION_TYPES; ++i) {
829     all_[i].Initialize();
830     unhooked_[i].Initialize();
831   }
832   profiled_mmap_.Initialize();
833 
834   MemoryRegionMap::RegionIterator mmap_iter =
835       MemoryRegionMap::BeginRegionLocked();
836   DeepBucket* deep_bucket = NULL;
837   if (mmap_iter != MemoryRegionMap::EndRegionLocked()) {
838     deep_bucket = GetInformationOfMemoryRegion(
839         mmap_iter, memory_residence_info_getter, deep_profile);
840   }
841 
842   while (procmaps_iter.Next(&vma_start_addr, &vma_last_addr,
843                             &flags, &offset, &inode, &filename)) {
844     if (mmap_dump_buffer) {
845       char buffer[1024];
846       int written = procmaps_iter.FormatLine(buffer, sizeof(buffer),
847                                              vma_start_addr, vma_last_addr,
848                                              flags, offset, inode, filename, 0);
849       mmap_dump_buffer->AppendString(buffer, 0);
850     }
851 
852     // 'vma_last_addr' should be the last inclusive address of the region.
853     vma_last_addr -= 1;
854     if (strcmp("[vsyscall]", filename) == 0) {
855       continue;  // Reading pagemap will fail in [vsyscall].
856     }
857 
858     // TODO(dmikurube): |type| will be deprecated in the dump.
859     // See http://crbug.com/245603.
860     type = ABSENT;
861     if (filename[0] == '/') {
862       if (flags[2] == 'x')
863         type = FILE_EXEC;
864       else
865         type = FILE_NONEXEC;
866     } else if (filename[0] == '\0' || filename[0] == '\n') {
867       type = ANONYMOUS;
868     } else if (strcmp(filename, "[stack]") == 0) {
869       type = STACK;
870     } else {
871       type = OTHER;
872     }
873     // TODO(dmikurube): This |all_| count should be removed in future soon.
874     // See http://crbug.com/245603.
875     uint64 vma_total = all_[type].Record(
876         memory_residence_info_getter, vma_start_addr, vma_last_addr, NULL);
877     uint64 vma_subtotal = 0;
878 
879     // TODO(dmikurube): Stop double-counting pagemap.
880     // It will be fixed when http://crbug.com/245603 finishes.
881     if (MemoryRegionMap::IsRecordingLocked()) {
882       uint64 cursor = vma_start_addr;
883       bool first = true;
884 
885       // Iterates over MemoryRegionMap until the iterator moves out of the VMA.
886       do {
887         if (!first) {
888           cursor = mmap_iter->end_addr;
889           ++mmap_iter;
890           // Don't break here even if mmap_iter == EndRegionLocked().
891 
892           if (mmap_iter != MemoryRegionMap::EndRegionLocked()) {
893             deep_bucket = GetInformationOfMemoryRegion(
894                 mmap_iter, memory_residence_info_getter, deep_profile);
895           }
896         }
897         first = false;
898 
899         uint64 last_address_of_unhooked;
900         // If the next mmap entry is away from the current VMA.
901         if (mmap_iter == MemoryRegionMap::EndRegionLocked() ||
902             mmap_iter->start_addr > vma_last_addr) {
903           last_address_of_unhooked = vma_last_addr;
904         } else {
905           last_address_of_unhooked = mmap_iter->start_addr - 1;
906         }
907 
908         if (last_address_of_unhooked + 1 > cursor) {
909           RAW_CHECK(cursor >= vma_start_addr,
910                     "Wrong calculation for unhooked");
911           RAW_CHECK(last_address_of_unhooked <= vma_last_addr,
912                     "Wrong calculation for unhooked");
913           uint64 committed_size = unhooked_[type].Record(
914               memory_residence_info_getter,
915               cursor,
916               last_address_of_unhooked,
917               mmap_dump_buffer);
918           vma_subtotal += committed_size;
919           if (mmap_dump_buffer) {
920             mmap_dump_buffer->AppendString("  ", 0);
921             mmap_dump_buffer->AppendPtr(cursor, 0);
922             mmap_dump_buffer->AppendString(" - ", 0);
923             mmap_dump_buffer->AppendPtr(last_address_of_unhooked + 1, 0);
924             mmap_dump_buffer->AppendString("  unhooked ", 0);
925             mmap_dump_buffer->AppendInt64(committed_size, 0);
926             mmap_dump_buffer->AppendString(" / ", 0);
927             mmap_dump_buffer->AppendInt64(
928                 last_address_of_unhooked - cursor + 1, 0);
929             mmap_dump_buffer->AppendString("\n", 0);
930           }
931           cursor = last_address_of_unhooked + 1;
932         }
933 
934         if (mmap_iter != MemoryRegionMap::EndRegionLocked() &&
935             mmap_iter->start_addr <= vma_last_addr &&
936             mmap_dump_buffer) {
937           bool trailing = mmap_iter->start_addr < vma_start_addr;
938           bool continued = mmap_iter->end_addr - 1 > vma_last_addr;
939           uint64 partial_first_address, partial_last_address;
940           if (trailing)
941             partial_first_address = vma_start_addr;
942           else
943             partial_first_address = mmap_iter->start_addr;
944           if (continued)
945             partial_last_address = vma_last_addr;
946           else
947             partial_last_address = mmap_iter->end_addr - 1;
948           uint64 committed_size = 0;
949           if (memory_residence_info_getter)
950             committed_size = memory_residence_info_getter->CommittedSize(
951                 partial_first_address, partial_last_address, mmap_dump_buffer);
952           vma_subtotal += committed_size;
953           mmap_dump_buffer->AppendString(trailing ? " (" : "  ", 0);
954           mmap_dump_buffer->AppendPtr(mmap_iter->start_addr, 0);
955           mmap_dump_buffer->AppendString(trailing ? ")" : " ", 0);
956           mmap_dump_buffer->AppendString("-", 0);
957           mmap_dump_buffer->AppendString(continued ? "(" : " ", 0);
958           mmap_dump_buffer->AppendPtr(mmap_iter->end_addr, 0);
959           mmap_dump_buffer->AppendString(continued ? ")" : " ", 0);
960           mmap_dump_buffer->AppendString(" hooked ", 0);
961           mmap_dump_buffer->AppendInt64(committed_size, 0);
962           mmap_dump_buffer->AppendString(" / ", 0);
963           mmap_dump_buffer->AppendInt64(
964               partial_last_address - partial_first_address + 1, 0);
965           mmap_dump_buffer->AppendString(" @ ", 0);
966           if (deep_bucket != NULL) {
967             mmap_dump_buffer->AppendInt(deep_bucket->id, 0, false);
968           } else {
969             mmap_dump_buffer->AppendInt(0, 0, false);
970           }
971           mmap_dump_buffer->AppendString("\n", 0);
972         }
973       } while (mmap_iter != MemoryRegionMap::EndRegionLocked() &&
974                mmap_iter->end_addr - 1 <= vma_last_addr);
975     }
976 
977     if (vma_total != vma_subtotal) {
978       char buffer[1024];
979       int written = procmaps_iter.FormatLine(buffer, sizeof(buffer),
980                                              vma_start_addr, vma_last_addr,
981                                              flags, offset, inode, filename, 0);
982       RAW_VLOG(0, "[%d] Mismatched total in VMA %" PRId64 ":"
983               "%" PRId64 " (%" PRId64 ")",
984               getpid(), vma_total, vma_subtotal, vma_total - vma_subtotal);
985       RAW_VLOG(0, "[%d]   in %s", getpid(), buffer);
986     }
987   }
988 
989   // TODO(dmikurube): Investigate and fix http://crbug.com/189114.
990   //
991   // The total committed memory usage in all_ (from /proc/<pid>/maps) is
992   // sometimes smaller than the sum of the committed mmap'ed addresses and
993   // unhooked regions.  Within our observation, the difference was only 4KB
994   // in committed usage, zero in reserved virtual addresses
995   //
996   // A guess is that an uncommitted (but reserved) page may become committed
997   // during counting memory usage in the loop above.
998   //
999   // The difference is accounted as "ABSENT" to investigate such cases.
1000   //
1001   // It will be fixed when http://crbug.com/245603 finishes (no double count).
1002 
1003   RegionStats all_total;
1004   RegionStats unhooked_total;
1005   for (int i = 0; i < NUMBER_OF_MAPS_REGION_TYPES; ++i) {
1006     all_total.AddAnotherRegionStat(all_[i]);
1007     unhooked_total.AddAnotherRegionStat(unhooked_[i]);
1008   }
1009 
1010   size_t absent_virtual = profiled_mmap_.virtual_bytes() +
1011                           unhooked_total.virtual_bytes() -
1012                           all_total.virtual_bytes();
1013   if (absent_virtual > 0)
1014     all_[ABSENT].AddToVirtualBytes(absent_virtual);
1015 
1016   size_t absent_committed = profiled_mmap_.committed_bytes() +
1017                             unhooked_total.committed_bytes() -
1018                             all_total.committed_bytes();
1019   if (absent_committed > 0)
1020     all_[ABSENT].AddToCommittedBytes(absent_committed);
1021 }
1022 
SnapshotAllocations(DeepHeapProfile * deep_profile)1023 void DeepHeapProfile::GlobalStats::SnapshotAllocations(
1024     DeepHeapProfile* deep_profile) {
1025   profiled_malloc_.Initialize();
1026 
1027   deep_profile->heap_profile_->address_map_->Iterate(RecordAlloc, deep_profile);
1028 }
1029 
Unparse(TextBuffer * buffer)1030 void DeepHeapProfile::GlobalStats::Unparse(TextBuffer* buffer) {
1031   RegionStats all_total;
1032   RegionStats unhooked_total;
1033   for (int i = 0; i < NUMBER_OF_MAPS_REGION_TYPES; ++i) {
1034     all_total.AddAnotherRegionStat(all_[i]);
1035     unhooked_total.AddAnotherRegionStat(unhooked_[i]);
1036   }
1037 
1038   // "# total (%lu) %c= profiled-mmap (%lu) + nonprofiled-* (%lu)\n"
1039   buffer->AppendString("# total (", 0);
1040   buffer->AppendUnsignedLong(all_total.committed_bytes(), 0);
1041   buffer->AppendString(") ", 0);
1042   buffer->AppendChar(all_total.committed_bytes() ==
1043                      profiled_mmap_.committed_bytes() +
1044                      unhooked_total.committed_bytes() ? '=' : '!');
1045   buffer->AppendString("= profiled-mmap (", 0);
1046   buffer->AppendUnsignedLong(profiled_mmap_.committed_bytes(), 0);
1047   buffer->AppendString(") + nonprofiled-* (", 0);
1048   buffer->AppendUnsignedLong(unhooked_total.committed_bytes(), 0);
1049   buffer->AppendString(")\n", 0);
1050 
1051   // "                               virtual    committed"
1052   buffer->AppendString("", 26);
1053   buffer->AppendString(kVirtualLabel, 12);
1054   buffer->AppendChar(' ');
1055   buffer->AppendString(kCommittedLabel, 12);
1056   buffer->AppendString("\n", 0);
1057 
1058   all_total.Unparse("total", buffer);
1059   all_[ABSENT].Unparse("absent", buffer);
1060   all_[FILE_EXEC].Unparse("file-exec", buffer);
1061   all_[FILE_NONEXEC].Unparse("file-nonexec", buffer);
1062   all_[ANONYMOUS].Unparse("anonymous", buffer);
1063   all_[STACK].Unparse("stack", buffer);
1064   all_[OTHER].Unparse("other", buffer);
1065   unhooked_total.Unparse("nonprofiled-total", buffer);
1066   unhooked_[ABSENT].Unparse("nonprofiled-absent", buffer);
1067   unhooked_[ANONYMOUS].Unparse("nonprofiled-anonymous", buffer);
1068   unhooked_[FILE_EXEC].Unparse("nonprofiled-file-exec", buffer);
1069   unhooked_[FILE_NONEXEC].Unparse("nonprofiled-file-nonexec", buffer);
1070   unhooked_[STACK].Unparse("nonprofiled-stack", buffer);
1071   unhooked_[OTHER].Unparse("nonprofiled-other", buffer);
1072   profiled_mmap_.Unparse("profiled-mmap", buffer);
1073   profiled_malloc_.Unparse("profiled-malloc", buffer);
1074 }
1075 
1076 // static
RecordAlloc(const void * pointer,AllocValue * alloc_value,DeepHeapProfile * deep_profile)1077 void DeepHeapProfile::GlobalStats::RecordAlloc(const void* pointer,
1078                                                AllocValue* alloc_value,
1079                                                DeepHeapProfile* deep_profile) {
1080   uint64 address = reinterpret_cast<uintptr_t>(pointer);
1081   size_t committed = deep_profile->memory_residence_info_getter_->CommittedSize(
1082       address, address + alloc_value->bytes - 1, NULL);
1083 
1084   DeepBucket* deep_bucket = deep_profile->deep_table_.Lookup(
1085       alloc_value->bucket(),
1086 #if defined(TYPE_PROFILING)
1087       LookupType(pointer),
1088 #endif
1089       /* is_mmap */ false);
1090   deep_bucket->committed_size += committed;
1091   deep_profile->stats_.profiled_malloc_.AddToVirtualBytes(alloc_value->bytes);
1092   deep_profile->stats_.profiled_malloc_.AddToCommittedBytes(committed);
1093 }
1094 
1095 DeepHeapProfile::DeepBucket*
GetInformationOfMemoryRegion(const MemoryRegionMap::RegionIterator & mmap_iter,const MemoryResidenceInfoGetterInterface * memory_residence_info_getter,DeepHeapProfile * deep_profile)1096     DeepHeapProfile::GlobalStats::GetInformationOfMemoryRegion(
1097         const MemoryRegionMap::RegionIterator& mmap_iter,
1098         const MemoryResidenceInfoGetterInterface* memory_residence_info_getter,
1099         DeepHeapProfile* deep_profile) {
1100   size_t committed = deep_profile->memory_residence_info_getter_->
1101       CommittedSize(mmap_iter->start_addr, mmap_iter->end_addr - 1, NULL);
1102 
1103   // TODO(dmikurube): Store a reference to the bucket in region.
1104   Bucket* bucket = MemoryRegionMap::GetBucket(
1105       mmap_iter->call_stack_depth, mmap_iter->call_stack);
1106   DeepBucket* deep_bucket = NULL;
1107   if (bucket != NULL) {
1108     deep_bucket = deep_profile->deep_table_.Lookup(
1109         bucket,
1110 #if defined(TYPE_PROFILING)
1111         NULL,  // No type information for memory regions by mmap.
1112 #endif
1113         /* is_mmap */ true);
1114     if (deep_bucket != NULL)
1115       deep_bucket->committed_size += committed;
1116   }
1117 
1118   profiled_mmap_.AddToVirtualBytes(
1119       mmap_iter->end_addr - mmap_iter->start_addr);
1120   profiled_mmap_.AddToCommittedBytes(committed);
1121 
1122   return deep_bucket;
1123 }
1124 
1125 // static
WriteProcMaps(const char * prefix,char raw_buffer[],int buffer_size)1126 void DeepHeapProfile::WriteProcMaps(const char* prefix,
1127                                     char raw_buffer[],
1128                                     int buffer_size) {
1129   char filename[100];
1130   snprintf(filename, sizeof(filename),
1131            "%s.%05d.maps", prefix, static_cast<int>(getpid()));
1132 
1133   RawFD fd = RawOpenForWriting(filename);
1134   RAW_DCHECK(fd != kIllegalRawFD, "");
1135 
1136   int length;
1137   bool wrote_all;
1138   length = tcmalloc::FillProcSelfMaps(raw_buffer, buffer_size, &wrote_all);
1139   RAW_DCHECK(wrote_all, "");
1140   RAW_DCHECK(length <= buffer_size, "");
1141   RawWrite(fd, raw_buffer, length);
1142   RawClose(fd);
1143 }
1144 #else  // USE_DEEP_HEAP_PROFILE
1145 
DeepHeapProfile(HeapProfileTable * heap_profile,const char * prefix,enum PageFrameType pageframe_type)1146 DeepHeapProfile::DeepHeapProfile(HeapProfileTable* heap_profile,
1147                                  const char* prefix,
1148                                  enum PageFrameType pageframe_type)
1149     : heap_profile_(heap_profile) {
1150 }
1151 
~DeepHeapProfile()1152 DeepHeapProfile::~DeepHeapProfile() {
1153 }
1154 
DumpOrderedProfile(const char * reason,char raw_buffer[],int buffer_size,RawFD fd)1155 void DeepHeapProfile::DumpOrderedProfile(const char* reason,
1156                                          char raw_buffer[],
1157                                          int buffer_size,
1158                                          RawFD fd) {
1159 }
1160 
1161 #endif  // USE_DEEP_HEAP_PROFILE
1162