Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Unified Diff: third_party/tcmalloc/chromium/src/deep-memory-profiler.cc

Issue 7865021: Deep-Memory-Profiler (DMP) implementation (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Fixed bugs Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/tcmalloc/chromium/src/deep-memory-profiler.h ('k') | third_party/tcmalloc/chromium/src/dmprof » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/tcmalloc/chromium/src/deep-memory-profiler.cc
diff --git a/third_party/tcmalloc/chromium/src/deep-memory-profiler.cc b/third_party/tcmalloc/chromium/src/deep-memory-profiler.cc
new file mode 100644
index 0000000000000000000000000000000000000000..05e34cc64527ef7e42c0295a881bde0e3c138ef4
--- /dev/null
+++ b/third_party/tcmalloc/chromium/src/deep-memory-profiler.cc
@@ -0,0 +1,552 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "deep-memory-profiler.h"
+#include "base/sysinfo.h"
+#include "base/cycleclock.h"
+
+using tcmalloc::FillProcSelfMaps; // from sysinfo.h
+using tcmalloc::DumpProcSelfMaps; // from sysinfo.h
+
+// Those are used in parsing /proc/self/pagemaps
+#define PAGE_SIZE 4096
+#define U64_1 ((uint64)1)
+#define PFN_FILTER ((U64_1 << 55) - U64_1)
+#define PAGE_PRESENT U64_1 << 63
+#define PAGE_SWAP U64_1 << 62
+#define PAGE_RESERVED U64_1 << 61
+#define FLAG_NOPAGE U64_1 << 20
+#define FLAG_KSM U64_1 << 21
+#define FLAG_MMAP U64_1 << 11
+#define PAGEMAP_BYTES 8
+
+static const int kProfilerBufferSize = 1 << 20;
+static const int kHashTableSize = 179999; // Have to same as heap-profile-table.cc
+
+// header of the dumped heap profile
+static const char kProfileHeader[] = "Deep Memory Profile\n";
+static const char kGlobalStatsHeader[] = "GLOBAL_STATS:\n";
+static const char kStacktraceHeader[] = "STACKTRACES:\n";
+static const char kProcSelfMapsHeader[] = "\nMAPPED_LIBRARIES:\n";
+
+DeepMemoryProfiler::DeepMemoryProfiler(HeapProfileTable* heap_profile, const char* prefix) {
+ heap_profile_ = heap_profile;
+
+ //kpageflags_fd_ = open("/proc/kpageflags", O_RDONLY);
+ //RAW_DCHECK(kpageflags_fd_ != -1, "Failed to open /proc/kpageflags");
+ kpageflags_fd_ = -1; // Not currently using this
+
+ page_map_ = new(heap_profile_->alloc_(sizeof(PageStateMap)))
+ PageStateMap(heap_profile_->alloc_, heap_profile_->dealloc_);
+
+ // Copy filename prefix
+ RAW_DCHECK(filename_prefix_ == NULL, "");
+ const int prefix_length = strlen(prefix);
+ filename_prefix_ = reinterpret_cast<char*>(heap_profile_->alloc_(prefix_length + 1));
+ memcpy(filename_prefix_, prefix, prefix_length);
+ filename_prefix_[prefix_length] = '\0';
+
+ profiler_buffer_ = reinterpret_cast<char*>(heap_profile_->alloc_(kProfilerBufferSize));
+ dump_count_ = 0;
+}
+
+DeepMemoryProfiler::~DeepMemoryProfiler() {
+ page_map_->~PageStateMap();
+ heap_profile_->dealloc_(page_map_);
+ heap_profile_->dealloc_(filename_prefix_);
+ heap_profile_->dealloc_(profiler_buffer_);
+}
+
+// This function need to be called after each fork
+void DeepMemoryProfiler::OpenPageMap() {
+ char filename[100];
+ sprintf(filename, "/proc/%d/pagemap", getpid());
+ pagemap_fd_ = open(filename, O_RDONLY);
+ RAW_DCHECK(pagemap_fd_ != -1, "Failed to open /proc/self/pagemap");
+}
+
+bool DeepMemoryProfiler::PageMapSeek(uint64 addr) {
+ uint64 index = (addr / PAGE_SIZE) * PAGEMAP_BYTES;
+ uint64 o = lseek64(pagemap_fd_, index, SEEK_SET);
+ RAW_DCHECK(o == index, "");
+ return true;
+}
+
+bool DeepMemoryProfiler::PageMapRead(PageState* state) {
+ uint64 pa;
+ int t = read(pagemap_fd_, &pa, PAGEMAP_BYTES);
+ if(t != PAGEMAP_BYTES)
+ return false;
+
+ // Check if the page is committed
+ state->is_committed = (pa & (PAGE_PRESENT | PAGE_SWAP));
+
+ state->is_present = (pa & PAGE_PRESENT);
+ state->is_swapped = (pa & PAGE_SWAP);
+
+ // We can get more detailed stats from kPageflags
+ if(state->is_present && kpageflags_fd_ != -1) {
+ uint64 pfn = pa & PFN_FILTER;
+ int64 index = pfn * sizeof(uint64);
+ if (lseek64(kpageflags_fd_, index, SEEK_SET) != index) {
+ RAW_LOG(ERROR, "kpageflags seek failed. errno %d",errno);
+ return false;
+ }
+ uint64 flags;
+ if (read(kpageflags_fd_, &flags, sizeof(uint64)) < 0) {
+ RAW_LOG(ERROR, "kpageflags read failed. errno %d",errno);
+ return false;
+ }
+ if(flags & FLAG_NOPAGE) RAW_LOG(ERROR,"NOPAGE at present page frame %"PRId64"", pfn);
+ state->is_shared = (flags & FLAG_KSM);
+ state->is_mmap = (flags & FLAG_MMAP);
+ }else{
+ state->is_shared = false;
+ }
+
+ return true;
+}
+
+uint64 DeepMemoryProfiler::GetCommittedSize(uint64 addr, uint64 size) {
+ uint64 page_addr = (addr / PAGE_SIZE) * PAGE_SIZE;
+ uint64 committed_size = 0;
+
+ PageMapSeek(addr);
+ // Check every pages on which the allocation reside
+ while(page_addr < addr + size) {
+ // Read corresponding physical page
+ PageState state;
+ if(PageMapRead(&state) == false){
+ // We can't read the last region (e.g vsyscall)
+ RAW_LOG(0, "pagemap read failed @ %#llx %"PRId64" bytes", addr, size);
+ return 0;
+ }
+
+ if(state.is_committed){
+ // Calculate the size of the allocation part in this page
+ uint64 bytes = PAGE_SIZE;
+ if(page_addr < addr)
+ bytes -= addr - page_addr;
+ if(addr + size < page_addr + PAGE_SIZE)
+ bytes -= PAGE_SIZE - (addr + size - page_addr);
+
+ committed_size += bytes;
+ }
+ page_addr += PAGE_SIZE;
+ }
+
+ return committed_size;
+}
+
+void DeepMemoryProfiler::InitRegionStats(RegionStats* stats) {
+ stats->virtual_bytes = 0;
+ stats->committed_bytes = 0;
+}
+
+void DeepMemoryProfiler::RecordRegionStats(uint64 start, uint64 end,
+ RegionStats* stats) {
+ stats->virtual_bytes += end - start;
+ stats->committed_bytes += GetCommittedSize(start, end - start);
+}
+
+void DeepMemoryProfiler::GetGlobalStats() {
+ ProcMapsIterator::Buffer iterbuf;
+ ProcMapsIterator it(0, &iterbuf);
+ uint64 start, end, offset;
+ int64 inode;
+ char *flags, *filename;
+
+ InitRegionStats(&(stats_.total));
+ InitRegionStats(&(stats_.file_mapped));
+ InitRegionStats(&(stats_.anonymous));
+ InitRegionStats(&(stats_.other));
+
+ /*
+ char file_name[1000];
+ snprintf(file_name, sizeof(file_name), "%s.%05d.%04d.maps",
+ filename_prefix_, getpid(), dump_count_);
+
+ RawFD maps_fd = RawOpenForWriting(file_name);
+ RAW_DCHECK(maps_fd != kIllegalRawFD, "");
+
+ // We use global buffer here
+ char* buf = profiler_buffer_;
+ int bufsize = kProfilerBufferSize;
+ int buflen = 0;
+ int64 mmap_end = 0;
+ const AllocValue* v = NULL;
+ */
+
+ while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
+ if(strcmp("[vsyscall]", filename) == 0) continue; // pagemap read fails in this region
+
+ int64 committed_bytes = stats_.total.committed_bytes;
+ RecordRegionStats(start, end, &(stats_.total));
+ committed_bytes = stats_.total.committed_bytes - committed_bytes;
+
+ if(filename[0] == '/') {
+ RecordRegionStats(start, end, &(stats_.file_mapped));
+ }else if(filename[0] == '\0' || filename[0] == '\n' || filename[0] == EOF){
+ RecordRegionStats(start, end, &(stats_.anonymous));
+ }else{
+ RecordRegionStats(start, end, &(stats_.other));
+ }
+ /*
+ RegionValue rv;
+ rv.start = start;
+ rv.end = end;
+ rv.size = end - start;
+ rv.committed_size = committed_bytes;
+ rv.recorded_size = 0;
+ rv.recorded_committed_size = 0;
+ memcpy(rv.permissions, flags, 4);
+ rv.permissions[4] = '\0';
+ memcpy(rv.filename, filename, strlen(filename) + 1);
+ regions_->Insert(reinterpret_cast<void*>(start), rv);
+
+ if(rv.size > max_region_size_)
+ max_region_size_ = rv.size;
+ */
+
+ /*
+ uint64 recorded_bytes = 0;
+ if(mmap_end > start && v != NULL){
+ // This region is a part of the previous mmap allocation
+ if(mmap_end >= end)
+ recorded_bytes = end - start;
+ else
+ recorded_bytes = mmap_end - start;
+ }else{
+ v = heap_profile_->allocation_mmap_->Find(reinterpret_cast<void*>(start));
+ if(v != NULL){
+ mmap_end = start + v->bytes;
+ if(mmap_end >= end)
+ recorded_bytes = end - start;
+ else
+ recorded_bytes = mmap_end - start;
+ while(mmap_end < end){
+ // There might be multiple mmap allocations in this region
+ v = heap_profile_->allocation_mmap_->Find(reinterpret_cast<void*>(mmap_end));
+ if(v == NULL)
+ break;
+ else{
+ mmap_end += v->bytes;
+ recorded_bytes += v->bytes;
+ }
+ }
+ }
+ }
+
+ buflen += snprintf(buf + buflen, bufsize - buflen,
+ "%#llx-%#llx %10"PRId64" %10"PRId64" %10"PRId64" %s\n",
+ start, end, (end - start), committed_bytes, recorded_bytes,
+ filename);
+ */
+ }
+
+ //RawWrite(maps_fd, buf, buflen);
+ //RawClose(maps_fd);
+}
+
+void DeepMemoryProfiler::RecordAllocInRegions(uint64 addr, uint64 size) {
+ const void* regions_start;
+ const RegionValue* rv_const = regions_->FindInside(&GetRegionSize,
+ (size_t)max_region_size_,
+ (void*)addr, &regions_start);
+
+ if(rv_const == NULL) {
+ RAW_LOG(0, "Could't find a region for allocation!");
+ return;
+ }
+ RegionValue* rv = regions_->FindMutable(regions_start);
+ RAW_DCHECK(rv->start <= addr, "");
+ RAW_DCHECK(rv->end > addr, "");
+
+ if(rv->end >= addr + size) {
+ // This region includes the whole allocation
+ rv->recorded_size += size;
+ rv->recorded_committed_size += GetCommittedSize(addr, size);
+ }else{
+ // This region includes only a part of the allocation.
+ // Need to find the other regions that include this allocation.
+ rv->recorded_size += rv->end - addr;
+ rv->recorded_committed_size += GetCommittedSize(addr, rv->end - addr);
+ RecordAllocInRegions(rv->end, addr + size - rv->end);
+ }
+}
+
+void DeepMemoryProfiler::RecordAlloc(const void* ptr, AllocValue* v,
+ DeepMemoryProfiler* deep_profiler) {
+ uint64 alloc_addr = (uint64)ptr;
+ uint64 committed = deep_profiler->GetCommittedSize(alloc_addr, v->bytes);
+
+ v->bucket()->committed_size += committed;
+ if(deep_profiler->recording_mmap_){
+ deep_profiler->stats_.record_mmap.virtual_bytes += v->bytes;
+ deep_profiler->stats_.record_mmap.committed_bytes += committed;
+ // Record this allocation in the region map
+ //deep_profiler->RecordAllocInRegions(alloc_addr, v->bytes);
+ }else{
+ deep_profiler->stats_.record_tcmalloc.virtual_bytes += v->bytes;
+ deep_profiler->stats_.record_tcmalloc.committed_bytes += committed;
+ }
+}
+
+void DeepMemoryProfiler::RecordAllAllocs() {
+ stats_.record_mmap.virtual_bytes = 0;
+ stats_.record_mmap.committed_bytes = 0;
+ stats_.record_tcmalloc.virtual_bytes = 0;
+ stats_.record_tcmalloc.committed_bytes = 0;
+
+ // Tcmalloc allocs
+ recording_mmap_ = false;
+ heap_profile_->allocation_->Iterate(RecordAlloc, this);
+
+ // Mmap allocs
+ recording_mmap_ = true;
+ heap_profile_->allocation_mmap_->Iterate(RecordAlloc, this);
+}
+
+
+void DeepMemoryProfiler::WriteLeakyRegion(const void* ptr,
+ RegionValue* rv,
+ BufferArgs* buffer) {
+ if(rv->committed_size > rv->recorded_committed_size){
+ //if(rv->filename[0] != '\0')
+ // return;
+ if(buffer->len >= buffer->size)
+ return;
+
+ int printed = snprintf(buffer->buf + buffer->len, buffer->size - buffer->len,
+ "%#llx-%#llx %s %10"PRId64" %10"PRId64""
+ " %10"PRId64" %10"PRId64" %s\n",
+ rv->start, rv->end, rv->permissions,
+ rv->size - rv->recorded_size, rv->size,
+ rv->committed_size - rv->recorded_committed_size,
+ rv->committed_size,
+ rv->filename);
+
+ if(printed >= buffer->size - buffer->len)
+ return;
+ buffer->len += printed;
+ }
+}
+
+// Write leaked regions to a file
+void DeepMemoryProfiler::WriteAllLeakyRegions() {
+ BufferArgs buffer;
+ buffer.buf = profiler_buffer_;
+ buffer.size = kProfilerBufferSize;
+ buffer.len = 0;
+ regions_->Iterate(WriteLeakyRegion, &buffer);
+
+ char leaks_file_name[1000];
+ snprintf(leaks_file_name, sizeof(leaks_file_name), "%s.%05d.%04d.leaks",
+ filename_prefix_, getpid(), dump_count_);
+
+ RawFD leaks_fd = RawOpenForWriting(leaks_file_name);
+ RAW_DCHECK(leaks_fd != kIllegalRawFD, "");
+ RawWrite(leaks_fd, buffer.buf, buffer.len);
+ RawClose(leaks_fd);
+}
+
+void DeepMemoryProfiler::WriteMapsToFile(char buf[], int size) {
+ char file_name[100];
+ snprintf(file_name, sizeof(file_name), "%s.%05d.maps", filename_prefix_, getpid());
+
+ RawFD maps_fd = RawOpenForWriting(file_name);
+ RAW_DCHECK(maps_fd != kIllegalRawFD, "");
+
+ int map_length;
+ bool dummy; // "wrote_all" -- did /proc/self/maps fit in its entirety?
+ map_length = FillProcSelfMaps(profiler_buffer_, kProfilerBufferSize, &dummy);
+ RAW_DCHECK(map_length <= kProfilerBufferSize, "");
+ RawWrite(maps_fd, profiler_buffer_, map_length);
+ RawClose(maps_fd);
+}
+
+int DeepMemoryProfiler::WriteBucket(const Bucket* b, char buf[], int bufsize) {
+ int buflen = 0;
+ buflen += snprintf(buf + buflen, bufsize - buflen, "%05d", b->id);
+ for (int d = 0; d < b->depth; d++) {
+ buflen += snprintf(buf + buflen, bufsize - buflen, " 0x%08" PRIxPTR,
+ reinterpret_cast<uintptr_t>(b->stack[d]));
+ }
+ buflen += snprintf(buf + buflen, bufsize - buflen, "\n");
+ return buflen;
+}
+
+void DeepMemoryProfiler::WriteBucketsToFile() {
+ char file_name[100];
+ snprintf(file_name, sizeof(file_name), "%s.%05d.%04d.buckets",
+ filename_prefix_, getpid(), dump_count_);
+ RawFD bucket_fd = RawOpenForWriting(file_name);
+ RAW_DCHECK(bucket_fd != kIllegalRawFD, "");
+
+ // We will use the global buffer here
+ char* buf = profiler_buffer_;
+ int size = kProfilerBufferSize;
+ int buflen = 0;
+ for (int b = 0; b < kHashTableSize; b++) {
+ for (Bucket* x = heap_profile_->table_[b]; x != 0; x = x->next) {
+ if(x->is_logged)
+ continue; // Skip the bucket if it is already logged
+ if(x->alloc_size - x->free_size <= 64)
+ continue; // Skip small buckets
+
+ buflen += WriteBucket(x, buf + buflen, size - buflen);
+ x->is_logged = true;
+
+ // Write to file if buffer 80% full
+ if(buflen > size * 0.8){
+ RawWrite(bucket_fd, buf, buflen);
+ buflen = 0;
+ }
+ }
+ }
+ RawWrite(bucket_fd, buf, buflen);
+ RawClose(bucket_fd);
+}
+
+int DeepMemoryProfiler::UnparseBucket(const Bucket& b,
+ char* buf, int buflen, int bufsize,
+ const char* extra,
+ Stats* profile_stats) {
+ if (profile_stats != NULL) {
+ profile_stats->allocs += b.allocs;
+ profile_stats->alloc_size += b.alloc_size;
+ profile_stats->frees += b.frees;
+ profile_stats->free_size += b.free_size;
+ }
+ int printed =
+ snprintf(buf + buflen, bufsize - buflen,
+ "%10"PRId64" %10"PRId64" %6d %6d @%s %d\n",
+ b.alloc_size - b.free_size,
+ b.committed_size,
+ b.allocs, b.frees,
+ extra,
+ b.id);
+ // If it looks like the snprintf failed, ignore the fact we printed anything
+ if (printed < 0 || printed >= bufsize - buflen) return buflen;
+ buflen += printed;
+
+ return buflen;
+}
+
+int DeepMemoryProfiler::UnparseRegionStats(const RegionStats* stats, const char* name,
+ char* buf, int buflen, int bufsize) {
+ int printed = snprintf(buf + buflen, bufsize - buflen,
+ "%15s %10"PRId64" %10"PRId64"\n",
+ name,
+ stats->virtual_bytes,
+ stats->committed_bytes);
+
+ return buflen + printed;
+}
+
+int DeepMemoryProfiler::UnparseGlobalStats(char* buf, int buflen, int bufsize) {
+ buflen += snprintf(buf + buflen, bufsize - buflen,
+ "%15s %10s %10s\n",
+ "", "virtual", "committed");
+
+ buflen = UnparseRegionStats(&(stats_.total), "total", buf, buflen, bufsize);
+ buflen = UnparseRegionStats(&(stats_.file_mapped), "file mapped", buf, buflen, bufsize);
+ buflen = UnparseRegionStats(&(stats_.anonymous), "anonymous", buf, buflen, bufsize);
+ buflen = UnparseRegionStats(&(stats_.other), "other", buf, buflen, bufsize);
+ buflen = UnparseRegionStats(&(stats_.record_mmap), "mmap", buf, buflen, bufsize);
+ buflen = UnparseRegionStats(&(stats_.record_tcmalloc), "tcmalloc", buf, buflen, bufsize);
+ return buflen;
+}
+
+// Takes snapshot of current memory usage.
+// We avoid any memory allocations during snapshots.
+//void DeepMemoryProfiler::TakeMemorySnapshot() {
+//}
+
+int DeepMemoryProfiler::FillOrderedProfile(char buf[], int size) {
+ int64 start_time = CycleClock::Now();
+ dump_count_++;
+
+ // We need to re-open files in /proc/pid/ if pid is created
+ if(most_recent_pid_ != getpid()) {
+ most_recent_pid_ = getpid();
+ OpenPageMap();
+ WriteMapsToFile(profiler_buffer_, kProfilerBufferSize); // Using the global buffer
+ }
+
+
+ //regions_ = new(heap_profile_->alloc_(sizeof(RegionMap)))
+ // RegionMap(heap_profile_->alloc_, heap_profile_->dealloc_);
+ //max_region_size_ = 0;
+ GetGlobalStats();
+ uint64 anonymous_committed = stats_.anonymous.committed_bytes;
+
+ // Reset committed size of buckets
+ for (int b = 0; b < kHashTableSize; b++) {
+ for (Bucket* x = heap_profile_->table_[b]; x != 0; x = x->next) {
+ x->committed_size = 0;
+ }
+ }
+
+ //Bucket** list = heap_profile_->MakeBucketList();
+ //for (int i = 0; i < heap_profile_->num_buckets_; i++) {
+ // list[i]->committed_size = 0;
+ //}
+
+ // No allocation zone starts -----------------------------------
+
+ // Record committed sizes
+ RecordAllAllocs();
+
+ // Check if committed bytes changed during RecordAllAllocs.
+ GetGlobalStats();
+ uint64 comm_diff = stats_.anonymous.committed_bytes - anonymous_committed;
+ if(comm_diff != 0)
+ RAW_LOG(0, "committed diff: %"PRId64"", comm_diff);
+
+ HeapProfileTable::Stats stats;
+ memset(&stats, 0, sizeof(stats));
+
+ int bucket_length = snprintf(buf, size, kProfileHeader);
+ if (bucket_length < 0 || bucket_length >= size) return 0;
+
+ // Printing Global Stats
+ bucket_length += snprintf(buf + bucket_length, size - bucket_length, kGlobalStatsHeader);
+ bucket_length = UnparseGlobalStats(buf, bucket_length, size);
+
+ // Printing Stacktraces
+ bucket_length += snprintf(buf + bucket_length, size - bucket_length, kStacktraceHeader);
+ bucket_length += snprintf(buf + bucket_length, size - bucket_length,
+ "%10s %10s\n", "virtual", "committed");
+
+ for (int b = 0; b < kHashTableSize; b++) {
+ for (Bucket* x = heap_profile_->table_[b]; x != 0; x = x->next) {
+ if(x->alloc_size - x->free_size == 0)
+ continue; // Skip empty buckets
+ bucket_length = UnparseBucket(*x, buf, bucket_length, size, "",
+ &stats);
+ }
+ }
+
+ RAW_DCHECK(bucket_length < size, "");
+ // No allocation zone ends -----------------------------------
+
+ //WriteAllLeakyRegions();
+ // Write stacktraces
+ WriteBucketsToFile();
+
+ //regions_->~RegionMap();
+ //heap_profile_->dealloc_(regions_);
+
+ int64 dt = CycleClock::Now() - start_time;
+ double dtf = dt / CyclesPerSecond();
+ RAW_LOG(0, "Time spent on DeepProfiler: %.3f sec\n", dtf);
+
+ return bucket_length;
+}
« no previous file with comments | « third_party/tcmalloc/chromium/src/deep-memory-profiler.h ('k') | third_party/tcmalloc/chromium/src/dmprof » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698