Index: third_party/tcmalloc/chromium/src/deep-heap-profile.cc |
diff --git a/third_party/tcmalloc/chromium/src/deep-heap-profile.cc b/third_party/tcmalloc/chromium/src/deep-heap-profile.cc |
index 97c874ac3cc669d952969cc0c9528f0500c8493a..dc96f9cac3582584e67c14f7ee567affda8f8456 100644 |
--- a/third_party/tcmalloc/chromium/src/deep-heap-profile.cc |
+++ b/third_party/tcmalloc/chromium/src/deep-heap-profile.cc |
@@ -10,6 +10,7 @@ |
#include "deep-heap-profile.h" |
#ifdef DEEP_HEAP_PROFILE |
+#include <algorithm> |
#include <fcntl.h> |
#include <sys/stat.h> |
#include <sys/types.h> |
@@ -28,7 +29,7 @@ static const uint64 MAX_ADDRESS = kuint64max; |
// Header strings of the dumped heap profile. |
static const char kProfileHeader[] = "heap profile: "; |
-static const char kProfileVersion[] = "DUMP_DEEP_3"; |
+static const char kProfileVersion[] = "DUMP_DEEP_4"; |
static const char kGlobalStatsHeader[] = "GLOBAL_STATS:\n"; |
static const char kMMapStacktraceHeader[] = "MMAP_STACKTRACES:\n"; |
static const char kAllocStacktraceHeader[] = "MALLOC_STACKTRACES:\n"; |
@@ -89,8 +90,23 @@ int DeepHeapProfile::FillOrderedProfile(char buffer[], int buffer_size) { |
ResetCommittedSize(heap_profile_->alloc_table_); |
ResetCommittedSize(heap_profile_->mmap_table_); |
- SnapshotGlobalStatsWithoutMalloc(pagemap_fd_, &stats_); |
- size_t anonymous_committed = stats_.anonymous.committed_bytes(); |
+ // Allocate a list for mmap'ed regions. |
+ num_mmap_allocations_ = 0; |
+ heap_profile_->mmap_address_map_->Iterate(CountMMap, this); |
+ mmap_list_length_ = 0; |
+ mmap_list_ = reinterpret_cast<MMapListEntry*>(heap_profile_->alloc_( |
+ sizeof(MMapListEntry) * num_mmap_allocations_)); |
+ |
+ // Touch all the allocated pages. Touching is required to avoid new page |
+ // commitment while filling the list in SnapshotGlobalStatsWithoutMalloc. |
+ for (int i = 0; |
+ i < num_mmap_allocations_; |
+ i += getpagesize() / 2 / sizeof(MMapListEntry)) |
+ mmap_list_[i].first_address = 0; |
+ mmap_list_[num_mmap_allocations_ - 1].last_address = 0; |
+ |
+ SnapshotGlobalStatsWithoutMalloc(pagemap_fd_, &stats_, NULL, 0); |
+ size_t anonymous_committed = stats_.all[ANONYMOUS].committed_bytes(); |
// Note: Try to minimize the number of calls to malloc in the following |
// region, up until we call WriteBucketsToBucketFile(), near the end of this |
@@ -106,10 +122,11 @@ int DeepHeapProfile::FillOrderedProfile(char buffer[], int buffer_size) { |
SnapshotAllAllocsWithoutMalloc(); |
// Check if committed bytes changed during SnapshotAllAllocsWithoutMalloc. |
- SnapshotGlobalStatsWithoutMalloc(pagemap_fd_, &stats_); |
+ SnapshotGlobalStatsWithoutMalloc(pagemap_fd_, &stats_, |
+ mmap_list_, mmap_list_length_); |
#ifndef NDEBUG |
size_t committed_difference = |
- stats_.anonymous.committed_bytes() - anonymous_committed; |
+ stats_.all[ANONYMOUS].committed_bytes() - anonymous_committed; |
if (committed_difference != 0) { |
RAW_LOG(0, "Difference in committed size: %ld", committed_difference); |
} |
@@ -179,6 +196,9 @@ int DeepHeapProfile::FillOrderedProfile(char buffer[], int buffer_size) { |
// Note: Memory snapshots are complete, and malloc may again be used freely. |
+ heap_profile_->dealloc_(mmap_list_); |
+ mmap_list_ = NULL; |
+ |
// Write the bucket listing into a .bucket file. |
WriteBucketsToBucketFile(); |
@@ -276,6 +296,7 @@ size_t DeepHeapProfile::GetCommittedSize( |
while (page_address <= last_address) { |
// Read corresponding physical page. |
PageState state; |
+ // TODO(dmikurube): Read pagemap in bulk for speed. |
if (ReadProcPagemap(pagemap_fd, &state) == false) { |
// We can't read the last region (e.g vsyscall). |
#ifndef NDEBUG |
@@ -334,18 +355,22 @@ void DeepHeapProfile::WriteMapsToFile(const char* filename_prefix, |
// ProcMapsIterator uses snprintf internally in construction. |
// static |
void DeepHeapProfile::SnapshotGlobalStatsWithoutMalloc(int pagemap_fd, |
- GlobalStats* stats) { |
+ GlobalStats* stats, |
+ MMapListEntry* mmap_list, |
+ int mmap_list_length) { |
ProcMapsIterator::Buffer iterator_buffer; |
ProcMapsIterator iterator(0, &iterator_buffer); |
uint64 first_address, last_address, offset; |
int64 unused_inode; |
char* flags; |
char* filename; |
+ int mmap_list_index = 0; |
+ enum MapsRegionType type; |
- stats->total.Initialize(); |
- stats->file_mapped.Initialize(); |
- stats->anonymous.Initialize(); |
- stats->other.Initialize(); |
+ for (int i = 0; i < NUMBER_OF_MAPS_REGION_TYPES; ++i) { |
+ stats->all[i].Initialize(); |
+ stats->nonprofiled[i].Initialize(); |
+ } |
while (iterator.Next(&first_address, &last_address, |
&flags, &offset, &unused_inode, &filename)) { |
@@ -355,14 +380,53 @@ void DeepHeapProfile::SnapshotGlobalStatsWithoutMalloc(int pagemap_fd, |
continue; // Reading pagemap will fail in [vsyscall]. |
} |
- stats->total.Record(pagemap_fd, first_address, last_address); |
- |
+ type = ABSENT; |
if (filename[0] == '/') { |
- stats->file_mapped.Record(pagemap_fd, first_address, last_address); |
+ if (flags[2] == 'x') |
+ type = FILE_EXEC; |
+ else |
+ type = FILE_NONEXEC; |
} else if (filename[0] == '\0' || filename[0] == '\n') { |
- stats->anonymous.Record(pagemap_fd, first_address, last_address); |
+ type = ANONYMOUS; |
+ } else if (strcmp(filename, "[stack]") == 0) { |
+ type = STACK; |
} else { |
- stats->other.Record(pagemap_fd, first_address, last_address); |
+ type = OTHER; |
+ } |
+ stats->all[type].Record(pagemap_fd, first_address, last_address); |
+ |
+ // TODO(dmikurube): Avoid double-counting of pagemap. |
+ // Counts nonprofiled memory regions in /proc/<pid>/maps. |
+ if (mmap_list != NULL) { |
+ // It assumes that every mmap'ed region is included in one maps line. |
+ uint64 cursor = first_address; |
+ bool first = true; |
+ |
+ do { |
+ if (!first) { |
+ mmap_list[mmap_list_index].type = type; |
+ cursor = mmap_list[mmap_list_index].last_address + 1; |
+ ++mmap_list_index; |
+ } |
+ first = false; |
+ |
+ uint64 last_address_of_nonprofiled; |
+ // If the next mmap entry is away from the current maps line. |
+ if (mmap_list_index >= mmap_list_length || |
+ mmap_list[mmap_list_index].first_address > last_address) { |
+ last_address_of_nonprofiled = last_address; |
+ } else { |
+ last_address_of_nonprofiled = |
+ mmap_list[mmap_list_index].first_address - 1; |
+ } |
+ |
+ if (last_address_of_nonprofiled + 1 > cursor) { |
+ stats->nonprofiled[type].Record( |
+ pagemap_fd, cursor, last_address_of_nonprofiled); |
+ cursor = last_address_of_nonprofiled + 1; |
+ } |
+ } while (mmap_list_index < mmap_list_length && |
+ mmap_list[mmap_list_index].last_address <= last_address); |
} |
} |
} |
@@ -412,6 +476,19 @@ int DeepHeapProfile::SnapshotBucketTableWithoutMalloc(Bucket** bucket_table, |
return used_in_buffer; |
} |
+// static |
+bool DeepHeapProfile::ByFirstAddress(const MMapListEntry& a, |
+ const MMapListEntry& b) { |
+ return a.first_address < b.first_address; |
+} |
+ |
+// static |
+void DeepHeapProfile::CountMMap(const void* pointer, |
+ AllocValue* alloc_value, |
+ DeepHeapProfile* deep_profile) { |
+ ++deep_profile->num_mmap_allocations_; |
+} |
+ |
void DeepHeapProfile::RecordAlloc(const void* pointer, |
AllocValue* alloc_value, |
DeepHeapProfile* deep_profile) { |
@@ -421,8 +498,8 @@ void DeepHeapProfile::RecordAlloc(const void* pointer, |
DeepBucket* deep_bucket = deep_profile->GetDeepBucket(alloc_value->bucket()); |
deep_bucket->committed_size += committed; |
- deep_profile->stats_.record_malloc.AddToVirtualBytes(alloc_value->bytes); |
- deep_profile->stats_.record_malloc.AddToCommittedBytes(committed); |
+ deep_profile->stats_.profiled_malloc.AddToVirtualBytes(alloc_value->bytes); |
+ deep_profile->stats_.profiled_malloc.AddToCommittedBytes(committed); |
} |
void DeepHeapProfile::RecordMMap(const void* pointer, |
@@ -434,19 +511,33 @@ void DeepHeapProfile::RecordMMap(const void* pointer, |
DeepBucket* deep_bucket = deep_profile->GetDeepBucket(alloc_value->bucket()); |
deep_bucket->committed_size += committed; |
- deep_profile->stats_.record_mmap.AddToVirtualBytes(alloc_value->bytes); |
- deep_profile->stats_.record_mmap.AddToCommittedBytes(committed); |
+ deep_profile->stats_.profiled_mmap.AddToVirtualBytes(alloc_value->bytes); |
+ deep_profile->stats_.profiled_mmap.AddToCommittedBytes(committed); |
+ |
+ if (deep_profile->mmap_list_length_ < deep_profile->num_mmap_allocations_) { |
+ deep_profile->mmap_list_[deep_profile->mmap_list_length_].first_address = |
+ address; |
+ deep_profile->mmap_list_[deep_profile->mmap_list_length_].last_address = |
+ address - 1 + alloc_value->bytes; |
+ deep_profile->mmap_list_[deep_profile->mmap_list_length_].type = ABSENT; |
+ ++deep_profile->mmap_list_length_; |
+ } else { |
+ RAW_LOG(0, "Unexpected number of mmap entries: %d/%d", |
+ deep_profile->mmap_list_length_, |
+ deep_profile->num_mmap_allocations_); |
+ } |
} |
void DeepHeapProfile::SnapshotAllAllocsWithoutMalloc() { |
- stats_.record_mmap.Initialize(); |
- stats_.record_malloc.Initialize(); |
+ stats_.profiled_mmap.Initialize(); |
+ stats_.profiled_malloc.Initialize(); |
// malloc allocations. |
heap_profile_->alloc_address_map_->Iterate(RecordAlloc, this); |
// mmap allocations. |
heap_profile_->mmap_address_map_->Iterate(RecordMMap, this); |
+ std::sort(mmap_list_, mmap_list_ + mmap_list_length_, ByFirstAddress); |
} |
int DeepHeapProfile::FillBucketForBucketFile(const DeepBucket* deep_bucket, |
@@ -559,7 +650,7 @@ int DeepHeapProfile::UnparseRegionStats(const RegionStats* stats, |
int buffer_size, |
char* buffer) { |
int printed = snprintf(buffer + used_in_buffer, buffer_size - used_in_buffer, |
- "%15s %10ld %10ld\n", |
+ "%25s %12ld %12ld\n", |
name, stats->virtual_bytes(), |
stats->committed_bytes()); |
if (IsPrintedStringValid(printed, buffer_size, used_in_buffer)) { |
@@ -573,26 +664,64 @@ int DeepHeapProfile::UnparseRegionStats(const RegionStats* stats, |
int DeepHeapProfile::UnparseGlobalStats(int used_in_buffer, |
int buffer_size, |
char* buffer) { |
- int printed = snprintf(buffer + used_in_buffer, buffer_size - used_in_buffer, |
- "%15s %10s %10s\n", "", |
+ RegionStats all_total; |
+ RegionStats nonprofiled_total; |
+ for (int i = 0; i < NUMBER_OF_MAPS_REGION_TYPES; ++i) { |
+ all_total.AddAnotherRegionStat(stats_.all[i]); |
+ nonprofiled_total.AddAnotherRegionStat(stats_.nonprofiled[i]); |
+ } |
+ int printed = snprintf( |
+ buffer + used_in_buffer, buffer_size - used_in_buffer, |
+ "# total (%lu) %c= profiled-mmap (%lu) + nonprofiled-* (%lu)\n", |
+ all_total.committed_bytes(), |
+ all_total.committed_bytes() == |
+ stats_.profiled_mmap.committed_bytes() + |
+ nonprofiled_total.committed_bytes() ? '=' : '!', |
+ stats_.profiled_mmap.committed_bytes(), |
+ nonprofiled_total.committed_bytes()); |
+ if (IsPrintedStringValid(printed, buffer_size, used_in_buffer)) { |
+ return used_in_buffer; |
+ } |
+ used_in_buffer += printed; |
+ |
+ printed = snprintf(buffer + used_in_buffer, buffer_size - used_in_buffer, |
+ "%25s %12s %12s\n", "", |
kVirtualLabel, kCommittedLabel); |
if (IsPrintedStringValid(printed, buffer_size, used_in_buffer)) { |
return used_in_buffer; |
} |
used_in_buffer += printed; |
- used_in_buffer = UnparseRegionStats(&(stats_.total), "total", |
- used_in_buffer, buffer_size, buffer); |
- used_in_buffer = UnparseRegionStats(&(stats_.file_mapped), "file mapped", |
- used_in_buffer, buffer_size, buffer); |
- used_in_buffer = UnparseRegionStats(&(stats_.anonymous), "anonymous", |
- used_in_buffer, buffer_size, buffer); |
- used_in_buffer = UnparseRegionStats(&(stats_.other), "other", |
- used_in_buffer, buffer_size, buffer); |
- used_in_buffer = UnparseRegionStats(&(stats_.record_mmap), "mmap", |
- used_in_buffer, buffer_size, buffer); |
- used_in_buffer = UnparseRegionStats(&(stats_.record_malloc), "tcmalloc", |
- used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(all_total), |
+ "total", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.all[FILE_EXEC]), |
+ "file-exec", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.all[FILE_NONEXEC]), |
+ "file-nonexec", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.all[ANONYMOUS]), |
+ "anonymous", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.all[STACK]), |
+ "stack", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.all[OTHER]), |
+ "other", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(nonprofiled_total), |
+ "nonprofiled-total", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.nonprofiled[ABSENT]), |
+ "nonprofiled-absent", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.nonprofiled[ANONYMOUS]), |
+ "nonprofiled-anonymous", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.nonprofiled[FILE_EXEC]), |
+ "nonprofiled-file-exec", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.nonprofiled[FILE_NONEXEC]), |
+ "nonprofiled-file-nonexec", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.nonprofiled[STACK]), |
+ "nonprofiled-stack", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.nonprofiled[OTHER]), |
+ "nonprofiled-other", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.profiled_mmap), |
+ "profiled-mmap", used_in_buffer, buffer_size, buffer); |
+ used_in_buffer = UnparseRegionStats(&(stats_.profiled_malloc), |
+ "profiled-malloc", used_in_buffer, buffer_size, buffer); |
return used_in_buffer; |
} |
#else // DEEP_HEAP_PROFILE |