This source file includes following definitions.
- AppendCommandLine
- Initialize
- CommittedSize
- IsPageCountAvailable
- Initialize
- CommittedSize
- ReadPageCount
- Seek
- Read
- IsPageCountAvailable
- MemoryResidenceInfoGetterInterface
- MemoryResidenceInfoGetterInterface
- Create
- heap_profile_
- DumpOrderedProfile
- Size
- FilledBytes
- Clear
- Flush
- AppendChar
- AppendString
- AppendInt
- AppendLong
- AppendUnsignedLong
- AppendInt64
- AppendPtr
- AppendBase64
- ForwardCursor
- UnparseForStats
- UnparseForBucketFile
- bucket_id_
- Lookup
- UnparseForStats
- WriteForBucketFile
- ResetCommittedSize
- ResetIsLogged
- AddToHashValue
- FinishHashValue
- Initialize
- Record
- Unparse
- SnapshotMaps
- SnapshotAllocations
- Unparse
- RecordAlloc
- GetInformationOfMemoryRegion
- WriteProcMaps
- DumpOrderedProfile
#include "deep-heap-profile.h"
#ifdef USE_DEEP_HEAP_PROFILE
#include <algorithm>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#if defined(__linux__)
#include <endian.h>
#if !defined(__LITTLE_ENDIAN__) and !defined(__BIG_ENDIAN__)
#if __BYTE_ORDER == __BIG_ENDIAN
#define __BIG_ENDIAN__
#endif
#endif
#if defined(__BIG_ENDIAN__)
#include <byteswap.h>
#endif
#endif
#if defined(COMPILER_MSVC)
#include <Winsock2.h>
#endif
#include "base/cycleclock.h"
#include "base/sysinfo.h"
#include "internal_logging.h"
static const int kProfilerBufferSize = 1 << 20;
static const int kHashTableSize = 179999;
static const int PAGEMAP_BYTES = 8;
static const int KPAGECOUNT_BYTES = 8;
static const uint64 MAX_ADDRESS = kuint64max;
static const char kProfileHeader[] = "heap profile: ";
static const char kProfileVersion[] = "DUMP_DEEP_6";
static const char kMetaInformationHeader[] = "META:\n";
static const char kMMapListHeader[] = "MMAP_LIST:\n";
static const char kGlobalStatsHeader[] = "GLOBAL_STATS:\n";
static const char kStacktraceHeader[] = "STACKTRACES:\n";
static const char kProcSelfMapsHeader[] = "\nMAPPED_LIBRARIES:\n";
static const char kVirtualLabel[] = "virtual";
static const char kCommittedLabel[] = "committed";
#if defined(__linux__)
#define OS_NAME "linux"
#elif defined(_WIN32) || defined(_WIN64)
#define OS_NAME "windows"
#else
#define OS_NAME "unknown-os"
#endif
bool DeepHeapProfile::AppendCommandLine(TextBuffer* buffer) {
#if defined(__linux__)
RawFD fd;
char filename[100];
char cmdline[4096];
snprintf(filename, sizeof(filename), "/proc/%d/cmdline",
static_cast<int>(getpid()));
fd = open(filename, O_RDONLY);
if (fd == kIllegalRawFD) {
RAW_VLOG(0, "Failed to open /proc/self/cmdline");
return false;
}
size_t length = read(fd, cmdline, sizeof(cmdline) - 1);
close(fd);
for (int i = 0; i < length; ++i)
if (cmdline[i] == '\0')
cmdline[i] = ' ';
cmdline[length] = '\0';
buffer->AppendString("CommandLine: ", 0);
buffer->AppendString(cmdline, 0);
buffer->AppendChar('\n');
return true;
#else
return false;
#endif
}
#if defined(_WIN32) || defined(_WIN64)
void DeepHeapProfile::MemoryInfoGetterWindows::Initialize() {
}
size_t DeepHeapProfile::MemoryInfoGetterWindows::CommittedSize(
uint64 first_address,
uint64 last_address,
TextBuffer* buffer) const {
return 0;
}
bool DeepHeapProfile::MemoryInfoGetterWindows::IsPageCountAvailable() const {
return false;
}
#endif
#if defined(__linux__)
void DeepHeapProfile::MemoryInfoGetterLinux::Initialize() {
char filename[100];
snprintf(filename, sizeof(filename), "/proc/%d/pagemap",
static_cast<int>(getpid()));
pagemap_fd_ = open(filename, O_RDONLY);
RAW_CHECK(pagemap_fd_ != -1, "Failed to open /proc/self/pagemap");
if (pageframe_type_ == DUMP_PAGECOUNT) {
snprintf(filename, sizeof(filename), "/proc/kpagecount");
kpagecount_fd_ = open(filename, O_RDONLY);
if (kpagecount_fd_ == -1)
RAW_VLOG(0, "Failed to open /proc/kpagecount");
}
}
size_t DeepHeapProfile::MemoryInfoGetterLinux::CommittedSize(
uint64 first_address,
uint64 last_address,
DeepHeapProfile::TextBuffer* buffer) const {
int page_size = getpagesize();
uint64 page_address = (first_address / page_size) * page_size;
size_t committed_size = 0;
size_t pageframe_list_length = 0;
Seek(first_address);
while (page_address <= last_address) {
State state;
if (Read(&state, pageframe_type_ != DUMP_NO_PAGEFRAME) == false) {
#ifndef NDEBUG
RAW_VLOG(0, "pagemap read failed @ %#llx %" PRId64 " bytes",
first_address, last_address - first_address + 1);
#endif
return 0;
}
if (pageframe_type_ != DUMP_NO_PAGEFRAME &&
buffer != NULL && state.pfn != 0) {
if (pageframe_list_length == 0) {
buffer->AppendString(" PF:", 0);
pageframe_list_length = 5;
}
buffer->AppendChar(' ');
if (page_address < first_address)
buffer->AppendChar('<');
buffer->AppendBase64(state.pfn, 4);
pageframe_list_length += 5;
if (pageframe_type_ == DUMP_PAGECOUNT && IsPageCountAvailable()) {
uint64 pagecount = ReadPageCount(state.pfn);
if (pagecount > 63)
pagecount = 63;
buffer->AppendChar('#');
buffer->AppendBase64(pagecount, 1);
pageframe_list_length += 2;
}
if (last_address < page_address - 1 + page_size)
buffer->AppendChar('>');
if (pageframe_list_length > 94) {
buffer->AppendChar('\n');
pageframe_list_length = 0;
}
}
if (state.is_committed) {
size_t bytes = page_size;
if (last_address <= page_address - 1 + page_size) {
bytes = last_address - page_address + 1;
}
if (page_address < first_address) {
bytes -= first_address - page_address;
}
committed_size += bytes;
}
if (page_address > MAX_ADDRESS - page_size) {
break;
}
page_address += page_size;
}
if (pageframe_type_ != DUMP_NO_PAGEFRAME &&
buffer != NULL && pageframe_list_length != 0) {
buffer->AppendChar('\n');
}
return committed_size;
}
uint64 DeepHeapProfile::MemoryInfoGetterLinux::ReadPageCount(uint64 pfn) const {
int64 index = pfn * KPAGECOUNT_BYTES;
int64 offset = lseek64(kpagecount_fd_, index, SEEK_SET);
RAW_DCHECK(offset == index, "Failed in seeking in kpagecount.");
uint64 kpagecount_value;
int result = read(kpagecount_fd_, &kpagecount_value, KPAGECOUNT_BYTES);
if (result != KPAGECOUNT_BYTES)
return 0;
return kpagecount_value;
}
bool DeepHeapProfile::MemoryInfoGetterLinux::Seek(uint64 address) const {
int64 index = (address / getpagesize()) * PAGEMAP_BYTES;
RAW_DCHECK(pagemap_fd_ != -1, "Failed to seek in /proc/self/pagemap");
int64 offset = lseek64(pagemap_fd_, index, SEEK_SET);
RAW_DCHECK(offset == index, "Failed in seeking.");
return offset >= 0;
}
bool DeepHeapProfile::MemoryInfoGetterLinux::Read(
State* state, bool get_pfn) const {
static const uint64 U64_1 = 1;
static const uint64 PFN_FILTER = (U64_1 << 55) - U64_1;
static const uint64 PAGE_PRESENT = U64_1 << 63;
static const uint64 PAGE_SWAP = U64_1 << 62;
static const uint64 PAGE_RESERVED = U64_1 << 61;
static const uint64 FLAG_NOPAGE = U64_1 << 20;
static const uint64 FLAG_KSM = U64_1 << 21;
static const uint64 FLAG_MMAP = U64_1 << 11;
uint64 pagemap_value;
RAW_DCHECK(pagemap_fd_ != -1, "Failed to read from /proc/self/pagemap");
int result = read(pagemap_fd_, &pagemap_value, PAGEMAP_BYTES);
if (result != PAGEMAP_BYTES) {
return false;
}
state->is_committed = (pagemap_value & (PAGE_PRESENT | PAGE_SWAP));
state->is_present = (pagemap_value & PAGE_PRESENT);
state->is_swapped = (pagemap_value & PAGE_SWAP);
state->is_shared = false;
if (get_pfn && state->is_present && !state->is_swapped)
state->pfn = (pagemap_value & PFN_FILTER);
else
state->pfn = 0;
return true;
}
bool DeepHeapProfile::MemoryInfoGetterLinux::IsPageCountAvailable() const {
return kpagecount_fd_ != -1;
}
#endif
DeepHeapProfile::MemoryResidenceInfoGetterInterface::
MemoryResidenceInfoGetterInterface() {}
DeepHeapProfile::MemoryResidenceInfoGetterInterface::
~MemoryResidenceInfoGetterInterface() {}
DeepHeapProfile::MemoryResidenceInfoGetterInterface*
DeepHeapProfile::MemoryResidenceInfoGetterInterface::Create(
PageFrameType pageframe_type) {
#if defined(_WIN32) || defined(_WIN64)
return new MemoryInfoGetterWindows(pageframe_type);
#elif defined(__linux__)
return new MemoryInfoGetterLinux(pageframe_type);
#else
return NULL;
#endif
}
DeepHeapProfile::DeepHeapProfile(HeapProfileTable* heap_profile,
const char* prefix,
enum PageFrameType pageframe_type)
: memory_residence_info_getter_(
MemoryResidenceInfoGetterInterface::Create(pageframe_type)),
most_recent_pid_(-1),
stats_(),
dump_count_(0),
filename_prefix_(NULL),
deep_table_(kHashTableSize, heap_profile->alloc_, heap_profile->dealloc_),
pageframe_type_(pageframe_type),
heap_profile_(heap_profile) {
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';
strncpy(run_id_, "undetermined-run-id", sizeof(run_id_));
}
DeepHeapProfile::~DeepHeapProfile() {
heap_profile_->dealloc_(filename_prefix_);
delete memory_residence_info_getter_;
}
void DeepHeapProfile::DumpOrderedProfile(const char* reason,
char raw_buffer[],
int buffer_size,
RawFD fd) {
TextBuffer buffer(raw_buffer, buffer_size, fd);
#ifndef NDEBUG
int64 starting_cycles = CycleClock::Now();
#endif
time_t time_value = time(NULL);
++dump_count_;
if (most_recent_pid_ != getpid()) {
char hostname[64];
if (0 == gethostname(hostname, sizeof(hostname))) {
char* dot = strchr(hostname, '.');
if (dot != NULL)
*dot = '\0';
} else {
strcpy(hostname, "unknown");
}
most_recent_pid_ = getpid();
snprintf(run_id_, sizeof(run_id_), "%s-" OS_NAME "-%d-%lu",
hostname, most_recent_pid_, time(NULL));
if (memory_residence_info_getter_)
memory_residence_info_getter_->Initialize();
deep_table_.ResetIsLogged();
WriteProcMaps(filename_prefix_, raw_buffer, buffer_size);
}
deep_table_.ResetCommittedSize();
stats_.SnapshotAllocations(this);
buffer.AppendString(kProfileHeader, 0);
buffer.AppendString(kProfileVersion, 0);
buffer.AppendString("\n", 0);
buffer.AppendString(kMetaInformationHeader, 0);
buffer.AppendString("Time: ", 0);
buffer.AppendUnsignedLong(time_value, 0);
buffer.AppendChar('\n');
if (reason != NULL) {
buffer.AppendString("Reason: ", 0);
buffer.AppendString(reason, 0);
buffer.AppendChar('\n');
}
AppendCommandLine(&buffer);
buffer.AppendString("RunID: ", 0);
buffer.AppendString(run_id_, 0);
buffer.AppendChar('\n');
buffer.AppendString("PageSize: ", 0);
buffer.AppendInt(getpagesize(), 0, 0);
buffer.AppendChar('\n');
if (pageframe_type_ == DUMP_PAGECOUNT && memory_residence_info_getter_ &&
memory_residence_info_getter_->IsPageCountAvailable()) {
buffer.AppendString("PageFrame: 24,Base64,PageCount", 0);
buffer.AppendChar('\n');
} else if (pageframe_type_ != DUMP_NO_PAGEFRAME) {
buffer.AppendString("PageFrame: 24,Base64", 0);
buffer.AppendChar('\n');
}
buffer.AppendString(kMMapListHeader, 0);
stats_.SnapshotMaps(memory_residence_info_getter_, this, &buffer);
buffer.AppendString(kGlobalStatsHeader, 0);
stats_.Unparse(&buffer);
buffer.AppendString(kStacktraceHeader, 0);
buffer.AppendString(kVirtualLabel, 10);
buffer.AppendChar(' ');
buffer.AppendString(kCommittedLabel, 10);
buffer.AppendString("\n", 0);
deep_table_.UnparseForStats(&buffer);
buffer.Flush();
deep_table_.WriteForBucketFile(
filename_prefix_, dump_count_, raw_buffer, buffer_size);
#ifndef NDEBUG
int64 elapsed_cycles = CycleClock::Now() - starting_cycles;
double elapsed_seconds = elapsed_cycles / CyclesPerSecond();
RAW_VLOG(0, "Time spent on DeepProfiler: %.3f sec\n", elapsed_seconds);
#endif
}
int DeepHeapProfile::TextBuffer::Size() {
return size_;
}
int DeepHeapProfile::TextBuffer::FilledBytes() {
return cursor_;
}
void DeepHeapProfile::TextBuffer::Clear() {
cursor_ = 0;
}
void DeepHeapProfile::TextBuffer::Flush() {
RawWrite(fd_, buffer_, cursor_);
cursor_ = 0;
}
bool DeepHeapProfile::TextBuffer::AppendChar(char value) {
return ForwardCursor(snprintf(buffer_ + cursor_, size_ - cursor_,
"%c", value));
}
bool DeepHeapProfile::TextBuffer::AppendString(const char* value, int width) {
char* position = buffer_ + cursor_;
int available = size_ - cursor_;
int appended;
if (width == 0)
appended = snprintf(position, available, "%s", value);
else
appended = snprintf(position, available, "%*s",
width, value);
return ForwardCursor(appended);
}
bool DeepHeapProfile::TextBuffer::AppendInt(int value, int width,
bool leading_zero) {
char* position = buffer_ + cursor_;
int available = size_ - cursor_;
int appended;
if (width == 0)
appended = snprintf(position, available, "%d", value);
else if (leading_zero)
appended = snprintf(position, available, "%0*d", width, value);
else
appended = snprintf(position, available, "%*d", width, value);
return ForwardCursor(appended);
}
bool DeepHeapProfile::TextBuffer::AppendLong(long value, int width) {
char* position = buffer_ + cursor_;
int available = size_ - cursor_;
int appended;
if (width == 0)
appended = snprintf(position, available, "%ld", value);
else
appended = snprintf(position, available, "%*ld", width, value);
return ForwardCursor(appended);
}
bool DeepHeapProfile::TextBuffer::AppendUnsignedLong(unsigned long value,
int width) {
char* position = buffer_ + cursor_;
int available = size_ - cursor_;
int appended;
if (width == 0)
appended = snprintf(position, available, "%lu", value);
else
appended = snprintf(position, available, "%*lu", width, value);
return ForwardCursor(appended);
}
bool DeepHeapProfile::TextBuffer::AppendInt64(int64 value, int width) {
char* position = buffer_ + cursor_;
int available = size_ - cursor_;
int appended;
if (width == 0)
appended = snprintf(position, available, "%" PRId64, value);
else
appended = snprintf(position, available, "%*" PRId64, width, value);
return ForwardCursor(appended);
}
bool DeepHeapProfile::TextBuffer::AppendPtr(uint64 value, int width) {
char* position = buffer_ + cursor_;
int available = size_ - cursor_;
int appended;
if (width == 0)
appended = snprintf(position, available, "%" PRIx64, value);
else
appended = snprintf(position, available, "%0*" PRIx64, width, value);
return ForwardCursor(appended);
}
bool DeepHeapProfile::TextBuffer::AppendBase64(uint64 value, int width) {
static const char base64[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#if defined(__BIG_ENDIAN__)
value = bswap_64(value);
#endif
for (int shift = (width - 1) * 6; shift >= 0; shift -= 6) {
if (!AppendChar(base64[(value >> shift) & 0x3f]))
return false;
}
return true;
}
bool DeepHeapProfile::TextBuffer::ForwardCursor(int appended) {
if (appended < 0 || appended >= size_ - cursor_)
return false;
cursor_ += appended;
if (cursor_ > size_ * 4 / 5)
Flush();
return true;
}
void DeepHeapProfile::DeepBucket::UnparseForStats(TextBuffer* buffer) {
buffer->AppendInt64(bucket->alloc_size - bucket->free_size, 10);
buffer->AppendChar(' ');
buffer->AppendInt64(committed_size, 10);
buffer->AppendChar(' ');
buffer->AppendInt(bucket->allocs, 6, false);
buffer->AppendChar(' ');
buffer->AppendInt(bucket->frees, 6, false);
buffer->AppendString(" @ ", 0);
buffer->AppendInt(id, 0, false);
buffer->AppendString("\n", 0);
}
void DeepHeapProfile::DeepBucket::UnparseForBucketFile(TextBuffer* buffer) {
buffer->AppendInt(id, 0, false);
buffer->AppendChar(' ');
buffer->AppendString(is_mmap ? "mmap" : "malloc", 0);
#if defined(TYPE_PROFILING)
buffer->AppendString(" t0x", 0);
buffer->AppendPtr(reinterpret_cast<uintptr_t>(type), 0);
if (type == NULL) {
buffer->AppendString(" nno_typeinfo", 0);
} else {
buffer->AppendString(" n", 0);
buffer->AppendString(type->name(), 0);
}
#endif
for (int depth = 0; depth < bucket->depth; depth++) {
buffer->AppendString(" 0x", 0);
buffer->AppendPtr(reinterpret_cast<uintptr_t>(bucket->stack[depth]), 8);
}
buffer->AppendString("\n", 0);
}
DeepHeapProfile::DeepBucketTable::DeepBucketTable(
int table_size,
HeapProfileTable::Allocator alloc,
HeapProfileTable::DeAllocator dealloc)
: table_(NULL),
table_size_(table_size),
alloc_(alloc),
dealloc_(dealloc),
bucket_id_(0) {
const int bytes = table_size * sizeof(DeepBucket*);
table_ = reinterpret_cast<DeepBucket**>(alloc(bytes));
memset(table_, 0, bytes);
}
DeepHeapProfile::DeepBucketTable::~DeepBucketTable() {
ASSERT(table_ != NULL);
for (int db = 0; db < table_size_; db++) {
for (DeepBucket* x = table_[db]; x != 0; ) {
DeepBucket* db = x;
x = x->next;
dealloc_(db);
}
}
dealloc_(table_);
}
DeepHeapProfile::DeepBucket* DeepHeapProfile::DeepBucketTable::Lookup(
Bucket* bucket,
#if defined(TYPE_PROFILING)
const std::type_info* type,
#endif
bool is_mmap) {
uintptr_t h = 0;
AddToHashValue(reinterpret_cast<uintptr_t>(bucket), &h);
if (is_mmap) {
AddToHashValue(1, &h);
} else {
AddToHashValue(0, &h);
}
#if defined(TYPE_PROFILING)
if (type == NULL) {
AddToHashValue(0, &h);
} else {
AddToHashValue(reinterpret_cast<uintptr_t>(type->name()), &h);
}
#endif
FinishHashValue(&h);
unsigned int buck = ((unsigned int) h) % table_size_;
for (DeepBucket* db = table_[buck]; db != 0; db = db->next) {
if (db->bucket == bucket) {
return db;
}
}
DeepBucket* db = reinterpret_cast<DeepBucket*>(alloc_(sizeof(DeepBucket)));
memset(db, 0, sizeof(*db));
db->bucket = bucket;
#if defined(TYPE_PROFILING)
db->type = type;
#endif
db->committed_size = 0;
db->is_mmap = is_mmap;
db->id = (bucket_id_++);
db->is_logged = false;
db->next = table_[buck];
table_[buck] = db;
return db;
}
void DeepHeapProfile::DeepBucketTable::UnparseForStats(TextBuffer* buffer) {
for (int i = 0; i < table_size_; i++) {
for (DeepBucket* deep_bucket = table_[i];
deep_bucket != NULL;
deep_bucket = deep_bucket->next) {
Bucket* bucket = deep_bucket->bucket;
if (bucket->alloc_size - bucket->free_size == 0) {
continue;
}
deep_bucket->UnparseForStats(buffer);
}
}
}
void DeepHeapProfile::DeepBucketTable::WriteForBucketFile(
const char* prefix, int dump_count, char raw_buffer[], int buffer_size) {
char filename[100];
snprintf(filename, sizeof(filename),
"%s.%05d.%04d.buckets", prefix, getpid(), dump_count);
RawFD fd = RawOpenForWriting(filename);
RAW_DCHECK(fd != kIllegalRawFD, "");
TextBuffer buffer(raw_buffer, buffer_size, fd);
for (int i = 0; i < table_size_; i++) {
for (DeepBucket* deep_bucket = table_[i];
deep_bucket != NULL;
deep_bucket = deep_bucket->next) {
Bucket* bucket = deep_bucket->bucket;
if (deep_bucket->is_logged) {
continue;
}
if (!deep_bucket->is_mmap &&
bucket->alloc_size - bucket->free_size <= 64) {
continue;
}
deep_bucket->UnparseForBucketFile(&buffer);
deep_bucket->is_logged = true;
}
}
buffer.Flush();
RawClose(fd);
}
void DeepHeapProfile::DeepBucketTable::ResetCommittedSize() {
for (int i = 0; i < table_size_; i++) {
for (DeepBucket* deep_bucket = table_[i];
deep_bucket != NULL;
deep_bucket = deep_bucket->next) {
deep_bucket->committed_size = 0;
}
}
}
void DeepHeapProfile::DeepBucketTable::ResetIsLogged() {
for (int i = 0; i < table_size_; i++) {
for (DeepBucket* deep_bucket = table_[i];
deep_bucket != NULL;
deep_bucket = deep_bucket->next) {
deep_bucket->is_logged = false;
}
}
}
void DeepHeapProfile::DeepBucketTable::AddToHashValue(
uintptr_t add, uintptr_t* hash_value) {
*hash_value += add;
*hash_value += *hash_value << 10;
*hash_value ^= *hash_value >> 6;
}
void DeepHeapProfile::DeepBucketTable::FinishHashValue(uintptr_t* hash_value) {
*hash_value += *hash_value << 3;
*hash_value ^= *hash_value >> 11;
}
void DeepHeapProfile::RegionStats::Initialize() {
virtual_bytes_ = 0;
committed_bytes_ = 0;
}
uint64 DeepHeapProfile::RegionStats::Record(
const MemoryResidenceInfoGetterInterface* memory_residence_info_getter,
uint64 first_address,
uint64 last_address,
TextBuffer* buffer) {
uint64 committed = 0;
virtual_bytes_ += static_cast<size_t>(last_address - first_address + 1);
if (memory_residence_info_getter)
committed = memory_residence_info_getter->CommittedSize(first_address,
last_address,
buffer);
committed_bytes_ += committed;
return committed;
}
void DeepHeapProfile::RegionStats::Unparse(const char* name,
TextBuffer* buffer) {
buffer->AppendString(name, 25);
buffer->AppendChar(' ');
buffer->AppendLong(virtual_bytes_, 12);
buffer->AppendChar(' ');
buffer->AppendLong(committed_bytes_, 12);
buffer->AppendString("\n", 0);
}
void DeepHeapProfile::GlobalStats::SnapshotMaps(
const MemoryResidenceInfoGetterInterface* memory_residence_info_getter,
DeepHeapProfile* deep_profile,
TextBuffer* mmap_dump_buffer) {
MemoryRegionMap::LockHolder lock_holder;
ProcMapsIterator::Buffer procmaps_iter_buffer;
ProcMapsIterator procmaps_iter(0, &procmaps_iter_buffer);
uint64 vma_start_addr, vma_last_addr, offset;
int64 inode;
char* flags;
char* filename;
enum MapsRegionType type;
for (int i = 0; i < NUMBER_OF_MAPS_REGION_TYPES; ++i) {
all_[i].Initialize();
unhooked_[i].Initialize();
}
profiled_mmap_.Initialize();
MemoryRegionMap::RegionIterator mmap_iter =
MemoryRegionMap::BeginRegionLocked();
DeepBucket* deep_bucket = NULL;
if (mmap_iter != MemoryRegionMap::EndRegionLocked()) {
deep_bucket = GetInformationOfMemoryRegion(
mmap_iter, memory_residence_info_getter, deep_profile);
}
while (procmaps_iter.Next(&vma_start_addr, &vma_last_addr,
&flags, &offset, &inode, &filename)) {
if (mmap_dump_buffer) {
char buffer[1024];
int written = procmaps_iter.FormatLine(buffer, sizeof(buffer),
vma_start_addr, vma_last_addr,
flags, offset, inode, filename, 0);
mmap_dump_buffer->AppendString(buffer, 0);
}
vma_last_addr -= 1;
if (strcmp("[vsyscall]", filename) == 0) {
continue;
}
type = ABSENT;
if (filename[0] == '/') {
if (flags[2] == 'x')
type = FILE_EXEC;
else
type = FILE_NONEXEC;
} else if (filename[0] == '\0' || filename[0] == '\n') {
type = ANONYMOUS;
} else if (strcmp(filename, "[stack]") == 0) {
type = STACK;
} else {
type = OTHER;
}
uint64 vma_total = all_[type].Record(
memory_residence_info_getter, vma_start_addr, vma_last_addr, NULL);
uint64 vma_subtotal = 0;
if (MemoryRegionMap::IsRecordingLocked()) {
uint64 cursor = vma_start_addr;
bool first = true;
do {
if (!first) {
cursor = mmap_iter->end_addr;
++mmap_iter;
if (mmap_iter != MemoryRegionMap::EndRegionLocked()) {
deep_bucket = GetInformationOfMemoryRegion(
mmap_iter, memory_residence_info_getter, deep_profile);
}
}
first = false;
uint64 last_address_of_unhooked;
if (mmap_iter == MemoryRegionMap::EndRegionLocked() ||
mmap_iter->start_addr > vma_last_addr) {
last_address_of_unhooked = vma_last_addr;
} else {
last_address_of_unhooked = mmap_iter->start_addr - 1;
}
if (last_address_of_unhooked + 1 > cursor) {
RAW_CHECK(cursor >= vma_start_addr,
"Wrong calculation for unhooked");
RAW_CHECK(last_address_of_unhooked <= vma_last_addr,
"Wrong calculation for unhooked");
uint64 committed_size = unhooked_[type].Record(
memory_residence_info_getter,
cursor,
last_address_of_unhooked,
mmap_dump_buffer);
vma_subtotal += committed_size;
if (mmap_dump_buffer) {
mmap_dump_buffer->AppendString(" ", 0);
mmap_dump_buffer->AppendPtr(cursor, 0);
mmap_dump_buffer->AppendString(" - ", 0);
mmap_dump_buffer->AppendPtr(last_address_of_unhooked + 1, 0);
mmap_dump_buffer->AppendString(" unhooked ", 0);
mmap_dump_buffer->AppendInt64(committed_size, 0);
mmap_dump_buffer->AppendString(" / ", 0);
mmap_dump_buffer->AppendInt64(
last_address_of_unhooked - cursor + 1, 0);
mmap_dump_buffer->AppendString("\n", 0);
}
cursor = last_address_of_unhooked + 1;
}
if (mmap_iter != MemoryRegionMap::EndRegionLocked() &&
mmap_iter->start_addr <= vma_last_addr &&
mmap_dump_buffer) {
bool trailing = mmap_iter->start_addr < vma_start_addr;
bool continued = mmap_iter->end_addr - 1 > vma_last_addr;
uint64 partial_first_address, partial_last_address;
if (trailing)
partial_first_address = vma_start_addr;
else
partial_first_address = mmap_iter->start_addr;
if (continued)
partial_last_address = vma_last_addr;
else
partial_last_address = mmap_iter->end_addr - 1;
uint64 committed_size = 0;
if (memory_residence_info_getter)
committed_size = memory_residence_info_getter->CommittedSize(
partial_first_address, partial_last_address, mmap_dump_buffer);
vma_subtotal += committed_size;
mmap_dump_buffer->AppendString(trailing ? " (" : " ", 0);
mmap_dump_buffer->AppendPtr(mmap_iter->start_addr, 0);
mmap_dump_buffer->AppendString(trailing ? ")" : " ", 0);
mmap_dump_buffer->AppendString("-", 0);
mmap_dump_buffer->AppendString(continued ? "(" : " ", 0);
mmap_dump_buffer->AppendPtr(mmap_iter->end_addr, 0);
mmap_dump_buffer->AppendString(continued ? ")" : " ", 0);
mmap_dump_buffer->AppendString(" hooked ", 0);
mmap_dump_buffer->AppendInt64(committed_size, 0);
mmap_dump_buffer->AppendString(" / ", 0);
mmap_dump_buffer->AppendInt64(
partial_last_address - partial_first_address + 1, 0);
mmap_dump_buffer->AppendString(" @ ", 0);
if (deep_bucket != NULL) {
mmap_dump_buffer->AppendInt(deep_bucket->id, 0, false);
} else {
mmap_dump_buffer->AppendInt(0, 0, false);
}
mmap_dump_buffer->AppendString("\n", 0);
}
} while (mmap_iter != MemoryRegionMap::EndRegionLocked() &&
mmap_iter->end_addr - 1 <= vma_last_addr);
}
if (vma_total != vma_subtotal) {
char buffer[1024];
int written = procmaps_iter.FormatLine(buffer, sizeof(buffer),
vma_start_addr, vma_last_addr,
flags, offset, inode, filename, 0);
RAW_VLOG(0, "[%d] Mismatched total in VMA %" PRId64 ":"
"%" PRId64 " (%" PRId64 ")",
getpid(), vma_total, vma_subtotal, vma_total - vma_subtotal);
RAW_VLOG(0, "[%d] in %s", getpid(), buffer);
}
}
RegionStats all_total;
RegionStats unhooked_total;
for (int i = 0; i < NUMBER_OF_MAPS_REGION_TYPES; ++i) {
all_total.AddAnotherRegionStat(all_[i]);
unhooked_total.AddAnotherRegionStat(unhooked_[i]);
}
size_t absent_virtual = profiled_mmap_.virtual_bytes() +
unhooked_total.virtual_bytes() -
all_total.virtual_bytes();
if (absent_virtual > 0)
all_[ABSENT].AddToVirtualBytes(absent_virtual);
size_t absent_committed = profiled_mmap_.committed_bytes() +
unhooked_total.committed_bytes() -
all_total.committed_bytes();
if (absent_committed > 0)
all_[ABSENT].AddToCommittedBytes(absent_committed);
}
void DeepHeapProfile::GlobalStats::SnapshotAllocations(
DeepHeapProfile* deep_profile) {
profiled_malloc_.Initialize();
deep_profile->heap_profile_->address_map_->Iterate(RecordAlloc, deep_profile);
}
void DeepHeapProfile::GlobalStats::Unparse(TextBuffer* buffer) {
RegionStats all_total;
RegionStats unhooked_total;
for (int i = 0; i < NUMBER_OF_MAPS_REGION_TYPES; ++i) {
all_total.AddAnotherRegionStat(all_[i]);
unhooked_total.AddAnotherRegionStat(unhooked_[i]);
}
buffer->AppendString("# total (", 0);
buffer->AppendUnsignedLong(all_total.committed_bytes(), 0);
buffer->AppendString(") ", 0);
buffer->AppendChar(all_total.committed_bytes() ==
profiled_mmap_.committed_bytes() +
unhooked_total.committed_bytes() ? '=' : '!');
buffer->AppendString("= profiled-mmap (", 0);
buffer->AppendUnsignedLong(profiled_mmap_.committed_bytes(), 0);
buffer->AppendString(") + nonprofiled-* (", 0);
buffer->AppendUnsignedLong(unhooked_total.committed_bytes(), 0);
buffer->AppendString(")\n", 0);
buffer->AppendString("", 26);
buffer->AppendString(kVirtualLabel, 12);
buffer->AppendChar(' ');
buffer->AppendString(kCommittedLabel, 12);
buffer->AppendString("\n", 0);
all_total.Unparse("total", buffer);
all_[ABSENT].Unparse("absent", buffer);
all_[FILE_EXEC].Unparse("file-exec", buffer);
all_[FILE_NONEXEC].Unparse("file-nonexec", buffer);
all_[ANONYMOUS].Unparse("anonymous", buffer);
all_[STACK].Unparse("stack", buffer);
all_[OTHER].Unparse("other", buffer);
unhooked_total.Unparse("nonprofiled-total", buffer);
unhooked_[ABSENT].Unparse("nonprofiled-absent", buffer);
unhooked_[ANONYMOUS].Unparse("nonprofiled-anonymous", buffer);
unhooked_[FILE_EXEC].Unparse("nonprofiled-file-exec", buffer);
unhooked_[FILE_NONEXEC].Unparse("nonprofiled-file-nonexec", buffer);
unhooked_[STACK].Unparse("nonprofiled-stack", buffer);
unhooked_[OTHER].Unparse("nonprofiled-other", buffer);
profiled_mmap_.Unparse("profiled-mmap", buffer);
profiled_malloc_.Unparse("profiled-malloc", buffer);
}
void DeepHeapProfile::GlobalStats::RecordAlloc(const void* pointer,
AllocValue* alloc_value,
DeepHeapProfile* deep_profile) {
uint64 address = reinterpret_cast<uintptr_t>(pointer);
size_t committed = deep_profile->memory_residence_info_getter_->CommittedSize(
address, address + alloc_value->bytes - 1, NULL);
DeepBucket* deep_bucket = deep_profile->deep_table_.Lookup(
alloc_value->bucket(),
#if defined(TYPE_PROFILING)
LookupType(pointer),
#endif
false);
deep_bucket->committed_size += committed;
deep_profile->stats_.profiled_malloc_.AddToVirtualBytes(alloc_value->bytes);
deep_profile->stats_.profiled_malloc_.AddToCommittedBytes(committed);
}
DeepHeapProfile::DeepBucket*
DeepHeapProfile::GlobalStats::GetInformationOfMemoryRegion(
const MemoryRegionMap::RegionIterator& mmap_iter,
const MemoryResidenceInfoGetterInterface* memory_residence_info_getter,
DeepHeapProfile* deep_profile) {
size_t committed = deep_profile->memory_residence_info_getter_->
CommittedSize(mmap_iter->start_addr, mmap_iter->end_addr - 1, NULL);
Bucket* bucket = MemoryRegionMap::GetBucket(
mmap_iter->call_stack_depth, mmap_iter->call_stack);
DeepBucket* deep_bucket = NULL;
if (bucket != NULL) {
deep_bucket = deep_profile->deep_table_.Lookup(
bucket,
#if defined(TYPE_PROFILING)
NULL,
#endif
true);
if (deep_bucket != NULL)
deep_bucket->committed_size += committed;
}
profiled_mmap_.AddToVirtualBytes(
mmap_iter->end_addr - mmap_iter->start_addr);
profiled_mmap_.AddToCommittedBytes(committed);
return deep_bucket;
}
void DeepHeapProfile::WriteProcMaps(const char* prefix,
char raw_buffer[],
int buffer_size) {
char filename[100];
snprintf(filename, sizeof(filename),
"%s.%05d.maps", prefix, static_cast<int>(getpid()));
RawFD fd = RawOpenForWriting(filename);
RAW_DCHECK(fd != kIllegalRawFD, "");
int length;
bool wrote_all;
length = tcmalloc::FillProcSelfMaps(raw_buffer, buffer_size, &wrote_all);
RAW_DCHECK(wrote_all, "");
RAW_DCHECK(length <= buffer_size, "");
RawWrite(fd, raw_buffer, length);
RawClose(fd);
}
#else
DeepHeapProfile::DeepHeapProfile(HeapProfileTable* heap_profile,
const char* prefix,
enum PageFrameType pageframe_type)
: heap_profile_(heap_profile) {
}
DeepHeapProfile::~DeepHeapProfile() {
}
void DeepHeapProfile::DumpOrderedProfile(const char* reason,
char raw_buffer[],
int buffer_size,
RawFD fd) {
}
#endif