This source file includes following definitions.
- active_thread_id_
- Hook
- Unhook
- OpenLogFile
- CloseLogFile
- LockedRecursionDetected
- OnTrack
- OnUntrack
- SetLogName
- CompareCallStackIdItems
- DumpLeaks
#include <algorithm>
#include <windows.h>
#include <tlhelp32.h>
#include <map>
#include "tools/memory_watcher/memory_watcher.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/metrics/stats_counters.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "tools/memory_watcher/call_stack.h"
#include "tools/memory_watcher/preamble_patcher.h"
static base::StatsCounter mem_in_use("MemoryInUse.Bytes");
static base::StatsCounter mem_in_use_blocks("MemoryInUse.Blocks");
static base::StatsCounter mem_in_use_allocs("MemoryInUse.Allocs");
static base::StatsCounter mem_in_use_frees("MemoryInUse.Frees");
MemoryWatcher::MemoryWatcher()
: file_(NULL),
hooked_(false),
active_thread_id_(0) {
MemoryHook::Initialize();
CallStack::Initialize();
block_map_ = new CallStackMap();
Hook();
}
MemoryWatcher::~MemoryWatcher() {
Unhook();
CloseLogFile();
delete block_map_;
}
void MemoryWatcher::Hook() {
DCHECK(!hooked_);
MemoryHook::RegisterWatcher(this);
hooked_ = true;
}
void MemoryWatcher::Unhook() {
if (hooked_) {
MemoryHook::UnregisterWatcher(this);
hooked_ = false;
}
}
void MemoryWatcher::OpenLogFile() {
DCHECK(file_ == NULL);
file_name_ = "memwatcher";
if (!log_name_.empty()) {
file_name_ += ".";
file_name_ += log_name_;
}
file_name_ += ".log";
char buf[16];
file_name_ += _itoa(GetCurrentProcessId(), buf, 10);
std::string tmp_name(file_name_);
tmp_name += ".tmp";
file_ = fopen(tmp_name.c_str(), "w+");
}
void MemoryWatcher::CloseLogFile() {
if (file_ != NULL) {
fclose(file_);
file_ = NULL;
std::wstring tmp_name = base::ASCIIToWide(file_name_);
tmp_name += L".tmp";
base::Move(base::FilePath(tmp_name),
base::FilePath(base::ASCIIToWide(file_name_)));
}
}
bool MemoryWatcher::LockedRecursionDetected() const {
if (!active_thread_id_) return false;
DWORD thread_id = GetCurrentThreadId();
return thread_id == active_thread_id_;
}
void MemoryWatcher::OnTrack(HANDLE heap, int32 id, int32 size) {
if (size == 0)
return;
if (LockedRecursionDetected())
return;
AllocationStack* stack = new AllocationStack(size);
if (!stack->Valid()) return;
{
base::AutoLock lock(block_map_lock_);
CallStackMap::iterator block_it = block_map_->find(id);
if (block_it != block_map_->end()) {
#if 0
active_thread_id_ = GetCurrentThreadId();
PrivateAllocatorString output;
block_it->second->ToString(&output);
stack->ToString(&output);
#endif
delete block_it->second;
}
active_thread_id_ = 0;
(*block_map_)[id] = stack;
}
mem_in_use.Add(size);
mem_in_use_blocks.Increment();
mem_in_use_allocs.Increment();
}
void MemoryWatcher::OnUntrack(HANDLE heap, int32 id, int32 size) {
DCHECK_GE(size, 0);
if (size == 0)
return;
if (LockedRecursionDetected())
return;
{
base::AutoLock lock(block_map_lock_);
active_thread_id_ = GetCurrentThreadId();
CallStackMap::iterator it = block_map_->find(id);
if (it != block_map_->end()) {
AllocationStack* stack = it->second;
DCHECK(stack->size() == size);
block_map_->erase(id);
delete stack;
} else {
size = 0;
}
active_thread_id_ = 0;
}
mem_in_use.Add(-size);
mem_in_use_blocks.Decrement();
mem_in_use_frees.Increment();
}
void MemoryWatcher::SetLogName(char* log_name) {
if (!log_name)
return;
log_name_ = log_name;
}
static bool CompareCallStackIdItems(MemoryWatcher::StackTrack* left,
MemoryWatcher::StackTrack* right) {
return left->size > right->size;
}
void MemoryWatcher::DumpLeaks() {
if (!hooked_)
return;
Unhook();
base::AutoLock lock(block_map_lock_);
active_thread_id_ = GetCurrentThreadId();
OpenLogFile();
CallStackIdMap stack_map;
for (CallStackMap::iterator block_it = block_map_->begin();
block_it != block_map_->end(); ++block_it) {
AllocationStack* stack = block_it->second;
int32 stack_hash = stack->hash();
int32 alloc_block_size = stack->size();
CallStackIdMap::iterator it = stack_map.find(stack_hash);
if (it == stack_map.end()) {
StackTrack tracker;
tracker.count = 1;
tracker.size = alloc_block_size;
tracker.stack = stack;
stack_map[stack_hash] = tracker;
} else {
it->second.count++;
it->second.size += alloc_block_size;
}
}
std::vector<StackTrack*, PrivateHookAllocator<int32> >
stack_tracks(stack_map.size());
CallStackIdMap::iterator it = stack_map.begin();
for (size_t i = 0; i < stack_tracks.size(); ++i) {
stack_tracks[i] = &(it->second);
++it;
}
sort(stack_tracks.begin(), stack_tracks.end(), CompareCallStackIdItems);
int32 total_bytes = 0;
int32 total_blocks = 0;
for (size_t i = 0; i < stack_tracks.size(); ++i) {
StackTrack* stack_track = stack_tracks[i];
fwprintf(file_, L"%d bytes, %d allocs, #%d\n",
stack_track->size, stack_track->count, i);
total_bytes += stack_track->size;
total_blocks += stack_track->count;
CallStack* stack = stack_track->stack;
PrivateAllocatorString output;
stack->ToString(&output);
fprintf(file_, "%s", output.c_str());
}
fprintf(file_, "Total Leaks: %d\n", total_blocks);
fprintf(file_, "Total Stacks: %d\n", stack_tracks.size());
fprintf(file_, "Total Bytes: %d\n", total_bytes);
CloseLogFile();
}