Chromium Code Reviews| 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); |
|
Dai Mikurube (NOT FULLTIME)
2012/04/12 12:20:24
This part may be to be considered to simplify.
|
| return used_in_buffer; |
| } |
| #else // DEEP_HEAP_PROFILE |