Commit 57fa6dc6 authored by cbentzel@chromium.org's avatar cbentzel@chromium.org

Remove memory_watcher tool as well as --memory-profile command line flag.

This tool has not been maintained or used in a while, and tools such as UMDH work well as a replacement.

BUG=383024

Review URL: https://codereview.chromium.org/314253003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278607 0039d316-1c4b-4281-b951-d872f2087c98
parent 2871fcd2
......@@ -164,13 +164,6 @@
'../base/allocator/allocator.gyp:*',
],
}],
# Don't enable dependencies that don't work on Win64.
['target_arch!="x64"', {
'dependencies': [
# TODO(jschuh) Enable Win64 Memory Watcher. crbug.com/176877
'../tools/memory_watcher/memory_watcher.gyp:*',
],
}],
],
'dependencies': [
'../chrome_elf/chrome_elf.gyp:*',
......
......@@ -47,7 +47,6 @@
#include "base/strings/string_util.h"
#include "chrome/common/child_process_logging.h"
#include "sandbox/win/src/sandbox.h"
#include "tools/memory_watcher/memory_watcher.h"
#include "ui/base/resource/resource_bundle_win.h"
#endif
......@@ -134,15 +133,6 @@ extern int ServiceProcessMain(const content::MainFunctionParams&);
namespace {
#if defined(OS_WIN)
const wchar_t kProfilingDll[] = L"memory_watcher.dll";
// Load the memory profiling DLL. All it needs to be activated
// is to be loaded. Return true on success, false otherwise.
bool LoadMemoryProfiler() {
HMODULE prof_module = LoadLibrary(kProfilingDll);
return prof_module != NULL;
}
// Early versions of Chrome incorrectly registered a chromehtml: URL handler,
// which gives us nothing but trouble. Avoid launching chrome this way since
// some apps fail to properly escape arguments.
......@@ -225,16 +215,6 @@ static void AdjustLinuxOOMScore(const std::string& process_type) {
}
#endif // defined(OS_LINUX)
// Enable the heap profiler if the appropriate command-line switch is
// present, bailing out of the app we can't.
void EnableHeapProfiler(const CommandLine& command_line) {
#if defined(OS_WIN)
if (command_line.HasSwitch(switches::kMemoryProfiling))
if (!LoadMemoryProfiler())
exit(-1);
#endif
}
// Returns true if this subprocess type needs the ResourceBundle initialized
// and resources loaded.
bool SubprocessNeedsResourceBundle(const std::string& process_type) {
......@@ -672,9 +652,6 @@ void ChromeMainDelegate::PreSandboxStartup() {
startup_timer_.reset(new base::StatsScope<base::StatsCounterTimer>
(*stats_counter_timer_));
// Enable the heap profiler as early as possible!
EnableHeapProfiler(command_line);
// Enable Message Loop related state asap.
if (command_line.HasSwitch(switches::kMessageLoopHistogrammer))
base::MessageLoop::EnableHistogrammer(true);
......
......@@ -1721,7 +1721,6 @@ void ChromeContentBrowserClient::AppendExtraCommandLineSwitches(
switches::kEnableStreamlinedHostedApps,
switches::kEnableWatchdog,
switches::kEnableWebBasedSignin,
switches::kMemoryProfiling,
switches::kMessageLoopHistogrammer,
switches::kOutOfProcessPdf,
switches::kPlaybackMode,
......@@ -1748,15 +1747,14 @@ void ChromeContentBrowserClient::AppendExtraCommandLineSwitches(
command_line->CopySwitchesFrom(browser_command_line, kSwitchNames,
arraysize(kSwitchNames));
} else if (process_type == switches::kPluginProcess) {
static const char* const kSwitchNames[] = {
#if defined(OS_CHROMEOS)
static const char* const kSwitchNames[] = {
chromeos::switches::kLoginProfile,
#endif
switches::kMemoryProfiling,
};
command_line->CopySwitchesFrom(browser_command_line, kSwitchNames,
arraysize(kSwitchNames));
#endif
} else if (process_type == switches::kZygoteProcess) {
static const char* const kSwitchNames[] = {
// Load (in-process) Pepper plugins in-process in the zygote pre-sandbox.
......
......@@ -832,10 +832,6 @@ const char kManualEnhancedBookmarksOptout[] =
// Forces the maximum disk space to be used by the media cache, in bytes.
const char kMediaCacheSize[] = "media-cache-size";
// Enables dynamic loading of the Memory Profiler DLL, which will trace all
// memory allocations during the run.
const char kMemoryProfiling[] = "memory-profile";
// Enables histograming of tasks served by MessageLoop. See
// about:histograms/Loop for results, which show frequency of messages on each
// thread, including APC count, object signalling count, etc.
......
......@@ -236,7 +236,6 @@ extern const char kSupervisedUserSyncToken[];
extern const char kManualEnhancedBookmarks[];
extern const char kManualEnhancedBookmarksOptout[];
extern const char kMediaCacheSize[];
extern const char kMemoryProfiling[];
extern const char kMessageLoopHistogrammer[];
extern const char kMetricsRecordingOnly[];
extern const char kNetLogLevel[];
......
include_rules = [
"+ui/gfx",
]
memory_watcher is a library that can be linked into chromium to trace the
memory allocations. It works by hooking the system allocation/deallocation
functions, and recording the actions.
To use memory_watcher in chromium:
(1) Compile the memory_watcher library (it is part of the solution by default)
(2) Run chromium with these flags "--memory-profile -no-sandbox"
(The instrumentation doesn't work with the sandbox)
(3) Hit ctrl-alt-D to generate a dump of the memory allocations.
This will create a log file called memorywatcher.logXXXX for every
chromium process (where XXXX is the pid).
The log file is a human readable text format, which can be further analyzed
using the helpers in the "scripts/" directory.
// Copyright (c) 2010 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 "tools/memory_watcher/call_stack.h"
#include <shlwapi.h>
#include <tlhelp32.h>
#include "base/strings/string_number_conversions.h"
#include "tools/memory_watcher/memory_hook.h"
// Typedefs for explicit dynamic linking with functions exported from
// dbghelp.dll.
typedef BOOL (__stdcall *t_StackWalk64)(DWORD, HANDLE, HANDLE,
LPSTACKFRAME64, PVOID,
PREAD_PROCESS_MEMORY_ROUTINE64,
PFUNCTION_TABLE_ACCESS_ROUTINE64,
PGET_MODULE_BASE_ROUTINE64,
PTRANSLATE_ADDRESS_ROUTINE64);
typedef PVOID (__stdcall *t_SymFunctionTableAccess64)(HANDLE, DWORD64);
typedef DWORD64 (__stdcall *t_SymGetModuleBase64)(HANDLE, DWORD64);
typedef BOOL (__stdcall *t_SymCleanup)(HANDLE);
typedef BOOL (__stdcall *t_SymGetSymFromAddr64)(HANDLE, DWORD64,
PDWORD64, PIMAGEHLP_SYMBOL64);
typedef BOOL (__stdcall *t_SymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD,
PIMAGEHLP_LINE64);
typedef BOOL (__stdcall *t_SymInitialize)(HANDLE, PCTSTR, BOOL);
typedef DWORD (__stdcall *t_SymGetOptions)(void);
typedef DWORD (__stdcall *t_SymSetOptions)(DWORD);
typedef BOOL (__stdcall *t_SymGetSearchPath)(HANDLE, PTSTR, DWORD);
typedef DWORD64 (__stdcall *t_SymLoadModule64)(HANDLE, HANDLE, PCSTR,
PCSTR, DWORD64, DWORD);
typedef BOOL (__stdcall *t_SymGetModuleInfo64)(HANDLE, DWORD64,
PIMAGEHLP_MODULE64);
// static
base::Lock CallStack::dbghelp_lock_;
// static
bool CallStack::dbghelp_loaded_ = false;
// static
DWORD CallStack::active_thread_id_ = 0;
static t_StackWalk64 pStackWalk64 = NULL;
static t_SymCleanup pSymCleanup = NULL;
static t_SymGetSymFromAddr64 pSymGetSymFromAddr64 = NULL;
static t_SymFunctionTableAccess64 pSymFunctionTableAccess64 = NULL;
static t_SymGetModuleBase64 pSymGetModuleBase64 = NULL;
static t_SymGetLineFromAddr64 pSymGetLineFromAddr64 = NULL;
static t_SymInitialize pSymInitialize = NULL;
static t_SymGetOptions pSymGetOptions = NULL;
static t_SymSetOptions pSymSetOptions = NULL;
static t_SymGetModuleInfo64 pSymGetModuleInfo64 = NULL;
static t_SymGetSearchPath pSymGetSearchPath = NULL;
static t_SymLoadModule64 pSymLoadModule64 = NULL;
#define LOADPROC(module, name) do { \
p##name = reinterpret_cast<t_##name>(GetProcAddress(module, #name)); \
if (p##name == NULL) return false; \
} while (0)
// This code has to be VERY careful to not induce any allocations, as memory
// watching code may cause recursion, which may obscure the stack for the truly
// offensive issue. We use this function to break into a debugger, and it
// is guaranteed to not do any allocations (in fact, not do anything).
static void UltraSafeDebugBreak() {
_asm int(3);
}
// static
bool CallStack::LoadDbgHelp() {
if (!dbghelp_loaded_) {
base::AutoLock Lock(dbghelp_lock_);
// Re-check if we've loaded successfully now that we have the lock.
if (dbghelp_loaded_)
return true;
// Load dbghelp.dll, and obtain pointers to the exported functions that we
// will be using.
HMODULE dbghelp_module = LoadLibrary(L"dbghelp.dll");
if (dbghelp_module) {
LOADPROC(dbghelp_module, StackWalk64);
LOADPROC(dbghelp_module, SymFunctionTableAccess64);
LOADPROC(dbghelp_module, SymGetModuleBase64);
LOADPROC(dbghelp_module, SymCleanup);
LOADPROC(dbghelp_module, SymGetSymFromAddr64);
LOADPROC(dbghelp_module, SymGetLineFromAddr64);
LOADPROC(dbghelp_module, SymInitialize);
LOADPROC(dbghelp_module, SymGetOptions);
LOADPROC(dbghelp_module, SymSetOptions);
LOADPROC(dbghelp_module, SymGetModuleInfo64);
LOADPROC(dbghelp_module, SymGetSearchPath);
LOADPROC(dbghelp_module, SymLoadModule64);
dbghelp_loaded_ = true;
} else {
UltraSafeDebugBreak();
return false;
}
}
return dbghelp_loaded_;
}
// Load the symbols for generating stack traces.
static bool LoadSymbols(HANDLE process_handle) {
static bool symbols_loaded = false;
if (symbols_loaded) return true;
BOOL ok;
// Initialize the symbol engine.
ok = pSymInitialize(process_handle, /* hProcess */
NULL, /* UserSearchPath */
FALSE); /* fInvadeProcess */
if (!ok) return false;
DWORD options = pSymGetOptions();
options |= SYMOPT_LOAD_LINES;
options |= SYMOPT_FAIL_CRITICAL_ERRORS;
options |= SYMOPT_UNDNAME;
options = pSymSetOptions(options);
const DWORD kMaxSearchPath = 1024;
TCHAR buf[kMaxSearchPath] = {0};
ok = pSymGetSearchPath(process_handle, buf, kMaxSearchPath);
if (!ok)
return false;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,
GetCurrentProcessId());
if (snapshot == INVALID_HANDLE_VALUE)
return false;
MODULEENTRY32W module;
module.dwSize = sizeof(module); // Set the size of the structure.
BOOL cont = Module32FirstW(snapshot, &module);
while (cont) {
DWORD64 base;
// NOTE the SymLoadModule64 function has the peculiarity of accepting a
// both unicode and ASCII strings even though the parameter is PSTR.
base = pSymLoadModule64(process_handle,
0,
reinterpret_cast<PSTR>(module.szExePath),
reinterpret_cast<PSTR>(module.szModule),
reinterpret_cast<DWORD64>(module.modBaseAddr),
module.modBaseSize);
if (base == 0) {
int err = GetLastError();
if (err != ERROR_MOD_NOT_FOUND && err != ERROR_INVALID_HANDLE)
return false;
}
cont = Module32NextW(snapshot, &module);
}
CloseHandle(snapshot);
symbols_loaded = true;
return true;
}
CallStack::SymbolCache* CallStack::symbol_cache_;
bool CallStack::Initialize() {
// We need to delay load the symbol cache until after
// the MemoryHook heap is alive.
symbol_cache_ = new SymbolCache();
return LoadDbgHelp();
}
CallStack::CallStack() {
static LONG callstack_id = 0;
frame_count_ = 0;
hash_ = 0;
id_ = InterlockedIncrement(&callstack_id);
valid_ = false;
if (!dbghelp_loaded_) {
UltraSafeDebugBreak(); // Initialize should have been called.
return;
}
GetStackTrace();
}
bool CallStack::IsEqual(const CallStack &target) {
if (frame_count_ != target.frame_count_)
return false; // They can't be equal if the sizes are different.
// Walk the frames array until we
// either find a mismatch, or until we reach the end of the call stacks.
for (int index = 0; index < frame_count_; index++) {
if (frames_[index] != target.frames_[index])
return false; // Found a mismatch. They are not equal.
}
// Reached the end of the call stacks. They are equal.
return true;
}
void CallStack::AddFrame(DWORD_PTR pc) {
DCHECK(frame_count_ < kMaxTraceFrames);
frames_[frame_count_++] = pc;
// Create a unique id for this CallStack.
pc = pc + (frame_count_ * 13); // Alter the PC based on position in stack.
hash_ = ~hash_ + (pc << 15);
hash_ = hash_ ^ (pc >> 12);
hash_ = hash_ + (pc << 2);
hash_ = hash_ ^ (pc >> 4);
hash_ = hash_ * 2057;
hash_ = hash_ ^ (pc >> 16);
}
bool CallStack::LockedRecursionDetected() const {
if (!active_thread_id_) return false;
DWORD thread_id = GetCurrentThreadId();
// TODO(jar): Perchance we should use atomic access to member.
return thread_id == active_thread_id_;
}
bool CallStack::GetStackTrace() {
if (LockedRecursionDetected())
return false;
// Initialize the context record.
CONTEXT context;
memset(&context, 0, sizeof(context));
context.ContextFlags = CONTEXT_FULL;
__asm call x
__asm x: pop eax
__asm mov context.Eip, eax
__asm mov context.Ebp, ebp
__asm mov context.Esp, esp
STACKFRAME64 frame;
memset(&frame, 0, sizeof(frame));
#ifdef _M_IX86
DWORD image_type = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif
NOT IMPLEMENTED!
#endif
HANDLE current_process = GetCurrentProcess();
HANDLE current_thread = GetCurrentThread();
// Walk the stack.
unsigned int count = 0;
{
AutoDbgHelpLock thread_monitoring_lock;
while (count < kMaxTraceFrames) {
count++;
if (!pStackWalk64(image_type,
current_process,
current_thread,
&frame,
&context,
0,
pSymFunctionTableAccess64,
pSymGetModuleBase64,
NULL))
break; // Couldn't trace back through any more frames.
if (frame.AddrFrame.Offset == 0)
continue; // End of stack.
// Push this frame's program counter onto the provided CallStack.
AddFrame((DWORD_PTR)frame.AddrPC.Offset);
}
valid_ = true;
}
return true;
}
void CallStack::ToString(PrivateAllocatorString* output) {
static const int kStackWalkMaxNameLen = MAX_SYM_NAME;
HANDLE current_process = GetCurrentProcess();
if (!LoadSymbols(current_process)) {
*output = "Error";
return;
}
base::AutoLock lock(dbghelp_lock_);
// Iterate through each frame in the call stack.
for (int32 index = 0; index < frame_count_; index++) {
PrivateAllocatorString line;
DWORD_PTR intruction_pointer = frame(index);
SymbolCache::iterator it;
it = symbol_cache_->find(intruction_pointer);
if (it != symbol_cache_->end()) {
line = it->second;
} else {
// Try to locate a symbol for this frame.
DWORD64 symbol_displacement = 0;
ULONG64 buffer[(sizeof(IMAGEHLP_SYMBOL64) +
sizeof(TCHAR)*kStackWalkMaxNameLen +
sizeof(ULONG64) - 1) / sizeof(ULONG64)];
IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(buffer);
memset(buffer, 0, sizeof(buffer));
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
symbol->MaxNameLength = kStackWalkMaxNameLen;
BOOL ok = pSymGetSymFromAddr64(current_process, // hProcess
intruction_pointer, // Address
&symbol_displacement, // Displacement
symbol); // Symbol
if (ok) {
// Try to locate more source information for the symbol.
IMAGEHLP_LINE64 Line;
memset(&Line, 0, sizeof(Line));
Line.SizeOfStruct = sizeof(Line);
DWORD line_displacement;
ok = pSymGetLineFromAddr64(current_process,
intruction_pointer,
&line_displacement,
&Line);
if (ok) {
// Skip junk symbols from our internal stuff.
if (strstr(symbol->Name, "CallStack::") ||
strstr(symbol->Name, "MemoryWatcher::") ||
strstr(symbol->Name, "Perftools_") ||
strstr(symbol->Name, "MemoryHook::") ) {
// Just record a blank string.
(*symbol_cache_)[intruction_pointer] = "";
continue;
}
line += " ";
line += static_cast<char*>(Line.FileName);
line += " (";
// TODO(jar): get something like this template to work :-/
// line += IntToCustomString<PrivateAllocatorString>(Line.LineNumber);
// ...and then delete this line, which uses std::string.
line += base::IntToString(Line.LineNumber).c_str();
line += "): ";
line += symbol->Name;
line += "\n";
} else {
line += " unknown (0):";
line += symbol->Name;
line += "\n";
}
} else {
// OK - couldn't get any info. Try for the module.
IMAGEHLP_MODULE64 module_info;
module_info.SizeOfStruct = sizeof(module_info);
if (pSymGetModuleInfo64(current_process, intruction_pointer,
&module_info)) {
line += " (";
line += static_cast<char*>(module_info.ModuleName);
line += ")\n";
} else {
line += " ???\n";
}
}
}
(*symbol_cache_)[intruction_pointer] = line;
*output += line;
}
*output += "==================\n";
}
base::Lock AllocationStack::freelist_lock_;
AllocationStack* AllocationStack::freelist_ = NULL;
void* AllocationStack::operator new(size_t size) {
DCHECK(size == sizeof(AllocationStack));
{
base::AutoLock lock(freelist_lock_);
if (freelist_ != NULL) {
AllocationStack* stack = freelist_;
freelist_ = freelist_->next_;
stack->next_ = NULL;
return stack;
}
}
return MemoryHook::Alloc(size);
}
void AllocationStack::operator delete(void* ptr) {
AllocationStack *stack = reinterpret_cast<AllocationStack*>(ptr);
base::AutoLock lock(freelist_lock_);
DCHECK(stack->next_ == NULL);
stack->next_ = freelist_;
freelist_ = stack;
}
// Copyright (c) 2010 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.
//
// Parts of this module come from:
// http://www.codeproject.com/KB/applications/visualleakdetector.aspx
// by Dan Moulding.
// http://www.codeproject.com/KB/threads/StackWalker.aspx
// by Jochen Kalmbach
#ifndef TOOLS_MEMORY_WATCHER_CALL_STACK_H_
#define TOOLS_MEMORY_WATCHER_CALL_STACK_H_
#include <windows.h>
#include <dbghelp.h>
#include <functional>
#include <map>
#include <string>
#include "base/logging.h"
#include "base/synchronization/lock.h"
#include "tools/memory_watcher/memory_watcher.h"
// The CallStack Class
// A stack where memory has been allocated.
class CallStack {
public:
// Initialize for tracing CallStacks.
static bool Initialize();
CallStack();
virtual ~CallStack() {}
// Get a hash for this CallStack.
// Identical stack traces will have matching hashes.
int32 hash() { return hash_; }
// Get a unique ID for this CallStack.
// No two CallStacks will ever have the same ID. The ID is a monotonically
// increasing number. Newer CallStacks always have larger IDs.
int32 id() { return id_; }
// Retrieves the frame at the specified index.
DWORD_PTR frame(int32 index) {
DCHECK(index < frame_count_ && index >= 0);
return frames_[index];
}
// Compares the CallStack to another CallStack
// for equality. Two CallStacks are equal if they are the same size and if
// every frame in each is identical to the corresponding frame in the other.
bool IsEqual(const CallStack &target);
typedef std::basic_string<char, std::char_traits<char>,
PrivateHookAllocator<char> > PrivateAllocatorString;
// Convert the callstack to a string stored in output.
void CallStack::ToString(PrivateAllocatorString* output);
//
bool Valid() const { return valid_; }
private:
// The maximum number of frames to trace.
static const int kMaxTraceFrames = 32;
// Pushes a frame's program counter onto the CallStack.
void AddFrame(DWORD_PTR programcounter);
// Traces the stack, starting from this function, up to kMaxTraceFrames
// frames.
bool GetStackTrace();
// Functions for manipulating the frame list.
void ClearFrames();
// Dynamically load the DbgHelp library and supporting routines that we
// will use.
static bool LoadDbgHelp();
static void LockDbgHelp() {
dbghelp_lock_.Acquire();
active_thread_id_ = GetCurrentThreadId();
}
static void UnlockDbgHelp() {
active_thread_id_ = 0;
dbghelp_lock_.Release();
}
class AutoDbgHelpLock {
public:
AutoDbgHelpLock() {
CallStack::LockDbgHelp();
}
~AutoDbgHelpLock() {
CallStack::UnlockDbgHelp();
}
};
// Check to see if this thread is already processing a stack.
bool LockedRecursionDetected() const;
// According to http://msdn2.microsoft.com/en-us/library/ms680650(VS.85).aspx
// "All DbgHelp functions, such as this one, are single threaded. Therefore,
// calls from more than one thread to this function will likely result in
// unexpected behavior or memory corruption. To avoid this, you must
// synchromize all concurrent calls from one thread to this function."
//
// dbghelp_lock_ is used to serialize access across all calls to the DbgHelp
// library. This may be overly conservative (serializing them all together),
// but does guarantee correctness.
static base::Lock dbghelp_lock_;
// Record the fact that dbghelp has been loaded.
// Changes to this variable are protected by dbghelp_lock_.
// It will only changes once... from false to true.
static bool dbghelp_loaded_;
// To prevent infinite recursion due to unexpected side effects in libraries,
// we track the thread_id of the thread currently holding the dbghelp_lock_.
// We avoid re-aquiring said lock and return an !valid_ instance when we
// detect recursion.
static DWORD active_thread_id_;
int frame_count_; // Current size (in frames)
DWORD_PTR frames_[kMaxTraceFrames];
int32 hash_;
int32 id_;
// Indicate is this is a valid stack.
// This is false if recursion precluded a real stack generation.
bool valid_;
// Cache ProgramCounter -> Symbol lookups.
// This cache is not thread safe.
typedef std::map<int32, PrivateAllocatorString, std::less<int32>,
PrivateHookAllocator<int32> > SymbolCache;
static SymbolCache* symbol_cache_;
DISALLOW_COPY_AND_ASSIGN(CallStack);
};
// An AllocationStack is a type of CallStack which represents a CallStack where
// memory has been allocated. This class is also a list item, so that it can
// be easilly allocated and deallocated from its static singly-linked-list of
// free instances.
class AllocationStack : public CallStack {
public:
explicit AllocationStack(int32 size)
: next_(NULL), size_(size), CallStack() {}
// We maintain a freelist of the AllocationStacks.
void* operator new(size_t s);
void operator delete(void*p);
int32 size() const { return size_; }
private:
AllocationStack* next_; // Pointer used when on the freelist.
int32 size_; // Size of block allocated.
static AllocationStack* freelist_;
static base::Lock freelist_lock_;
DISALLOW_COPY_AND_ASSIGN(AllocationStack);
};
#endif // TOOLS_MEMORY_WATCHER_CALL_STACK_H_
// Copyright (c) 2012 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.
// The memory_watcher.dll is hooked by simply linking it. When we get the
// windows notification that this DLL is loaded, we do a few things:
// 1) Register a Hot Key.
// Only one process can hook the Hot Key, so one will get it, and the
// others will silently fail.
// 2) Create a thread to wait on an event.
// Since only one process will get the HotKey, it will be responsible for
// notifying all process when it's time to do something. Each process
// will have a thread waiting for communication from the master to dump
// the callstacks.
#include <windows.h>
#include "base/at_exit.h"
#include "tools/memory_watcher/memory_watcher.h"
#include "tools/memory_watcher/hotkey.h"
class MemoryWatcherDumpKey; // Defined below.
static wchar_t* kDumpEvent = L"MemWatcher.DumpEvent";
static base::AtExitManager* g_memory_watcher_exit_manager = NULL;
static MemoryWatcher* g_memory_watcher = NULL;
static MemoryWatcherDumpKey* g_hotkey_handler = NULL;
static HANDLE g_dump_event = INVALID_HANDLE_VALUE;
static HANDLE g_quit_event = INVALID_HANDLE_VALUE;
static HANDLE g_watcher_thread = INVALID_HANDLE_VALUE;
// A HotKey to dump the memory statistics.
class MemoryWatcherDumpKey : public HotKeyHandler {
public:
MemoryWatcherDumpKey(UINT modifiers, UINT vkey)
: HotKeyHandler(modifiers, vkey) {}
virtual void OnHotKey(UINT, WPARAM, LPARAM) {
SetEvent(g_dump_event);
}
};
// Creates the global memory watcher.
void CreateMemoryWatcher() {
g_memory_watcher_exit_manager = new base::AtExitManager();
g_memory_watcher = new MemoryWatcher();
// Register ALT-CONTROL-D to Dump Memory stats.
g_hotkey_handler = new MemoryWatcherDumpKey(MOD_ALT|MOD_CONTROL, 0x44);
}
// Deletes the global memory watcher.
void DeleteMemoryWatcher() {
if (g_hotkey_handler)
delete g_hotkey_handler;
g_hotkey_handler = NULL;
if (g_memory_watcher)
delete g_memory_watcher;
g_memory_watcher = NULL;
// Intentionly leak g_memory_watcher_exit_manager.
}
// Thread for watching for key events.
DWORD WINAPI ThreadMain(LPVOID) {
bool stopping = false;
HANDLE events[2] = { g_dump_event, g_quit_event };
while (!stopping) {
DWORD rv = WaitForMultipleObjects(2, events, FALSE, INFINITE);
switch (rv) {
case WAIT_OBJECT_0:
if (g_memory_watcher) {
g_memory_watcher->DumpLeaks();
}
stopping = true;
break;
case WAIT_OBJECT_0 + 1:
stopping = true;
break;
default:
NOTREACHED();
break;
}
}
return 0;
}
// Creates the background thread
void CreateBackgroundThread() {
// Create a named event which can be used to notify
// all watched processes.
g_dump_event = CreateEvent(0, TRUE, FALSE, kDumpEvent);
DCHECK(g_dump_event != NULL);
// Create a local event which can be used to kill our
// background thread.
g_quit_event = CreateEvent(0, TRUE, FALSE, NULL);
DCHECK(g_quit_event != NULL);
// Create the background thread.
g_watcher_thread = CreateThread(0,
0,
ThreadMain,
0,
0,
0);
DCHECK(g_watcher_thread != NULL);
}
// Tell the background thread to stop.
void StopBackgroundThread() {
// Send notification to our background thread.
SetEvent(g_quit_event);
// Wait for our background thread to die.
DWORD rv = WaitForSingleObject(g_watcher_thread, INFINITE);
DCHECK(rv == WAIT_OBJECT_0);
// Cleanup our global handles.
CloseHandle(g_quit_event);
CloseHandle(g_dump_event);
CloseHandle(g_watcher_thread);
}
bool IsChromeExe() {
return GetModuleHandleA("chrome.exe") != NULL;
}
extern "C" {
// DllMain is the windows entry point to this DLL.
// We use the entry point as the mechanism for starting and stopping
// the MemoryWatcher.
BOOL WINAPI DllMain(HINSTANCE dll_instance, DWORD reason,
LPVOID reserved) {
if (!IsChromeExe())
return FALSE;
switch (reason) {
case DLL_PROCESS_ATTACH:
CreateMemoryWatcher();
CreateBackgroundThread();
break;
case DLL_PROCESS_DETACH:
DeleteMemoryWatcher();
StopBackgroundThread();
break;
}
return TRUE;
}
__declspec(dllexport) void __cdecl SetLogName(char* name) {
g_memory_watcher->SetLogName(name);
}
} // extern "C"
// Copyright (c) 2009 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.
#ifndef TOOLS_MEMORY_WATCHER_HOTKEY_H_
#define TOOLS_MEMORY_WATCHER_HOTKEY_H_
#include "ui/gfx/rect.h"
#include "ui/gfx/win/msg_util.h"
#include "ui/gfx/win/window_impl.h"
// HotKey handler.
// Programs wishing to register a hotkey can use this.
class HotKeyHandler : public gfx::WindowImpl {
public:
HotKeyHandler(UINT modifiers, UINT vk)
: modifiers_(modifiers),
vkey_(vk) {
Start();
}
~HotKeyHandler() { Stop(); }
CR_BEGIN_MSG_MAP_EX(HotKeyHandler)
CR_MSG_WM_HOTKEY(OnHotKey)
CR_END_MSG_MAP()
private:
static const int hotkey_id = 0x0000baba;
bool Start() {
set_window_style(WS_POPUP);
Init(NULL, gfx::Rect());
return RegisterHotKey(hwnd(), hotkey_id, modifiers_, vkey_) == TRUE;
}
void Stop() {
UnregisterHotKey(hwnd(), hotkey_id);
DestroyWindow(hwnd());
}
// Handle the registered Hotkey being pressed.
virtual void OnHotKey(UINT /*uMsg*/,
WPARAM /*wParam*/,
LPARAM /*lParam*/) = 0;
UINT modifiers_;
UINT vkey_;
};
#endif // TOOLS_MEMORY_WATCHER_HOTKEY_H_
// Copyright (c) 2012 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.
/*
* Table of relevant information about how to decode the ModR/M byte.
* Based on information in the IA 32 Intel Architecture
* Software Developer's Manual Volume 2: Instruction Set Reference.
*/
#include "mini_disassembler.h"
#include "mini_disassembler_types.h"
namespace sidestep {
const ModrmEntry MiniDisassembler::s_ia16_modrm_map_[] = {
// mod == 00
/* r/m == 000 */ { false, false, OS_ZERO },
/* r/m == 001 */ { false, false, OS_ZERO },
/* r/m == 010 */ { false, false, OS_ZERO },
/* r/m == 011 */ { false, false, OS_ZERO },
/* r/m == 100 */ { false, false, OS_ZERO },
/* r/m == 101 */ { false, false, OS_ZERO },
/* r/m == 110 */ { true, false, OS_WORD },
/* r/m == 111 */ { false, false, OS_ZERO },
// mod == 01
/* r/m == 000 */ { true, false, OS_BYTE },
/* r/m == 001 */ { true, false, OS_BYTE },
/* r/m == 010 */ { true, false, OS_BYTE },
/* r/m == 011 */ { true, false, OS_BYTE },
/* r/m == 100 */ { true, false, OS_BYTE },
/* r/m == 101 */ { true, false, OS_BYTE },
/* r/m == 110 */ { true, false, OS_BYTE },
/* r/m == 111 */ { true, false, OS_BYTE },
// mod == 10
/* r/m == 000 */ { true, false, OS_WORD },
/* r/m == 001 */ { true, false, OS_WORD },
/* r/m == 010 */ { true, false, OS_WORD },
/* r/m == 011 */ { true, false, OS_WORD },
/* r/m == 100 */ { true, false, OS_WORD },
/* r/m == 101 */ { true, false, OS_WORD },
/* r/m == 110 */ { true, false, OS_WORD },
/* r/m == 111 */ { true, false, OS_WORD },
// mod == 11
/* r/m == 000 */ { false, false, OS_ZERO },
/* r/m == 001 */ { false, false, OS_ZERO },
/* r/m == 010 */ { false, false, OS_ZERO },
/* r/m == 011 */ { false, false, OS_ZERO },
/* r/m == 100 */ { false, false, OS_ZERO },
/* r/m == 101 */ { false, false, OS_ZERO },
/* r/m == 110 */ { false, false, OS_ZERO },
/* r/m == 111 */ { false, false, OS_ZERO }
};
const ModrmEntry MiniDisassembler::s_ia32_modrm_map_[] = {
// mod == 00
/* r/m == 000 */ { false, false, OS_ZERO },
/* r/m == 001 */ { false, false, OS_ZERO },
/* r/m == 010 */ { false, false, OS_ZERO },
/* r/m == 011 */ { false, false, OS_ZERO },
/* r/m == 100 */ { false, true, OS_ZERO },
/* r/m == 101 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 110 */ { false, false, OS_ZERO },
/* r/m == 111 */ { false, false, OS_ZERO },
// mod == 01
/* r/m == 000 */ { true, false, OS_BYTE },
/* r/m == 001 */ { true, false, OS_BYTE },
/* r/m == 010 */ { true, false, OS_BYTE },
/* r/m == 011 */ { true, false, OS_BYTE },
/* r/m == 100 */ { true, true, OS_BYTE },
/* r/m == 101 */ { true, false, OS_BYTE },
/* r/m == 110 */ { true, false, OS_BYTE },
/* r/m == 111 */ { true, false, OS_BYTE },
// mod == 10
/* r/m == 000 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 001 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 010 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 011 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 100 */ { true, true, OS_DOUBLE_WORD },
/* r/m == 101 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 110 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 111 */ { true, false, OS_DOUBLE_WORD },
// mod == 11
/* r/m == 000 */ { false, false, OS_ZERO },
/* r/m == 001 */ { false, false, OS_ZERO },
/* r/m == 010 */ { false, false, OS_ZERO },
/* r/m == 011 */ { false, false, OS_ZERO },
/* r/m == 100 */ { false, false, OS_ZERO },
/* r/m == 101 */ { false, false, OS_ZERO },
/* r/m == 110 */ { false, false, OS_ZERO },
/* r/m == 111 */ { false, false, OS_ZERO },
};
}; // namespace sidestep
This source diff could not be displayed because it is too large. You can view the blob instead.
// Copyright (c) 2006-2008 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.
// Static class for hooking Win32 API routines.
// Some notes about how to hook Memory Allocation Routines in Windows.
//
// For our purposes we do not hook the libc routines. There are two
// reasons for this. First, the libc routines all go through HeapAlloc
// anyway. So, it's redundant to log both HeapAlloc and malloc.
// Second, it can be tricky to hook in both static and dynamic linkages
// of libc.
#include <windows.h>
#include "memory_hook.h"
#include "memory_watcher.h"
#include "preamble_patcher.h"
// Calls GetProcAddress, but casts to the correct type.
#define GET_PROC_ADDRESS(hmodule, name) \
( (Type_##name)(::GetProcAddress(hmodule, #name)) )
// Macro to declare Patch functions.
#define DECLARE_PATCH(name) Patch<Type_##name> patch_##name
// Macro to install Patch functions.
#define INSTALL_PATCH(name) do { \
patch_##name.set_original(GET_PROC_ADDRESS(hkernel32, ##name)); \
patch_##name.Install(&Perftools_##name); \
} while (0)
// Macro to install Patch functions.
#define INSTALL_NTDLLPATCH(name) do { \
patch_##name.set_original(GET_PROC_ADDRESS(hntdll, ##name)); \
patch_##name.Install(&Perftools_##name); \
} while (0)
// Macro to uninstall Patch functions.
#define UNINSTALL_PATCH(name) patch_##name.Uninstall();
// Windows APIs to be hooked
// HeapAlloc routines
typedef HANDLE (WINAPI *Type_HeapCreate)(DWORD flOptions,
SIZE_T dwInitialSize,
SIZE_T dwMaximumSize);
typedef BOOL (WINAPI *Type_HeapDestroy)(HANDLE hHeap);
typedef LPVOID (WINAPI *Type_HeapAlloc)(HANDLE hHeap, DWORD dwFlags,
DWORD_PTR dwBytes);
typedef LPVOID (WINAPI *Type_HeapReAlloc)(HANDLE hHeap, DWORD dwFlags,
LPVOID lpMem, SIZE_T dwBytes);
typedef BOOL (WINAPI *Type_HeapFree)(HANDLE hHeap, DWORD dwFlags,
LPVOID lpMem);
// GlobalAlloc routines
typedef HGLOBAL (WINAPI *Type_GlobalAlloc)(UINT uFlags, SIZE_T dwBytes);
typedef HGLOBAL (WINAPI *Type_GlobalReAlloc)(HGLOBAL hMem, SIZE_T dwBytes,
UINT uFlags);
typedef HGLOBAL (WINAPI *Type_GlobalFree)(HGLOBAL hMem);
// LocalAlloc routines
typedef HLOCAL (WINAPI *Type_LocalAlloc)(UINT uFlags, SIZE_T uBytes);
typedef HLOCAL (WINAPI *Type_LocalReAlloc)(HLOCAL hMem, SIZE_T uBytes,
UINT uFlags);
typedef HLOCAL (WINAPI *Type_LocalFree)(HLOCAL hMem);
// A Windows-API equivalent of mmap and munmap, for "anonymous regions"
typedef LPVOID (WINAPI *Type_VirtualAllocEx)(HANDLE process, LPVOID address,
SIZE_T size, DWORD type,
DWORD protect);
typedef BOOL (WINAPI *Type_VirtualFreeEx)(HANDLE process, LPVOID address,
SIZE_T size, DWORD type);
// A Windows-API equivalent of mmap and munmap, for actual files
typedef LPVOID (WINAPI *Type_MapViewOfFile)(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap);
typedef LPVOID (WINAPI *Type_MapViewOfFileEx)(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap,
LPVOID lpBaseAddress);
typedef BOOL (WINAPI *Type_UnmapViewOfFile)(LPVOID lpBaseAddress);
typedef DWORD (WINAPI *Type_NtUnmapViewOfSection)(HANDLE process,
LPVOID lpBaseAddress);
// Patch is a template for keeping the pointer to the original
// hooked routine, the function to call when hooked, and the
// stub routine which is patched.
template<class T>
class Patch {
public:
// Constructor. Does not hook the function yet.
Patch<T>()
: original_function_(NULL),
patch_function_(NULL),
stub_function_(NULL) {
}
// Destructor. Unhooks the function if it has been hooked.
~Patch<T>() {
Uninstall();
}
// Patches original function with func.
// Must have called set_original to set the original function.
void Install(T func) {
patch_function_ = func;
CHECK(patch_function_ != NULL);
CHECK(original_function_ != NULL);
CHECK(stub_function_ == NULL);
CHECK(sidestep::SIDESTEP_SUCCESS ==
sidestep::PreamblePatcher::Patch(original_function_,
patch_function_, &stub_function_));
}
// Un-patches the function.
void Uninstall() {
if (stub_function_)
sidestep::PreamblePatcher::Unpatch(original_function_,
patch_function_, stub_function_);
stub_function_ = NULL;
}
// Set the function to be patched.
void set_original(T original) { original_function_ = original; }
// Get the original function being patched.
T original() { return original_function_; }
// Get the patched function. (e.g. the replacement function)
T patched() { return patch_function_; }
// Access to the stub for calling the original function
// while it is patched.
T operator()() {
DCHECK(stub_function_);
return stub_function_;
}
private:
// The function that we plan to patch.
T original_function_;
// The function to replace the original with.
T patch_function_;
// To unpatch, we also need to keep around a "stub" that points to the
// pre-patched Windows function.
T stub_function_;
};
// All Windows memory-allocation routines call through to one of these.
DECLARE_PATCH(HeapCreate);
DECLARE_PATCH(HeapDestroy);
DECLARE_PATCH(HeapAlloc);
DECLARE_PATCH(HeapReAlloc);
DECLARE_PATCH(HeapFree);
DECLARE_PATCH(VirtualAllocEx);
DECLARE_PATCH(VirtualFreeEx);
DECLARE_PATCH(MapViewOfFile);
DECLARE_PATCH(MapViewOfFileEx);
DECLARE_PATCH(UnmapViewOfFile);
DECLARE_PATCH(GlobalAlloc);
DECLARE_PATCH(GlobalReAlloc);
DECLARE_PATCH(GlobalFree);
DECLARE_PATCH(LocalAlloc);
DECLARE_PATCH(LocalReAlloc);
DECLARE_PATCH(LocalFree);
DECLARE_PATCH(NtUnmapViewOfSection);
// Our replacement functions.
static HANDLE WINAPI Perftools_HeapCreate(DWORD flOptions,
SIZE_T dwInitialSize,
SIZE_T dwMaximumSize) {
if (dwInitialSize > 4096)
dwInitialSize = 4096;
return patch_HeapCreate()(flOptions, dwInitialSize, dwMaximumSize);
}
static BOOL WINAPI Perftools_HeapDestroy(HANDLE hHeap) {
return patch_HeapDestroy()(hHeap);
}
static LPVOID WINAPI Perftools_HeapAlloc(HANDLE hHeap, DWORD dwFlags,
DWORD_PTR dwBytes) {
LPVOID rv = patch_HeapAlloc()(hHeap, dwFlags, dwBytes);
MemoryHook::hook()->OnTrack(hHeap, reinterpret_cast<int32>(rv), dwBytes);
return rv;
}
static BOOL WINAPI Perftools_HeapFree(HANDLE hHeap, DWORD dwFlags,
LPVOID lpMem) {
size_t size = 0;
if (lpMem != 0) {
size = HeapSize(hHeap, 0, lpMem); // Will crash if lpMem is 0.
// Note: size could be 0; HeapAlloc does allocate 0 length buffers.
}
MemoryHook::hook()->OnUntrack(hHeap, reinterpret_cast<int32>(lpMem), size);
return patch_HeapFree()(hHeap, dwFlags, lpMem);
}
static LPVOID WINAPI Perftools_HeapReAlloc(HANDLE hHeap, DWORD dwFlags,
LPVOID lpMem, SIZE_T dwBytes) {
// Don't call realloc, but instead do a free/malloc. The problem is that
// the builtin realloc may either expand a buffer, or it may simply
// just call free/malloc. If so, we will already have tracked the new
// block via Perftools_HeapAlloc.
LPVOID rv = Perftools_HeapAlloc(hHeap, dwFlags, dwBytes);
DCHECK_EQ((HEAP_REALLOC_IN_PLACE_ONLY & dwFlags), 0u);
// If there was an old buffer, now copy the data to the new buffer.
if (lpMem != 0) {
size_t size = HeapSize(hHeap, 0, lpMem);
if (size > dwBytes)
size = dwBytes;
// Note: size could be 0; HeapAlloc does allocate 0 length buffers.
memcpy(rv, lpMem, size);
Perftools_HeapFree(hHeap, dwFlags, lpMem);
}
return rv;
}
static LPVOID WINAPI Perftools_VirtualAllocEx(HANDLE process, LPVOID address,
SIZE_T size, DWORD type,
DWORD protect) {
bool already_committed = false;
if (address != NULL) {
MEMORY_BASIC_INFORMATION info;
CHECK(VirtualQuery(address, &info, sizeof(info)));
if (info.State & MEM_COMMIT) {
already_committed = true;
CHECK(size >= info.RegionSize);
}
}
bool reserving = (address == NULL) || (type & MEM_RESERVE);
bool committing = !already_committed && (type & MEM_COMMIT);
LPVOID result = patch_VirtualAllocEx()(process, address, size, type,
protect);
MEMORY_BASIC_INFORMATION info;
CHECK(VirtualQuery(result, &info, sizeof(info)));
size = info.RegionSize;
if (committing)
MemoryHook::hook()->OnTrack(0, reinterpret_cast<int32>(result), size);
return result;
}
static BOOL WINAPI Perftools_VirtualFreeEx(HANDLE process, LPVOID address,
SIZE_T size, DWORD type) {
int chunk_size = size;
MEMORY_BASIC_INFORMATION info;
CHECK(VirtualQuery(address, &info, sizeof(info)));
if (chunk_size == 0)
chunk_size = info.RegionSize;
bool decommit = (info.State & MEM_COMMIT) != 0;
if (decommit)
MemoryHook::hook()->OnUntrack(0, reinterpret_cast<int32>(address),
chunk_size);
return patch_VirtualFreeEx()(process, address, size, type);
}
static base::Lock known_maps_lock;
static std::map<void*, int> known_maps;
static LPVOID WINAPI Perftools_MapViewOfFileEx(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap,
LPVOID lpBaseAddress) {
// For this function pair, you always deallocate the full block of
// data that you allocate, so NewHook/DeleteHook is the right API.
LPVOID result = patch_MapViewOfFileEx()(hFileMappingObject, dwDesiredAccess,
dwFileOffsetHigh, dwFileOffsetLow,
dwNumberOfBytesToMap, lpBaseAddress);
{
base::AutoLock lock(known_maps_lock);
MEMORY_BASIC_INFORMATION info;
if (known_maps.find(result) == known_maps.end()) {
CHECK(VirtualQuery(result, &info, sizeof(info)));
// TODO(mbelshe): THIS map uses the standard heap!!!!
known_maps[result] = 1;
MemoryHook::hook()->OnTrack(0, reinterpret_cast<int32>(result),
info.RegionSize);
} else {
known_maps[result] = known_maps[result] + 1;
}
}
return result;
}
static LPVOID WINAPI Perftools_MapViewOfFile(HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap) {
return Perftools_MapViewOfFileEx(hFileMappingObject, dwDesiredAccess,
dwFileOffsetHigh, dwFileOffsetLow,
dwNumberOfBytesToMap, 0);
}
static BOOL WINAPI Perftools_UnmapViewOfFile(LPVOID lpBaseAddress) {
// This will call into NtUnmapViewOfSection().
return patch_UnmapViewOfFile()(lpBaseAddress);
}
static DWORD WINAPI Perftools_NtUnmapViewOfSection(HANDLE process,
LPVOID lpBaseAddress) {
// Some windows APIs call directly into this routine rather
// than calling UnmapViewOfFile. If we didn't trap this function,
// then we appear to have bogus leaks.
{
base::AutoLock lock(known_maps_lock);
MEMORY_BASIC_INFORMATION info;
CHECK(VirtualQuery(lpBaseAddress, &info, sizeof(info)));
if (known_maps.find(lpBaseAddress) != known_maps.end()) {
if (known_maps[lpBaseAddress] == 1) {
MemoryHook::hook()->OnUntrack(0, reinterpret_cast<int32>(lpBaseAddress),
info.RegionSize);
known_maps.erase(lpBaseAddress);
} else {
known_maps[lpBaseAddress] = known_maps[lpBaseAddress] - 1;
}
}
}
return patch_NtUnmapViewOfSection()(process, lpBaseAddress);
}
static HGLOBAL WINAPI Perftools_GlobalAlloc(UINT uFlags, SIZE_T dwBytes) {
// GlobalAlloc is built atop HeapAlloc anyway. So we don't track these.
// GlobalAlloc will internally call into HeapAlloc and we track there.
// Force all memory to be fixed.
uFlags &= ~GMEM_MOVEABLE;
HGLOBAL rv = patch_GlobalAlloc()(uFlags, dwBytes);
return rv;
}
static HGLOBAL WINAPI Perftools_GlobalFree(HGLOBAL hMem) {
return patch_GlobalFree()(hMem);
}
static HGLOBAL WINAPI Perftools_GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes,
UINT uFlags) {
// TODO(jar): [The following looks like a copy/paste typo from LocalRealloc.]
// GlobalDiscard is a macro which calls LocalReAlloc with size 0.
if (dwBytes == 0) {
return patch_GlobalReAlloc()(hMem, dwBytes, uFlags);
}
HGLOBAL rv = Perftools_GlobalAlloc(uFlags, dwBytes);
if (hMem != 0) {
size_t size = GlobalSize(hMem);
if (size > dwBytes)
size = dwBytes;
// Note: size could be 0; HeapAlloc does allocate 0 length buffers.
memcpy(rv, hMem, size);
Perftools_GlobalFree(hMem);
}
return rv;
}
static HLOCAL WINAPI Perftools_LocalAlloc(UINT uFlags, SIZE_T dwBytes) {
// LocalAlloc is built atop HeapAlloc anyway. So we don't track these.
// LocalAlloc will internally call into HeapAlloc and we track there.
// Force all memory to be fixed.
uFlags &= ~LMEM_MOVEABLE;
HLOCAL rv = patch_LocalAlloc()(uFlags, dwBytes);
return rv;
}
static HLOCAL WINAPI Perftools_LocalFree(HLOCAL hMem) {
return patch_LocalFree()(hMem);
}
static HLOCAL WINAPI Perftools_LocalReAlloc(HLOCAL hMem, SIZE_T dwBytes,
UINT uFlags) {
// LocalDiscard is a macro which calls LocalReAlloc with size 0.
if (dwBytes == 0) {
return patch_LocalReAlloc()(hMem, dwBytes, uFlags);
}
HGLOBAL rv = Perftools_LocalAlloc(uFlags, dwBytes);
if (hMem != 0) {
size_t size = LocalSize(hMem);
if (size > dwBytes)
size = dwBytes;
// Note: size could be 0; HeapAlloc does allocate 0 length buffers.
memcpy(rv, hMem, size);
Perftools_LocalFree(hMem);
}
return rv;
}
bool MemoryHook::hooked_ = false;
MemoryHook* MemoryHook::global_hook_ = NULL;
MemoryHook::MemoryHook()
: watcher_(NULL),
heap_(NULL) {
CreateHeap();
}
MemoryHook::~MemoryHook() {
// It's a bit dangerous to ever close this heap; MemoryWatchers may have
// used this heap for their tracking data. Closing the heap while any
// MemoryWatchers still exist is pretty dangerous.
CloseHeap();
}
bool MemoryHook::Initialize() {
if (global_hook_ == NULL)
global_hook_ = new MemoryHook();
return true;
}
bool MemoryHook::Hook() {
DCHECK(!hooked_);
if (!hooked_) {
DCHECK(global_hook_);
// Luckily, Patch() doesn't call malloc or windows alloc routines
// itself -- though it does call new (we can use PatchWithStub to
// get around that, and will need to if we need to patch new).
HMODULE hkernel32 = ::GetModuleHandle(L"kernel32");
CHECK(hkernel32 != NULL);
HMODULE hntdll = ::GetModuleHandle(L"ntdll");
CHECK(hntdll != NULL);
// Now that we've found all the functions, patch them
INSTALL_PATCH(HeapCreate);
INSTALL_PATCH(HeapDestroy);
INSTALL_PATCH(HeapAlloc);
INSTALL_PATCH(HeapReAlloc);
INSTALL_PATCH(HeapFree);
INSTALL_PATCH(VirtualAllocEx);
INSTALL_PATCH(VirtualFreeEx);
INSTALL_PATCH(MapViewOfFileEx);
INSTALL_PATCH(MapViewOfFile);
INSTALL_PATCH(UnmapViewOfFile);
INSTALL_NTDLLPATCH(NtUnmapViewOfSection);
INSTALL_PATCH(GlobalAlloc);
INSTALL_PATCH(GlobalReAlloc);
INSTALL_PATCH(GlobalFree);
INSTALL_PATCH(LocalAlloc);
INSTALL_PATCH(LocalReAlloc);
INSTALL_PATCH(LocalFree);
// We are finally completely hooked.
hooked_ = true;
}
return true;
}
bool MemoryHook::Unhook() {
if (hooked_) {
// We need to go back to the system malloc/etc at global destruct time,
// so objects that were constructed before tcmalloc, using the system
// malloc, can destroy themselves using the system free. This depends
// on DLLs unloading in the reverse order in which they load!
//
// We also go back to the default HeapAlloc/etc, just for consistency.
// Who knows, it may help avoid weird bugs in some situations.
UNINSTALL_PATCH(HeapCreate);
UNINSTALL_PATCH(HeapDestroy);
UNINSTALL_PATCH(HeapAlloc);
UNINSTALL_PATCH(HeapReAlloc);
UNINSTALL_PATCH(HeapFree);
UNINSTALL_PATCH(VirtualAllocEx);
UNINSTALL_PATCH(VirtualFreeEx);
UNINSTALL_PATCH(MapViewOfFile);
UNINSTALL_PATCH(MapViewOfFileEx);
UNINSTALL_PATCH(UnmapViewOfFile);
UNINSTALL_PATCH(NtUnmapViewOfSection);
UNINSTALL_PATCH(GlobalAlloc);
UNINSTALL_PATCH(GlobalReAlloc);
UNINSTALL_PATCH(GlobalFree);
UNINSTALL_PATCH(LocalAlloc);
UNINSTALL_PATCH(LocalReAlloc);
UNINSTALL_PATCH(LocalFree);
hooked_ = false;
}
return true;
}
bool MemoryHook::RegisterWatcher(MemoryObserver* watcher) {
DCHECK(global_hook_->watcher_ == NULL);
if (!hooked_)
Hook();
DCHECK(global_hook_);
global_hook_->watcher_ = watcher;
return true;
}
bool MemoryHook::UnregisterWatcher(MemoryObserver* watcher) {
DCHECK(hooked_);
DCHECK(global_hook_->watcher_ == watcher);
// TODO(jar): changing watcher_ here is very racy. Other threads may (without
// a lock) testing, and then calling through this value. We probably can't
// remove this until we are single threaded.
global_hook_->watcher_ = NULL;
// For now, since there are no more watchers, unhook memory.
return Unhook();
}
bool MemoryHook::CreateHeap() {
// Create a heap for our own memory.
DCHECK(heap_ == NULL);
heap_ = HeapCreate(0, 0, 0);
DCHECK(heap_ != NULL);
return heap_ != NULL;
}
bool MemoryHook::CloseHeap() {
DCHECK(heap_ != NULL);
HeapDestroy(heap_);
heap_ = NULL;
return true;
}
void MemoryHook::OnTrack(HANDLE heap, int32 id, int32 size) {
// Don't notify about allocations to our internal heap.
if (heap == heap_)
return;
if (watcher_)
watcher_->OnTrack(heap, id, size);
}
void MemoryHook::OnUntrack(HANDLE heap, int32 id, int32 size) {
// Don't notify about allocations to our internal heap.
if (heap == heap_)
return;
if (watcher_)
watcher_->OnUntrack(heap, id, size);
}
// Copyright (c) 2006-2008 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.
//
// Static class for hooking Win32 API routines. For now,
// we only add one watcher at a time.
//
// TODO(mbelshe): Support multiple watchers.
#ifndef MEMORY_WATCHER_MEMORY_HOOK_
#define MEMORY_WATCHER_MEMORY_HOOK_
#include "base/logging.h"
// When allocating memory for internal use with the MemoryHook,
// we must always use the MemoryHook's heap; otherwise, the memory
// gets tracked, and it becomes an infinite loop (allocation() calls
// MemoryHook() which calls allocation(), etc).
//
// PrivateHookAllocator is an STL-friendly Allocator so that STL lists,
// maps, etc can be used on the global MemoryHook's heap.
template <class T>
class PrivateHookAllocator {
public:
// These type definitions are needed for stl allocators.
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
PrivateHookAllocator() {}
// Allocate memory for STL.
pointer allocate(size_type n, const void * = 0) {
return reinterpret_cast<T*>(MemoryHook::Alloc(n * sizeof(T)));
}
// Deallocate memory for STL.
void deallocate(void* p, size_type) {
if (p)
MemoryHook::Free(p);
}
// Construct the object
void construct(pointer p, const T& val) {
new (reinterpret_cast<T*>(p))T(val);
}
// Destruct an object
void destroy(pointer p) { p->~T(); }
size_type max_size() const { return size_t(-1); }
template <class U>
struct rebind { typedef PrivateHookAllocator<U> other; };
template <class U>
PrivateHookAllocator(const PrivateHookAllocator<U>&) {}
};
template<class T, class U> inline
bool operator==(const PrivateHookAllocator<T>&,
const PrivateHookAllocator<U>&) {
return (true);
}
template<class T, class U> inline
bool operator!=(const PrivateHookAllocator<T>& left,
const PrivateHookAllocator<U>& right) {
return (!(left == right));
}
// Classes which monitor memory from these hooks implement
// the MemoryObserver interface.
class MemoryObserver {
public:
virtual ~MemoryObserver() {}
// Track a pointer. Will capture the current StackTrace.
virtual void OnTrack(HANDLE heap, int32 id, int32 size) = 0;
// Untrack a pointer, removing it from our list.
virtual void OnUntrack(HANDLE heap, int32 id, int32 size) = 0;
};
class MemoryHook : MemoryObserver {
public:
// Initialize the MemoryHook. Must be called before
// registering watchers. This can be called repeatedly,
// but is not thread safe.
static bool Initialize();
// Returns true is memory allocations and deallocations
// are being traced.
static bool hooked() { return hooked_ != NULL; }
// Register a class to receive memory allocation & deallocation
// callbacks. If we haven't hooked memory yet, this call will
// force memory hooking to start.
static bool RegisterWatcher(MemoryObserver* watcher);
// Register a class to stop receiving callbacks. If there are
// no more watchers, this call will unhook memory.
static bool UnregisterWatcher(MemoryObserver* watcher);
// MemoryHook provides a private heap for allocating
// unwatched memory.
static void* Alloc(size_t size) {
DCHECK(global_hook_ && global_hook_->heap_);
return HeapAlloc(global_hook_->heap_, 0, size);
}
static void Free(void* ptr) {
DCHECK(global_hook_ && global_hook_->heap_);
HeapFree(global_hook_->heap_, 0, ptr);
}
// Access the global hook. For internal use only from static "C"
// hooks.
static MemoryHook* hook() { return global_hook_; }
// MemoryObserver interface.
virtual void OnTrack(HANDLE hHeap, int32 id, int32 size);
virtual void OnUntrack(HANDLE hHeap, int32 id, int32 size);
private:
MemoryHook();
~MemoryHook();
// Enable memory tracing. When memory is 'hooked',
// MemoryWatchers which have registered will be called
// as memory is allocated and deallocated.
static bool Hook();
// Disables memory tracing.
static bool Unhook();
// Create our private heap
bool CreateHeap();
// Close our private heap.
bool CloseHeap();
MemoryObserver* watcher_;
HANDLE heap_; // An internal accounting heap.
static bool hooked_;
static MemoryHook* global_hook_;
};
#endif // MEMORY_WATCHER_MEMORY_HOOK_
// Copyright (c) 2010 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 <algorithm>
#include <windows.h>
#include <tlhelp32.h> // for CreateToolhelp32Snapshot()
#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();
// Register last - only after we're ready for notifications!
Hook();
}
MemoryWatcher::~MemoryWatcher() {
Unhook();
CloseLogFile();
// Pointers in the block_map are part of the MemoryHook heap. Be sure
// to delete the map before closing the heap.
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();
// TODO(jar): Perchance we should use atomic access to member.
return thread_id == active_thread_id_;
}
void MemoryWatcher::OnTrack(HANDLE heap, int32 id, int32 size) {
// Don't track zeroes. It's a waste of time.
if (size == 0)
return;
if (LockedRecursionDetected())
return;
// AllocationStack overrides new/delete to not allocate
// from the main heap.
AllocationStack* stack = new AllocationStack(size);
if (!stack->Valid()) return; // Recursion blocked generation of stack.
{
base::AutoLock lock(block_map_lock_);
// Ideally, we'd like to verify that the block being added
// here is not already in our list of tracked blocks. However,
// the lookup in our hash table is expensive and slows us too
// much.
CallStackMap::iterator block_it = block_map_->find(id);
if (block_it != block_map_->end()) {
#if 0 // Don't do this until stack->ToString() uses ONLY our heap.
active_thread_id_ = GetCurrentThreadId();
PrivateAllocatorString output;
block_it->second->ToString(&output);
// VLOG(1) << "First Stack size " << stack->size() << "was\n" << output;
stack->ToString(&output);
// VLOG(1) << "Second Stack size " << stack->size() << "was\n" << output;
#endif // 0
// TODO(jar): We should delete one stack, and keep the other, perhaps
// based on size.
// For now, just delete the first, and keep the second?
delete block_it->second;
}
// TODO(jar): Perchance we should use atomic access to member.
active_thread_id_ = 0; // Note: Only do this AFTER exiting above scope!
(*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);
// Don't bother with these.
if (size == 0)
return;
if (LockedRecursionDetected())
return;
{
base::AutoLock lock(block_map_lock_);
active_thread_id_ = GetCurrentThreadId();
// First, find the block in our block_map.
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 {
// Untracked item. This happens a fair amount, and it is
// normal. A lot of time elapses during process startup
// before the allocation routines are hooked.
size = 0; // Ignore size in tallies.
}
// TODO(jar): Perchance we should use atomic access to member.
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;
}
// Help sort lists of stacks based on allocation cost.
// Note: Sort based on allocation count is interesting too!
static bool CompareCallStackIdItems(MemoryWatcher::StackTrack* left,
MemoryWatcher::StackTrack* right) {
return left->size > right->size;
}
void MemoryWatcher::DumpLeaks() {
// We can only dump the leaks once. We'll cleanup the hooks here.
if (!hooked_)
return;
Unhook();
base::AutoLock lock(block_map_lock_);
active_thread_id_ = GetCurrentThreadId();
OpenLogFile();
// Aggregate contributions from each allocated block on per-stack basis.
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; // Temporary pointer into block_map_.
stack_map[stack_hash] = tracker;
} else {
it->second.count++;
it->second.size += alloc_block_size;
}
}
// Don't release lock yet, as block_map_ is still pointed into.
// Put references to StrackTracks into array for sorting.
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();
}
# 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.
{
'variables': {
'chromium_code': 1,
},
'targets': [
{
'target_name': 'memory_watcher',
'type': 'shared_library',
'dependencies': [
'../../base/base.gyp:base',
'../../ui/gfx/gfx.gyp:gfx',
'../../ui/gfx/gfx.gyp:gfx_geometry',
],
'defines': [
'BUILD_MEMORY_WATCHER',
],
'include_dirs': [
'../..',
],
# 4748 "/GS can not protect parameters and local variables from local
# buffer overrun because optimizations are disabled in function".
# 4740 "flow in or out of inline asm code suppresses global optimization"
# (result of __asm call x, __asm x:).
# Nothing to be done about these warnings.
'msvs_disabled_warnings': [ 4748, 4740 ],
'sources': [
'call_stack.cc',
'call_stack.h',
'dllmain.cc',
'hotkey.h',
'ia32_modrm_map.cc',
'ia32_opcode_map.cc',
'memory_hook.cc',
'memory_hook.h',
'memory_watcher.cc',
'memory_watcher.h',
'mini_disassembler.cc',
'preamble_patcher.cc',
'preamble_patcher.h',
'preamble_patcher_with_stub.cc',
],
},
],
}
// Copyright (c) 2006-2008 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.
// MemoryWatcher.
// The MemoryWatcher is a library that can be linked into any
// win32 application. It will override the default memory allocators
// and track call stacks for any allocations that are made. It can
// then be used to see what memory is in use.
#ifndef TOOLS_MEMORY_WATCHER_MEMORY_WATCHER_
#define TOOLS_MEMORY_WATCHER_MEMORY_WATCHER_
#include <map>
#include <functional>
#include "base/synchronization/lock.h"
#include "tools/memory_watcher/memory_hook.h"
class CallStack;
class AllocationStack;
// The MemoryWatcher installs allocation hooks and monitors
// allocations and frees.
class MemoryWatcher : MemoryObserver {
public:
struct StackTrack {
CallStack* stack;
int count;
int size;
};
typedef std::map<int32, AllocationStack*, std::less<int32>,
PrivateHookAllocator<int32> > CallStackMap;
typedef std::map<int32, StackTrack, std::less<int32>,
PrivateHookAllocator<int32> > CallStackIdMap;
typedef std::basic_string<char, std::char_traits<char>,
PrivateHookAllocator<char> > PrivateAllocatorString;
MemoryWatcher();
virtual ~MemoryWatcher();
// Dump all tracked pointers still in use.
void DumpLeaks();
// MemoryObserver interface.
virtual void OnTrack(HANDLE heap, int32 id, int32 size);
virtual void OnUntrack(HANDLE heap, int32 id, int32 size);
// Sets a name that appears in the generated file name.
void SetLogName(char* log_name);
private:
// Opens the logfile which we create.
void OpenLogFile();
// Close the logfile.
void CloseLogFile();
// Hook the memory hooks.
void Hook();
// Unhooks our memory hooks.
void Unhook();
// Check to see if this thread is already processing a block, and should not
// recurse.
bool LockedRecursionDetected() const;
// This is for logging.
FILE* file_;
bool hooked_; // True when this class has the memory_hooks hooked.
// Either 0, or else the threadID for a thread that is actively working on
// a stack track. Used to avoid recursive tracking.
DWORD active_thread_id_;
base::Lock block_map_lock_;
// The block_map provides quick lookups based on the allocation
// pointer. This is important for having fast round trips through
// malloc/free.
CallStackMap *block_map_;
// The file name for that log.
std::string file_name_;
// An optional name that appears in the log file name (used to differentiate
// logs).
std::string log_name_;
};
#endif // TOOLS_MEMORY_WATCHER_MEMORY_WATCHER_
// Copyright (c) 2012 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.
/*
* Implementation of MiniDisassembler.
*/
#include "mini_disassembler.h"
namespace sidestep {
MiniDisassembler::MiniDisassembler(bool operand_default_is_32_bits,
bool address_default_is_32_bits)
: operand_default_is_32_bits_(operand_default_is_32_bits),
address_default_is_32_bits_(address_default_is_32_bits) {
Initialize();
}
MiniDisassembler::MiniDisassembler()
: operand_default_is_32_bits_(true),
address_default_is_32_bits_(true) {
Initialize();
}
InstructionType MiniDisassembler::Disassemble(
unsigned char* start_byte,
unsigned int& instruction_bytes) {
// Clean up any state from previous invocations.
Initialize();
// Start by processing any prefixes.
unsigned char* current_byte = start_byte;
unsigned int size = 0;
InstructionType instruction_type = ProcessPrefixes(current_byte, size);
if (IT_UNKNOWN == instruction_type)
return instruction_type;
current_byte += size;
size = 0;
// Invariant: We have stripped all prefixes, and the operand_is_32_bits_
// and address_is_32_bits_ flags are correctly set.
instruction_type = ProcessOpcode(current_byte, 0, size);
// Check for error processing instruction
if ((IT_UNKNOWN == instruction_type_) || (IT_UNUSED == instruction_type_)) {
return IT_UNKNOWN;
}
current_byte += size;
// Invariant: operand_bytes_ indicates the total size of operands
// specified by the opcode and/or ModR/M byte and/or SIB byte.
// pCurrentByte points to the first byte after the ModR/M byte, or after
// the SIB byte if it is present (i.e. the first byte of any operands
// encoded in the instruction).
// We get the total length of any prefixes, the opcode, and the ModR/M and
// SIB bytes if present, by taking the difference of the original starting
// address and the current byte (which points to the first byte of the
// operands if present, or to the first byte of the next instruction if
// they are not). Adding the count of bytes in the operands encoded in
// the instruction gives us the full length of the instruction in bytes.
instruction_bytes += operand_bytes_ + (current_byte - start_byte);
// Return the instruction type, which was set by ProcessOpcode().
return instruction_type_;
}
void MiniDisassembler::Initialize() {
operand_is_32_bits_ = operand_default_is_32_bits_;
address_is_32_bits_ = address_default_is_32_bits_;
operand_bytes_ = 0;
have_modrm_ = false;
should_decode_modrm_ = false;
instruction_type_ = IT_UNKNOWN;
got_f2_prefix_ = false;
got_f3_prefix_ = false;
got_66_prefix_ = false;
}
InstructionType MiniDisassembler::ProcessPrefixes(unsigned char* start_byte,
unsigned int& size) {
InstructionType instruction_type = IT_GENERIC;
const Opcode& opcode = s_ia32_opcode_map_[0].table_[*start_byte];
switch (opcode.type_) {
case IT_PREFIX_ADDRESS:
address_is_32_bits_ = !address_default_is_32_bits_;
goto nochangeoperand;
case IT_PREFIX_OPERAND:
operand_is_32_bits_ = !operand_default_is_32_bits_;
nochangeoperand:
case IT_PREFIX:
if (0xF2 == (*start_byte))
got_f2_prefix_ = true;
else if (0xF3 == (*start_byte))
got_f3_prefix_ = true;
else if (0x66 == (*start_byte))
got_66_prefix_ = true;
instruction_type = opcode.type_;
size ++;
// we got a prefix, so add one and check next byte
ProcessPrefixes(start_byte + 1, size);
default:
break; // not a prefix byte
}
return instruction_type;
}
InstructionType MiniDisassembler::ProcessOpcode(unsigned char* start_byte,
unsigned int table_index,
unsigned int& size) {
const OpcodeTable& table = s_ia32_opcode_map_[table_index]; // Get our table
unsigned char current_byte = (*start_byte) >> table.shift_;
current_byte = current_byte & table.mask_; // Mask out the bits we will use
// Check whether the byte we have is inside the table we have.
if (current_byte < table.min_lim_ || current_byte > table.max_lim_) {
instruction_type_ = IT_UNKNOWN;
return instruction_type_;
}
const Opcode& opcode = table.table_[current_byte];
if (IT_UNUSED == opcode.type_) {
// This instruction is not used by the IA-32 ISA, so we indicate
// this to the user. Probably means that we were pointed to
// a byte in memory that was not the start of an instruction.
instruction_type_ = IT_UNUSED;
return instruction_type_;
} else if (IT_REFERENCE == opcode.type_) {
// We are looking at an opcode that has more bytes (or is continued
// in the ModR/M byte). Recursively find the opcode definition in
// the table for the opcode's next byte.
size++;
ProcessOpcode(start_byte + 1, opcode.table_index_, size);
return instruction_type_;
}
const SpecificOpcode* specific_opcode = (SpecificOpcode*)&opcode;
if (opcode.is_prefix_dependent_) {
if (got_f2_prefix_ && opcode.opcode_if_f2_prefix_.mnemonic_ != 0) {
specific_opcode = &opcode.opcode_if_f2_prefix_;
} else if (got_f3_prefix_ && opcode.opcode_if_f3_prefix_.mnemonic_ != 0) {
specific_opcode = &opcode.opcode_if_f3_prefix_;
} else if (got_66_prefix_ && opcode.opcode_if_66_prefix_.mnemonic_ != 0) {
specific_opcode = &opcode.opcode_if_66_prefix_;
}
}
// Inv: The opcode type is known.
instruction_type_ = specific_opcode->type_;
// Let's process the operand types to see if we have any immediate
// operands, and/or a ModR/M byte.
ProcessOperand(specific_opcode->flag_dest_);
ProcessOperand(specific_opcode->flag_source_);
ProcessOperand(specific_opcode->flag_aux_);
// Inv: We have processed the opcode and incremented operand_bytes_
// by the number of bytes of any operands specified by the opcode
// that are stored in the instruction (not registers etc.). Now
// we need to return the total number of bytes for the opcode and
// for the ModR/M or SIB bytes if they are present.
if (table.mask_ != 0xff) {
if (have_modrm_) {
// we're looking at a ModR/M byte so we're not going to
// count that into the opcode size
ProcessModrm(start_byte, size);
return IT_GENERIC;
} else {
// need to count the ModR/M byte even if it's just being
// used for opcode extension
size++;
return IT_GENERIC;
}
} else {
if (have_modrm_) {
// The ModR/M byte is the next byte.
size++;
ProcessModrm(start_byte + 1, size);
return IT_GENERIC;
} else {
size++;
return IT_GENERIC;
}
}
}
bool MiniDisassembler::ProcessOperand(int flag_operand) {
bool succeeded = true;
if (AM_NOT_USED == flag_operand)
return succeeded;
// Decide what to do based on the addressing mode.
switch (flag_operand & AM_MASK) {
// No ModR/M byte indicated by these addressing modes, and no
// additional (e.g. immediate) parameters.
case AM_A: // Direct address
case AM_F: // EFLAGS register
case AM_X: // Memory addressed by the DS:SI register pair
case AM_Y: // Memory addressed by the ES:DI register pair
case AM_IMPLICIT: // Parameter is implicit, occupies no space in
// instruction
break;
// There is a ModR/M byte but it does not necessarily need
// to be decoded.
case AM_C: // reg field of ModR/M selects a control register
case AM_D: // reg field of ModR/M selects a debug register
case AM_G: // reg field of ModR/M selects a general register
case AM_P: // reg field of ModR/M selects an MMX register
case AM_R: // mod field of ModR/M may refer only to a general register
case AM_S: // reg field of ModR/M selects a segment register
case AM_T: // reg field of ModR/M selects a test register
case AM_V: // reg field of ModR/M selects a 128-bit XMM register
have_modrm_ = true;
break;
// In these addressing modes, there is a ModR/M byte and it needs to be
// decoded. No other (e.g. immediate) params than indicated in ModR/M.
case AM_E: // Operand is either a general-purpose register or memory,
// specified by ModR/M byte
case AM_M: // ModR/M byte will refer only to memory
case AM_Q: // Operand is either an MMX register or memory (complex
// evaluation), specified by ModR/M byte
case AM_W: // Operand is either a 128-bit XMM register or memory (complex
// eval), specified by ModR/M byte
have_modrm_ = true;
should_decode_modrm_ = true;
break;
// These addressing modes specify an immediate or an offset value
// directly, so we need to look at the operand type to see how many
// bytes.
case AM_I: // Immediate data.
case AM_J: // Jump to offset.
case AM_O: // Operand is at offset.
switch (flag_operand & OT_MASK) {
case OT_B: // Byte regardless of operand-size attribute.
operand_bytes_ += OS_BYTE;
break;
case OT_C: // Byte or word, depending on operand-size attribute.
if (operand_is_32_bits_)
operand_bytes_ += OS_WORD;
else
operand_bytes_ += OS_BYTE;
break;
case OT_D: // Doubleword, regardless of operand-size attribute.
operand_bytes_ += OS_DOUBLE_WORD;
break;
case OT_DQ: // Double-quadword, regardless of operand-size attribute.
operand_bytes_ += OS_DOUBLE_QUAD_WORD;
break;
case OT_P: // 32-bit or 48-bit pointer, depending on operand-size
// attribute.
if (operand_is_32_bits_)
operand_bytes_ += OS_48_BIT_POINTER;
else
operand_bytes_ += OS_32_BIT_POINTER;
break;
case OT_PS: // 128-bit packed single-precision floating-point data.
operand_bytes_ += OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING;
break;
case OT_Q: // Quadword, regardless of operand-size attribute.
operand_bytes_ += OS_QUAD_WORD;
break;
case OT_S: // 6-byte pseudo-descriptor.
operand_bytes_ += OS_PSEUDO_DESCRIPTOR;
break;
case OT_SD: // Scalar Double-Precision Floating-Point Value
case OT_PD: // Unaligned packed double-precision floating point value
operand_bytes_ += OS_DOUBLE_PRECISION_FLOATING;
break;
case OT_SS:
// Scalar element of a 128-bit packed single-precision
// floating data.
// We simply return enItUnknown since we don't have to support
// floating point
succeeded = false;
break;
case OT_V: // Word or doubleword, depending on operand-size attribute.
if (operand_is_32_bits_)
operand_bytes_ += OS_DOUBLE_WORD;
else
operand_bytes_ += OS_WORD;
break;
case OT_W: // Word, regardless of operand-size attribute.
operand_bytes_ += OS_WORD;
break;
// Can safely ignore these.
case OT_A: // Two one-word operands in memory or two double-word
// operands in memory
case OT_PI: // Quadword MMX technology register (e.g. mm0)
case OT_SI: // Doubleword integer register (e.g., eax)
break;
default:
break;
}
break;
default:
break;
}
return succeeded;
}
bool MiniDisassembler::ProcessModrm(unsigned char* start_byte,
unsigned int& size) {
// If we don't need to decode, we just return the size of the ModR/M
// byte (there is never a SIB byte in this case).
if (!should_decode_modrm_) {
size++;
return true;
}
// We never care about the reg field, only the combination of the mod
// and r/m fields, so let's start by packing those fields together into
// 5 bits.
unsigned char modrm = (*start_byte);
unsigned char mod = modrm & 0xC0; // mask out top two bits to get mod field
modrm = modrm & 0x07; // mask out bottom 3 bits to get r/m field
mod = mod >> 3; // shift the mod field to the right place
modrm = mod | modrm; // combine the r/m and mod fields as discussed
mod = mod >> 3; // shift the mod field to bits 2..0
// Invariant: modrm contains the mod field in bits 4..3 and the r/m field
// in bits 2..0, and mod contains the mod field in bits 2..0
const ModrmEntry* modrm_entry = 0;
if (address_is_32_bits_)
modrm_entry = &s_ia32_modrm_map_[modrm];
else
modrm_entry = &s_ia16_modrm_map_[modrm];
// Invariant: modrm_entry points to information that we need to decode
// the ModR/M byte.
// Add to the count of operand bytes, if the ModR/M byte indicates
// that some operands are encoded in the instruction.
if (modrm_entry->is_encoded_in_instruction_)
operand_bytes_ += modrm_entry->operand_size_;
// Process the SIB byte if necessary, and return the count
// of ModR/M and SIB bytes.
if (modrm_entry->use_sib_byte_) {
size++;
return ProcessSib(start_byte + 1, mod, size);
} else {
size++;
return true;
}
}
bool MiniDisassembler::ProcessSib(unsigned char* start_byte,
unsigned char mod,
unsigned int& size) {
// get the mod field from the 2..0 bits of the SIB byte
unsigned char sib_base = (*start_byte) & 0x07;
if (0x05 == sib_base) {
switch (mod) {
case 0x00: // mod == 00
case 0x02: // mod == 10
operand_bytes_ += OS_DOUBLE_WORD;
break;
case 0x01: // mod == 01
operand_bytes_ += OS_BYTE;
break;
case 0x03: // mod == 11
// According to the IA-32 docs, there does not seem to be a disp
// value for this value of mod
default:
break;
}
}
size++;
return true;
}
}; // namespace sidestep
// Copyright (c) 2012 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.
/*
* Definition of MiniDisassembler.
*/
#ifndef GOOGLE_PERFTOOLS_MINI_DISASSEMBLER_H__
#define GOOGLE_PERFTOOLS_MINI_DISASSEMBLER_H__
#include <windows.h>
#include "mini_disassembler_types.h"
// compatibility shim
#include "base/logging.h"
#define ASSERT(cond, msg) DCHECK(cond)
#define ASSERT1(cond) DCHECK(cond)
namespace sidestep {
// This small disassembler is very limited
// in its functionality, and in fact does only the bare minimum required by the
// preamble patching utility. It may be useful for other purposes, however.
//
// The limitations include at least the following:
// -# No support for coprocessor opcodes, MMX, etc.
// -# No machine-readable identification of opcodes or decoding of
// assembly parameters. The name of the opcode (as a string) is given,
// however, to aid debugging.
//
// You may ask what this little disassembler actually does, then? The answer is
// that it does the following, which is exactly what the patching utility needs:
// -# Indicates if opcode is a jump (any kind) or a return (any kind)
// because this is important for the patching utility to determine if
// a function is too short or there are jumps too early in it for it
// to be preamble patched.
// -# The opcode length is always calculated, so that the patching utility
// can figure out where the next instruction starts, and whether it
// already has enough instructions to replace with the absolute jump
// to the patching code.
//
// The usage is quite simple; just create a MiniDisassembler and use its
// Disassemble() method.
//
// If you would like to extend this disassembler, please refer to the
// IA-32 Intel Architecture Software Developer's Manual Volume 2:
// Instruction Set Reference for information about operand decoding
// etc.
class MiniDisassembler {
public:
// Creates a new instance and sets defaults.
//
// @param operand_default_32_bits If true, the default operand size is
// set to 32 bits, which is the default under Win32. Otherwise it is 16 bits.
// @param address_default_32_bits If true, the default address size is
// set to 32 bits, which is the default under Win32. Otherwise it is 16 bits.
MiniDisassembler(bool operand_default_32_bits,
bool address_default_32_bits);
// Equivalent to MiniDisassembler(true, true);
MiniDisassembler();
// Attempts to disassemble a single instruction starting from the
// address in memory it is pointed to.
//
// @param start Address where disassembly should start.
// @param instruction_bytes Variable that will be <b>incremented</b> by
// the length in bytes of the instruction.
// @return enItJump, enItReturn or enItGeneric on success. enItUnknown
// if unable to disassemble, enItUnused if this seems to be an unused
// opcode. In the last two (error) cases, cbInstruction will be set
// to 0xffffffff.
//
// @post This instance of the disassembler is ready to be used again,
// with unchanged defaults from creation time.
InstructionType Disassemble(unsigned char* start, unsigned int& instruction_bytes);
private:
// Makes the disassembler ready for reuse.
void Initialize();
// Sets the flags for address and operand sizes.
// @return Number of prefix bytes.
InstructionType ProcessPrefixes(unsigned char* start, unsigned int& size);
// Sets the flag for whether we have ModR/M, and increments
// operand_bytes_ if any are specifies by the opcode directly.
// @return Number of opcode bytes.
InstructionType ProcessOpcode(unsigned char * start,
unsigned int table,
unsigned int& size);
// Checks the type of the supplied operand. Increments
// operand_bytes_ if it directly indicates an immediate etc.
// operand. Asserts have_modrm_ if the operand specifies
// a ModR/M byte.
bool ProcessOperand(int flag_operand);
// Increments operand_bytes_ by size specified by ModR/M and
// by SIB if present.
// @return 0 in case of error, 1 if there is just a ModR/M byte,
// 2 if there is a ModR/M byte and a SIB byte.
bool ProcessModrm(unsigned char* start, unsigned int& size);
// Processes the SIB byte that it is pointed to.
// @param start Pointer to the SIB byte.
// @param mod The mod field from the ModR/M byte.
// @return 1 to indicate success (indicates 1 SIB byte)
bool ProcessSib(unsigned char* start, unsigned char mod, unsigned int& size);
// The instruction type we have decoded from the opcode.
InstructionType instruction_type_;
// Counts the number of bytes that is occupied by operands in
// the current instruction (note: we don't care about how large
// operands stored in registers etc. are).
unsigned int operand_bytes_;
// True iff there is a ModR/M byte in this instruction.
bool have_modrm_;
// True iff we need to decode the ModR/M byte (sometimes it just
// points to a register, we can tell by the addressing mode).
bool should_decode_modrm_;
// Current operand size is 32 bits if true, 16 bits if false.
bool operand_is_32_bits_;
// Default operand size is 32 bits if true, 16 bits if false.
bool operand_default_is_32_bits_;
// Current address size is 32 bits if true, 16 bits if false.
bool address_is_32_bits_;
// Default address size is 32 bits if true, 16 bits if false.
bool address_default_is_32_bits_;
// Huge big opcode table based on the IA-32 manual, defined
// in Ia32OpcodeMap.cc
static const OpcodeTable s_ia32_opcode_map_[];
// Somewhat smaller table to help with decoding ModR/M bytes
// when 16-bit addressing mode is being used. Defined in
// Ia32ModrmMap.cc
static const ModrmEntry s_ia16_modrm_map_[];
// Somewhat smaller table to help with decoding ModR/M bytes
// when 32-bit addressing mode is being used. Defined in
// Ia32ModrmMap.cc
static const ModrmEntry s_ia32_modrm_map_[];
// Indicators of whether we got certain prefixes that certain
// silly Intel instructions depend on in nonstandard ways for
// their behaviors.
bool got_f2_prefix_, got_f3_prefix_, got_66_prefix_;
};
}; // namespace sidestep
#endif // GOOGLE_PERFTOOLS_MINI_DISASSEMBLER_H__
// Copyright (c) 2012 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.
/*
* Several simple types used by the disassembler and some of the patching
* mechanisms.
*/
#ifndef GOOGLE_PERFTOOLS_MINI_DISASSEMBLER_TYPES_H__
#define GOOGLE_PERFTOOLS_MINI_DISASSEMBLER_TYPES_H__
namespace sidestep {
// Categories of instructions that we care about
enum InstructionType {
// This opcode is not used
IT_UNUSED,
// This disassembler does not recognize this opcode (error)
IT_UNKNOWN,
// This is not an instruction but a reference to another table
IT_REFERENCE,
// This byte is a prefix byte that we can ignore
IT_PREFIX,
// This is a prefix byte that switches to the nondefault address size
IT_PREFIX_ADDRESS,
// This is a prefix byte that switches to the nondefault operand size
IT_PREFIX_OPERAND,
// A jump or call instruction
IT_JUMP,
// A return instruction
IT_RETURN,
// Any other type of instruction (in this case we don't care what it is)
IT_GENERIC,
};
// Lists IA-32 operand sizes in multiples of 8 bits
enum OperandSize {
OS_ZERO = 0,
OS_BYTE = 1,
OS_WORD = 2,
OS_DOUBLE_WORD = 4,
OS_QUAD_WORD = 8,
OS_DOUBLE_QUAD_WORD = 16,
OS_32_BIT_POINTER = 32/8,
OS_48_BIT_POINTER = 48/8,
OS_SINGLE_PRECISION_FLOATING = 32/8,
OS_DOUBLE_PRECISION_FLOATING = 64/8,
OS_DOUBLE_EXTENDED_PRECISION_FLOATING = 80/8,
OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING = 128/8,
OS_PSEUDO_DESCRIPTOR = 6
};
// Operand addressing methods from the IA-32 manual. The enAmMask value
// is a mask for the rest. The other enumeration values are named for the
// names given to the addressing methods in the manual, e.g. enAm_D is for
// the D addressing method.
//
// The reason we use a full 4 bytes and a mask, is that we need to combine
// these flags with the enOperandType to store the details
// on the operand in a single integer.
enum AddressingMethod {
AM_NOT_USED = 0, // This operand is not used for this instruction
AM_MASK = 0x00FF0000, // Mask for the rest of the values in this enumeration
AM_A = 0x00010000, // A addressing type
AM_C = 0x00020000, // C addressing type
AM_D = 0x00030000, // D addressing type
AM_E = 0x00040000, // E addressing type
AM_F = 0x00050000, // F addressing type
AM_G = 0x00060000, // G addressing type
AM_I = 0x00070000, // I addressing type
AM_J = 0x00080000, // J addressing type
AM_M = 0x00090000, // M addressing type
AM_O = 0x000A0000, // O addressing type
AM_P = 0x000B0000, // P addressing type
AM_Q = 0x000C0000, // Q addressing type
AM_R = 0x000D0000, // R addressing type
AM_S = 0x000E0000, // S addressing type
AM_T = 0x000F0000, // T addressing type
AM_V = 0x00100000, // V addressing type
AM_W = 0x00110000, // W addressing type
AM_X = 0x00120000, // X addressing type
AM_Y = 0x00130000, // Y addressing type
AM_REGISTER = 0x00140000, // Specific register is always used as this op
AM_IMPLICIT = 0x00150000, // An implicit, fixed value is used
};
// Operand types from the IA-32 manual. The enOtMask value is
// a mask for the rest. The rest of the values are named for the
// names given to these operand types in the manual, e.g. enOt_ps
// is for the ps operand type in the manual.
//
// The reason we use a full 4 bytes and a mask, is that we need
// to combine these flags with the enAddressingMethod to store the details
// on the operand in a single integer.
enum OperandType {
OT_MASK = 0xFF000000,
OT_A = 0x01000000,
OT_B = 0x02000000,
OT_C = 0x03000000,
OT_D = 0x04000000,
OT_DQ = 0x05000000,
OT_P = 0x06000000,
OT_PI = 0x07000000,
OT_PS = 0x08000000, // actually unsupported for (we don't know its size)
OT_Q = 0x09000000,
OT_S = 0x0A000000,
OT_SS = 0x0B000000,
OT_SI = 0x0C000000,
OT_V = 0x0D000000,
OT_W = 0x0E000000,
OT_SD = 0x0F000000, // scalar double-precision floating-point value
OT_PD = 0x10000000, // double-precision floating point
// dummy "operand type" for address mode M - which doesn't specify
// operand type
OT_ADDRESS_MODE_M = 0x80000000
};
// Everything that's in an Opcode (see below) except the three
// alternative opcode structs for different prefixes.
struct SpecificOpcode {
// Index to continuation table, or 0 if this is the last
// byte in the opcode.
int table_index_;
// The opcode type
InstructionType type_;
// Description of the type of the dest, src and aux operands,
// put together from an enOperandType flag and an enAddressingMethod
// flag.
int flag_dest_;
int flag_source_;
int flag_aux_;
// We indicate the mnemonic for debugging purposes
const char* mnemonic_;
};
// The information we keep in our tables about each of the different
// valid instructions recognized by the IA-32 architecture.
struct Opcode {
// Index to continuation table, or 0 if this is the last
// byte in the opcode.
int table_index_;
// The opcode type
InstructionType type_;
// Description of the type of the dest, src and aux operands,
// put together from an enOperandType flag and an enAddressingMethod
// flag.
int flag_dest_;
int flag_source_;
int flag_aux_;
// We indicate the mnemonic for debugging purposes
const char* mnemonic_;
// Alternative opcode info if certain prefixes are specified.
// In most cases, all of these are zeroed-out. Only used if
// bPrefixDependent is true.
bool is_prefix_dependent_;
SpecificOpcode opcode_if_f2_prefix_;
SpecificOpcode opcode_if_f3_prefix_;
SpecificOpcode opcode_if_66_prefix_;
};
// Information about each table entry.
struct OpcodeTable {
// Table of instruction entries
const Opcode* table_;
// How many bytes left to shift ModR/M byte <b>before</b> applying mask
unsigned char shift_;
// Mask to apply to byte being looked at before comparing to table
unsigned char mask_;
// Minimum/maximum indexes in table.
unsigned char min_lim_;
unsigned char max_lim_;
};
// Information about each entry in table used to decode ModR/M byte.
struct ModrmEntry {
// Is the operand encoded as bytes in the instruction (rather than
// if it's e.g. a register in which case it's just encoded in the
// ModR/M byte)
bool is_encoded_in_instruction_;
// Is there a SIB byte? In this case we always need to decode it.
bool use_sib_byte_;
// What is the size of the operand (only important if it's encoded
// in the instruction)?
OperandSize operand_size_;
};
}; // namespace sidestep
#endif // GOOGLE_PERFTOOLS_MINI_DISASSEMBLER_TYPES_H__
// Copyright (c) 2010 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 "preamble_patcher.h"
#include "memory_hook.h"
#include "mini_disassembler.h"
// compatibility shims
#include "base/logging.h"
// Definitions of assembly statements we need
#define ASM_JMP32REL 0xE9
#define ASM_INT3 0xCC
namespace sidestep {
SideStepError PreamblePatcher::RawPatchWithStubAndProtections(
void* target_function, void *replacement_function,
unsigned char* preamble_stub, unsigned long stub_size,
unsigned long* bytes_needed) {
// We need to be able to write to a process-local copy of the first
// MAX_PREAMBLE_STUB_SIZE bytes of target_function. We may be giving execute
// privilege to something that doesn't have it, but that's the price to pay
// for tools.
DWORD old_target_function_protect = 0;
BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function),
MAX_PREAMBLE_STUB_SIZE,
PAGE_EXECUTE_READWRITE,
&old_target_function_protect);
if (!succeeded) {
ASSERT(false, "Failed to make page containing target function "
"copy-on-write.");
return SIDESTEP_ACCESS_DENIED;
}
SideStepError error_code = RawPatchWithStub(target_function,
replacement_function,
preamble_stub,
stub_size,
bytes_needed);
if (SIDESTEP_SUCCESS != error_code) {
ASSERT1(false);
return error_code;
}
// Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of
// pTargetFunction to what they were before we started goofing around.
succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function),
MAX_PREAMBLE_STUB_SIZE,
old_target_function_protect,
&old_target_function_protect);
if (!succeeded) {
ASSERT(false, "Failed to restore protection to target function.");
// We must not return an error here because the function has actually
// been patched, and returning an error would likely cause our client
// code not to unpatch it. So we just keep going.
}
// Flush the instruction cache to make sure the processor doesn't execute the
// old version of the instructions (before our patch).
//
// FlushInstructionCache is actually a no-op at least on single-processor
// XP machines. I'm not sure why this is so, but it is, yet I want to keep
// the call to the API here for correctness in case there is a difference in
// some variants of Windows/hardware.
succeeded = ::FlushInstructionCache(::GetCurrentProcess(),
target_function,
MAX_PREAMBLE_STUB_SIZE);
if (!succeeded) {
ASSERT(false, "Failed to flush instruction cache.");
// We must not return an error here because the function has actually
// been patched, and returning an error would likely cause our client
// code not to unpatch it. So we just keep going.
}
return SIDESTEP_SUCCESS;
}
SideStepError PreamblePatcher::RawPatch(void* target_function,
void* replacement_function,
void** original_function_stub) {
if (!target_function || !replacement_function || !original_function_stub ||
(*original_function_stub) || target_function == replacement_function) {
ASSERT(false, "Preconditions not met");
return SIDESTEP_INVALID_PARAMETER;
}
// @see MAX_PREAMBLE_STUB_SIZE for an explanation of how we arrives at
// this size
unsigned char* preamble_stub =
reinterpret_cast<unsigned char*>(
MemoryHook::Alloc(sizeof(unsigned char) * MAX_PREAMBLE_STUB_SIZE));
if (!preamble_stub) {
ASSERT(false, "Unable to allocate preamble-stub.");
return SIDESTEP_INSUFFICIENT_BUFFER;
}
// Change the protection of the newly allocated preamble stub to
// PAGE_EXECUTE_READWRITE. This is required to work with DEP (Data
// Execution Prevention) which will cause an exception if code is executed
// from a page on which you do not have read access.
DWORD old_stub_protect = 0;
BOOL succeeded = VirtualProtect(preamble_stub, MAX_PREAMBLE_STUB_SIZE,
PAGE_EXECUTE_READWRITE, &old_stub_protect);
if (!succeeded) {
ASSERT(false, "Failed to make page preamble stub read-write-execute.");
delete[] preamble_stub;
return SIDESTEP_ACCESS_DENIED;
}
SideStepError error_code = RawPatchWithStubAndProtections(target_function,
replacement_function,
preamble_stub,
MAX_PREAMBLE_STUB_SIZE,
NULL);
if (SIDESTEP_SUCCESS != error_code) {
ASSERT1(false);
delete[] preamble_stub;
return error_code;
}
*original_function_stub = reinterpret_cast<void*>(preamble_stub);
// NOTE: For hooking malloc/free, we don't want to use streams which
// allocate. Basically, we've hooked malloc, but not necessarily
// hooked free yet. To do anything which uses the heap could crash
// with a mismatched malloc/free!
//VLOG(1) << "PreamblePatcher::RawPatch successfully patched 0x"
// << target_function;
return SIDESTEP_SUCCESS;
}
SideStepError PreamblePatcher::Unpatch(void* target_function,
void* replacement_function,
void* original_function_stub) {
ASSERT1(target_function && original_function_stub);
if (!target_function || !original_function_stub) {
return SIDESTEP_INVALID_PARAMETER;
}
// We disassemble the preamble of the _stub_ to see how many bytes we
// originally copied to the stub.
MiniDisassembler disassembler;
unsigned int preamble_bytes = 0;
while (preamble_bytes < 5) {
InstructionType instruction_type = disassembler.Disassemble(
reinterpret_cast<unsigned char*>(original_function_stub) +
preamble_bytes, preamble_bytes);
if (IT_GENERIC != instruction_type) {
ASSERT(false, "Should only have generic instructions in stub!!");
return SIDESTEP_UNSUPPORTED_INSTRUCTION;
}
}
// Before unpatching, target_function should be a JMP to
// replacement_function. If it's not, then either it's an error, or
// we're falling into the case where the original instruction was a
// JMP, and we patched the jumped_to address rather than the JMP
// itself. (For instance, if malloc() is just a JMP to __malloc(),
// we patched __malloc() and not malloc().)
unsigned char* target = reinterpret_cast<unsigned char*>(target_function);
while (1) { // we stop when target is a JMP to replacement_function
if (target[0] != ASM_JMP32REL) {
ASSERT(false, "target_function does not look like it was patched.");
return SIDESTEP_INVALID_PARAMETER;
}
int relative_offset; // Windows guarantees int is 4 bytes
ASSERT1(sizeof(relative_offset) == 4);
memcpy(reinterpret_cast<void*>(&relative_offset),
reinterpret_cast<void*>(target + 1), 4);
unsigned char* jump_to = target + 5 + relative_offset;
if (jump_to == replacement_function)
break;
target = jump_to; // follow the jmp
}
// We need to be able to write to a process-local copy of the first
// MAX_PREAMBLE_STUB_SIZE bytes of target_function. We may be giving execute
// privilege to something that doesn't have it, but that's the price to pay
// for tools.
DWORD old_target_function_protect = 0;
BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target),
MAX_PREAMBLE_STUB_SIZE,
PAGE_EXECUTE_READWRITE,
&old_target_function_protect);
if (!succeeded) {
ASSERT(false, "Failed to make page containing target function "
"copy-on-write.");
return SIDESTEP_ACCESS_DENIED;
}
// Replace the first few bytes of the original function with the bytes we
// previously moved to the preamble stub.
memcpy(reinterpret_cast<void*>(target),
original_function_stub, preamble_bytes);
// Stub is now useless so delete it.
// [csilvers: Commented out for perftools because it causes big problems
// when we're unpatching malloc. We just let this live on as a leak.]
//delete original_function_stub;
// Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of
// target to what they were before we started goofing around.
succeeded = ::VirtualProtect(reinterpret_cast<void*>(target),
MAX_PREAMBLE_STUB_SIZE,
old_target_function_protect,
&old_target_function_protect);
// Flush the instruction cache to make sure the processor doesn't execute the
// old version of the instructions (before our patch).
//
// See comment on FlushInstructionCache elsewhere in this file.
succeeded = ::FlushInstructionCache(::GetCurrentProcess(),
target,
MAX_PREAMBLE_STUB_SIZE);
if (!succeeded) {
ASSERT(false, "Failed to flush instruction cache.");
return SIDESTEP_UNEXPECTED;
}
VLOG(1) << "PreamblePatcher::Unpatch successfully unpatched 0x"
<< target_function;
return SIDESTEP_SUCCESS;
}
}; // namespace sidestep
// Copyright (c) 2012 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.
/*
* Definition of PreamblePatcher
*/
#ifndef MEMORY_WATCHER_PREAMBLE_PATCHER_H__
#define MEMORY_WATCHER_PREAMBLE_PATCHER_H__
#include <windows.h>
// compatibility shim
#include "base/logging.h"
#define ASSERT(cond, msg) DCHECK(cond)
#define ASSERT1(cond) DCHECK(cond)
// Maximum size of the preamble stub. We overwrite at least the first 5
// bytes of the function. Considering the worst case scenario, we need 4
// bytes + the max instruction size + 5 more bytes for our jump back to
// the original code. With that in mind, 32 is a good number :)
#define MAX_PREAMBLE_STUB_SIZE (32)
namespace sidestep {
// Possible results of patching/unpatching
enum SideStepError {
SIDESTEP_SUCCESS = 0,
SIDESTEP_INVALID_PARAMETER,
SIDESTEP_INSUFFICIENT_BUFFER,
SIDESTEP_JUMP_INSTRUCTION,
SIDESTEP_FUNCTION_TOO_SMALL,
SIDESTEP_UNSUPPORTED_INSTRUCTION,
SIDESTEP_NO_SUCH_MODULE,
SIDESTEP_NO_SUCH_FUNCTION,
SIDESTEP_ACCESS_DENIED,
SIDESTEP_UNEXPECTED,
};
#define SIDESTEP_TO_HRESULT(error) \
MAKE_HRESULT(SEVERITY_ERROR, FACILITY_NULL, error)
// Implements a patching mechanism that overwrites the first few bytes of
// a function preamble with a jump to our hook function, which is then
// able to call the original function via a specially-made preamble-stub
// that imitates the action of the original preamble.
//
// NOTE: This patching mechanism should currently only be used for
// non-production code, e.g. unit tests, because it is not threadsafe.
// See the TODO in preamble_patcher_with_stub.cc for instructions on what
// we need to do before using it in production code; it's fairly simple
// but unnecessary for now since we only intend to use it in unit tests.
//
// To patch a function, use either of the typesafe Patch() methods. You
// can unpatch a function using Unpatch().
//
// Typical usage goes something like this:
// @code
// typedef int (*MyTypesafeFuncPtr)(int x);
// MyTypesafeFuncPtr original_func_stub;
// int MyTypesafeFunc(int x) { return x + 1; }
// int HookMyTypesafeFunc(int x) { return 1 + original_func_stub(x); }
//
// void MyPatchInitializingFunction() {
// original_func_stub = PreamblePatcher::Patch(
// MyTypesafeFunc, HookMyTypesafeFunc);
// if (!original_func_stub) {
// // ... error handling ...
// }
//
// // ... continue - you have patched the function successfully ...
// }
// @endcode
//
// Note that there are a number of ways that this method of patching can
// fail. The most common are:
// - If there is a jump (jxx) instruction in the first 5 bytes of
// the function being patched, we cannot patch it because in the
// current implementation we do not know how to rewrite relative
// jumps after relocating them to the preamble-stub. Note that
// if you really really need to patch a function like this, it
// would be possible to add this functionality (but at some cost).
// - If there is a return (ret) instruction in the first 5 bytes
// we cannot patch the function because it may not be long enough
// for the jmp instruction we use to inject our patch.
// - If there is another thread currently executing within the bytes
// that are copied to the preamble stub, it will crash in an undefined
// way.
//
// If you get any other error than the above, you're either pointing the
// patcher at an invalid instruction (e.g. into the middle of a multi-
// byte instruction, or not at memory containing executable instructions)
// or, there may be a bug in the disassembler we use to find
// instruction boundaries.
//
// NOTE: In optimized builds, when you have very trivial functions that
// the compiler can reason do not have side effects, the compiler may
// reuse the result of calling the function with a given parameter, which
// may mean if you patch the function in between your patch will never get
// invoked. See preamble_patcher_test.cc for an example.
class PreamblePatcher {
public:
// This is a typesafe version of RawPatch(), identical in all other
// ways than it takes a template parameter indicating the type of the
// function being patched.
//
// @param T The type of the function you are patching. Usually
// you will establish this type using a typedef, as in the following
// example:
// @code
// typedef BOOL (WINAPI *MessageBoxPtr)(HWND, LPCTSTR, LPCTSTR, UINT);
// MessageBoxPtr original = NULL;
// PreamblePatcher::Patch(MessageBox, Hook_MessageBox, &original);
// @endcode
template <class T>
static SideStepError Patch(T target_function,
T replacement_function,
T* original_function_stub) {
// NOTE: casting from a function to a pointer is contra the C++
// spec. It's not safe on IA64, but is on i386. We use
// a C-style cast here to emphasize this is not legal C++.
return RawPatch((void*)(target_function),
(void*)(replacement_function),
(void**)(original_function_stub));
}
// Patches a named function imported from the named module using
// preamble patching. Uses RawPatch() to do the actual patching
// work.
//
// @param T The type of the function you are patching. Must
// exactly match the function you specify using module_name and
// function_name.
//
// @param module_name The name of the module from which the function
// is being imported. Note that the patch will fail if this module
// has not already been loaded into the current process.
//
// @param function_name The name of the function you wish to patch.
//
// @param replacement_function Your replacement function which
// will be called whenever code tries to call the original function.
//
// @param original_function_stub Pointer to memory that should receive a
// pointer that can be used (e.g. in the replacement function) to call the
// original function, or NULL to indicate failure.
//
// @return One of the EnSideStepError error codes; only SIDESTEP_SUCCESS
// indicates success.
template <class T>
static SideStepError Patch(LPCTSTR module_name,
LPCSTR function_name,
T replacement_function,
T* original_function_stub) {
ASSERT1(module_name && function_name);
if (!module_name || !function_name) {
ASSERT(false,
"You must specify a module name and function name.");
return SIDESTEP_INVALID_PARAMETER;
}
HMODULE module = ::GetModuleHandle(module_name);
ASSERT1(module != NULL);
if (!module) {
ASSERT(false, "Invalid module name.");
return SIDESTEP_NO_SUCH_MODULE;
}
FARPROC existing_function = ::GetProcAddress(module, function_name);
if (!existing_function) {
return SIDESTEP_NO_SUCH_FUNCTION;
}
// NOTE: casting from a function to a pointer is contra the C++
// spec. It's not safe on IA64, but is on i386. We use
// a C-style cast here to emphasize this is not legal C++.
return RawPatch((void*)existing_function, (void*)replacement_function,
(void**)(original_function_stub));
}
// Patches a function by overwriting its first few bytes with
// a jump to a different function. This is the "worker" function
// for each of the typesafe Patch() functions. In most cases,
// it is preferable to use the Patch() functions rather than
// this one as they do more checking at compile time.
//
// @param target_function A pointer to the function that should be
// patched.
//
// @param replacement_function A pointer to the function that should
// replace the target function. The replacement function must have
// exactly the same calling convention and parameters as the original
// function.
//
// @param original_function_stub Pointer to memory that should receive a
// pointer that can be used (e.g. in the replacement function) to call the
// original function, or NULL to indicate failure.
//
// @param original_function_stub Pointer to memory that should receive a
// pointer that can be used (e.g. in the replacement function) to call the
// original function, or NULL to indicate failure.
//
// @return One of the EnSideStepError error codes; only SIDESTEP_SUCCESS
// indicates success.
//
// @note The preamble-stub (the memory pointed to by
// *original_function_stub) is allocated on the heap, and (in
// production binaries) never destroyed, resulting in a memory leak. This
// will be the case until we implement safe unpatching of a method.
// However, it is quite difficult to unpatch a method (because other
// threads in the process may be using it) so we are leaving it for now.
// See however UnsafeUnpatch, which can be used for binaries where you
// know only one thread is running, e.g. unit tests.
static SideStepError RawPatch(void* target_function,
void* replacement_function,
void** original_function_stub);
// Unpatches target_function and deletes the stub that previously could be
// used to call the original version of the function.
//
// DELETES the stub that is passed to the function.
//
// @param target_function Pointer to the target function which was
// previously patched, i.e. a pointer which value should match the value
// of the symbol prior to patching it.
//
// @param replacement_function Pointer to the function target_function
// was patched to.
//
// @param original_function_stub Pointer to the stub returned when
// patching, that could be used to call the original version of the
// patched function. This function will also delete the stub, which after
// unpatching is useless.
//
// If your original call was
// origptr = Patch(VirtualAlloc, MyVirtualAlloc)
// then to undo it you would call
// Unpatch(VirtualAlloc, MyVirtualAlloc, origptr);
//
// @return One of the EnSideStepError error codes; only SIDESTEP_SUCCESS
// indicates success.
static SideStepError Unpatch(void* target_function,
void* replacement_function,
void* original_function_stub);
private:
// Patches a function by overwriting its first few bytes with
// a jump to a different function. This is similar to the RawPatch
// function except that it uses the stub allocated by the caller
// instead of allocating it.
//
// We call VirtualProtect to make the
// target function writable at least for the duration of the call.
//
// @param target_function A pointer to the function that should be
// patched.
//
// @param replacement_function A pointer to the function that should
// replace the target function. The replacement function must have
// exactly the same calling convention and parameters as the original
// function.
//
// @param preamble_stub A pointer to a buffer where the preamble stub
// should be copied. The size of the buffer should be sufficient to
// hold the preamble bytes.
//
// @param stub_size Size in bytes of the buffer allocated for the
// preamble_stub
//
// @param bytes_needed Pointer to a variable that receives the minimum
// number of bytes required for the stub. Can be set to NULL if you're
// not interested.
//
// @return An error code indicating the result of patching.
static SideStepError RawPatchWithStubAndProtections(void* target_function,
void *replacement_function,
unsigned char* preamble_stub,
unsigned long stub_size,
unsigned long* bytes_needed);
// A helper function used by RawPatchWithStubAndProtections -- it does
// everything but the VirtualProtect wsork. Defined in
// preamble_patcher_with_stub.cc.
static SideStepError RawPatchWithStub(void* target_function,
void *replacement_function,
unsigned char* preamble_stub,
unsigned long stub_size,
unsigned long* bytes_needed);
};
}; // namespace sidestep
#endif // MEMORY_WATCHER_PREAMBLE_PATCHER_H__
// Copyright (c) 2012 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.
/*
* Implementation of PreamblePatcher
*/
#include "preamble_patcher.h"
#include "mini_disassembler.h"
// Definitions of assembly statements we need
#define ASM_JMP32REL 0xE9
#define ASM_INT3 0xCC
namespace sidestep {
SideStepError PreamblePatcher::RawPatchWithStub(
void* target_function,
void *replacement_function,
unsigned char* preamble_stub,
unsigned long stub_size,
unsigned long* bytes_needed) {
if ((NULL == target_function) ||
(NULL == replacement_function) ||
(NULL == preamble_stub)) {
ASSERT(false, "Invalid parameters - either pTargetFunction or "
"pReplacementFunction or pPreambleStub were NULL.");
return SIDESTEP_INVALID_PARAMETER;
}
// TODO(V7:joi) Siggi and I just had a discussion and decided that both
// patching and unpatching are actually unsafe. We also discussed a
// method of making it safe, which is to freeze all other threads in the
// process, check their thread context to see if their eip is currently
// inside the block of instructions we need to copy to the stub, and if so
// wait a bit and try again, then unfreeze all threads once we've patched.
// Not implementing this for now since we're only using SideStep for unit
// testing, but if we ever use it for production code this is what we
// should do.
//
// NOTE: Stoyan suggests we can write 8 or even 10 bytes atomically using
// FPU instructions, and on newer processors we could use cmpxchg8b or
// cmpxchg16b. So it might be possible to do the patching/unpatching
// atomically and avoid having to freeze other threads. Note though, that
// doing it atomically does not help if one of the other threads happens
// to have its eip in the middle of the bytes you change while you change
// them.
unsigned char* target = reinterpret_cast<unsigned char*>(target_function);
// First, deal with a special case that we see with functions that
// point into an IAT table (including functions linked statically
// into the application): these function already starts with
// ASM_JMP32REL. For instance, malloc() might be implemented as a
// JMP to __malloc(). In that case, we replace the destination of
// the JMP (__malloc), rather than the JMP itself (malloc). This
// way we get the correct behavior no matter how malloc gets called.
if (target[0] == ASM_JMP32REL) {
// target[1-4] holds the place the jmp goes to, but it's
// relative to the next instruction.
int relative_offset; // Windows guarantees int is 4 bytes
ASSERT1(sizeof(relative_offset) == 4);
memcpy(reinterpret_cast<void*>(&relative_offset),
reinterpret_cast<void*>(target + 1), 4);
// I'd like to just say "target = target + 5 + relative_offset" here, but
// I can't, because the new target will need to have its protections set.
return RawPatchWithStubAndProtections(target + 5 + relative_offset,
replacement_function, preamble_stub,
stub_size, bytes_needed);
}
// Let's disassemble the preamble of the target function to see if we can
// patch, and to see how much of the preamble we need to take. We need 5
// bytes for our jmp instruction, so let's find the minimum number of
// instructions to get 5 bytes.
MiniDisassembler disassembler;
unsigned int preamble_bytes = 0;
while (preamble_bytes < 5) {
InstructionType instruction_type =
disassembler.Disassemble(target + preamble_bytes, preamble_bytes);
if (IT_JUMP == instruction_type) {
ASSERT(false, "Unable to patch because there is a jump instruction "
"in the first 5 bytes.");
return SIDESTEP_JUMP_INSTRUCTION;
} else if (IT_RETURN == instruction_type) {
ASSERT(false, "Unable to patch because function is too short");
return SIDESTEP_FUNCTION_TOO_SMALL;
} else if (IT_GENERIC != instruction_type) {
ASSERT(false, "Disassembler encountered unsupported instruction "
"(either unused or unknown)");
return SIDESTEP_UNSUPPORTED_INSTRUCTION;
}
}
if (NULL != bytes_needed)
*bytes_needed = preamble_bytes + 5;
// Inv: cbPreamble is the number of bytes (at least 5) that we need to take
// from the preamble to have whole instructions that are 5 bytes or more
// in size total. The size of the stub required is cbPreamble + size of
// jmp (5)
if (preamble_bytes + 5 > stub_size) {
ASSERT1(false);
return SIDESTEP_INSUFFICIENT_BUFFER;
}
// First, copy the preamble that we will overwrite.
memcpy(reinterpret_cast<void*>(preamble_stub),
reinterpret_cast<void*>(target), preamble_bytes);
// Now, make a jmp instruction to the rest of the target function (minus the
// preamble bytes we moved into the stub) and copy it into our preamble-stub.
// find address to jump to, relative to next address after jmp instruction
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4244)
#endif
int relative_offset_to_target_rest
= ((reinterpret_cast<unsigned char*>(target) + preamble_bytes) -
(preamble_stub + preamble_bytes + 5));
#ifdef _MSC_VER
#pragma warning(pop)
#endif
// jmp (Jump near, relative, displacement relative to next instruction)
preamble_stub[preamble_bytes] = ASM_JMP32REL;
// copy the address
memcpy(reinterpret_cast<void*>(preamble_stub + preamble_bytes + 1),
reinterpret_cast<void*>(&relative_offset_to_target_rest), 4);
// Inv: preamble_stub points to assembly code that will execute the
// original function by first executing the first cbPreamble bytes of the
// preamble, then jumping to the rest of the function.
// Overwrite the first 5 bytes of the target function with a jump to our
// replacement function.
// (Jump near, relative, displacement relative to next instruction)
target[0] = ASM_JMP32REL;
// Find offset from instruction after jmp, to the replacement function.
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4244)
#endif
int offset_to_replacement_function =
reinterpret_cast<unsigned char*>(replacement_function) -
reinterpret_cast<unsigned char*>(target) - 5;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
// complete the jmp instruction
memcpy(reinterpret_cast<void*>(target + 1),
reinterpret_cast<void*>(&offset_to_replacement_function), 4);
// Set any remaining bytes that were moved to the preamble-stub to INT3 so
// as not to cause confusion (otherwise you might see some strange
// instructions if you look at the disassembly, or even invalid
// instructions). Also, by doing this, we will break into the debugger if
// some code calls into this portion of the code. If this happens, it
// means that this function cannot be patched using this patcher without
// further thought.
if (preamble_bytes > 5) {
memset(reinterpret_cast<void*>(target + 5), ASM_INT3, preamble_bytes - 5);
}
// Inv: The memory pointed to by target_function now points to a relative
// jump instruction that jumps over to the preamble_stub. The preamble
// stub contains the first stub_size bytes of the original target
// function's preamble code, followed by a relative jump back to the next
// instruction after the first cbPreamble bytes.
return SIDESTEP_SUCCESS;
}
}; // namespace sidestep
#!/usr/bin/perl
# Copyright (c) 2012 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.
sub process_raw($$) {
my $file = shift;
my $search = shift;
my %leaks = ();
my $save = 0;
my $print = 0;
my $bytes = 0;
my $calls = 0;
my $sum_bytes = 0;
my $sum_calls = 0;
open (LOGFILE, "$file") or die("could not open $file");
while(<LOGFILE>) {
my $line = $_;
if ($line =~ m/([0-9]*) bytes, ([0-9]*) allocs/) {
$save = "";
$print = 0;
$bytes = $1;
$calls = $2;
}
elsif ($line =~ m/$search/) {
$print = 1;
}
elsif ($line =~ m/=============/) {
$save .= $line;
if ($print) {
print "$bytes bytes ($calls calls)\n";
print $save;
$sum_bytes += $bytes;
$sum_calls += $calls;
$save = "";
$print = 0;
$calls = 0;
}
}
$save .= $line;
}
print("TOTAL: $sum_bytes bytes ($sum_calls calls)\n");
}
# ----- Main ------------------------------------------------
# Get the command line argument
my $filename = shift;
my $search = shift;
# Process the file.
process_raw($filename, $search);
#!/usr/bin/perl
# Copyright (c) 2012 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.
#
# Given a memwatcher logfile, group memory allocations by callstack.
#
# Usage:
#
# memprof.pl <logfile>
#
# logfile -- The memwatcher.logXXXX file to summarize.
#
#
#
# Sample output:
#
# 54,061,617 100.00% AllocationStack::AllocationStack
# 41,975,368 77.64% malloc
# 11,886,592 21.99% VirtualAlloc
# 7,168,000 13.26% v8::internal::OS::Allocate
# 7,168,000 13.26% v8::internal::MemoryAllocator::AllocateRawMemory
# 5,976,184 11.05% WebCore::V8Bridge::evaluate
# 5,767,168 10.67% v8::internal::MemoryAllocator::AllocatePages
# 5,451,776 10.08% WebCore::V8Proxy::initContextIfNeeded
# ....
#
#
#
# ********
# Note: The output is currently sorted by decreasing size.
# ********
#
sub process_raw($$) {
my $file = shift;
my $filter = shift;
my %leaks = ();
my %stackframes = ();
my $blamed = 0;
my $bytes = 0;
my $hits = 0;
open (LOGFILE, "$file") or die("could not open $file");
while(<LOGFILE>) {
my $line = $_;
#print "$line";
chomp($line);
if ($line =~ m/([0-9]*) bytes, ([0-9]*) allocs/) {
# If we didn't find any frames to account this to, log that.
if ($blamed == 0) {
$leaks{"UNACCOUNTED"} += $bytes;
}
#print "START\n";
#print("stackframe " . $1 . ", " . $2 . "\n");
$hits = $2;
$bytes = $1;
%stackframes = (); # we have a new frame, clear the list.
$blamed = 0; # we haven't blamed anyone yet
}
elsif ($line =~ m/Total Bytes:[ ]*([0-9]*)/) {
$total_bytes += $1;
}
elsif ($line =~ m/=============/) {
next;
}
elsif ($line =~ m/[ ]*([\-a-zA-Z_\\0-9\.]*) \(([0-9]*)\):[ ]*([<>_a-zA-Z_0-9:]*)/) {
# print("junk: " . $line . "\n");
# print("file: $1\n");
# print("line: $2\n");
# print("function: $3\n");
#
# blame the function
my $pig = $3;
# my $pig = $1;
# only add the memory if this function is not yet on our callstack
if (!exists $stackframes{$pig}) {
$leaks{$pig} += $bytes;
}
$stackframes{$pig}++;
$blamed++;
}
}
# now dump our hash table
my $sum = 0;
my @keys = sort { $leaks{$b} <=> $leaks{$a} }keys %leaks;
for ($i=0; $i<@keys; $i++) {
my $key = @keys[$i];
printf "%11s\t%3.2f%%\t%s\n", comma_print($leaks{$key}), (100* $leaks{$key} / $total_bytes), $key;
$sum += $leaks{$key};
}
printf("TOTAL: %s\n", comma_print($sum));
}
# Insert commas into an integer after each three digits for printing.
sub comma_print {
my $num = "$_[0]";
$num =~ s/(\d{1,3}?)(?=(\d{3})+$)/$1,/g;
return $num;
}
# ----- Main ------------------------------------------------
# Get the command line argument
my $filename = shift;
my $filter = shift;
# Process the file.
process_raw($filename, $filter);
#!/usr/bin/perl
# Copyright (c) 2012 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.
#
# Blame callstacks for each memory allocation.
# Similar to memprof.pl, will also try to filter out unuseful stacks.
# TODO: better describe how these tools differ.
#
# Usage:
#
# memtrace.pl <logfile>
#
# logfile -- The memwatcher.logXXXX file to summarize.
#
#
#
# Sample output:
#
# 41,975,368 77.64% f:\sp\vctools\crt_bld\self_x86\crt\src\malloc.c (163): malloc
# 2,097,152 3.88% c:\src\chrome1\src\webkit\pending\frameloader.cpp (3300): WebCore::FrameLoader::committedLoad
# 1,572,864 2.91% c:\src\chrome1\src\webkit\port\bridge\v8bridge.cpp (214): WebCore::V8Bridge::evaluate
# 1,572,864 2.91% c:\src\chrome1\src\webkit\glue\webframeloaderclient_impl.cc (1071): WebFrameLoaderClient::committedLoad
# 1,572,864 2.91% c:\src\chrome1\src\v8\src\ast.h (1181): v8::internal::Visitor::Visit
#
#
#
sub process_raw($) {
my $file = shift;
my %leaks = ();
my $location_bytes = 0;
my $location_hits = 0;
my $location_blame = "";
my $location_last = "";
my $contains_load_lib = 0;
my $total_bytes = 0;
open (LOGFILE, "$file") or die("could not open $file");
while(<LOGFILE>) {
my $line = $_;
#print "$line";
chomp($line);
if ($line =~ m/([0-9]*) bytes, ([0-9]*) allocs/) {
#print "START\n";
# Dump "prior" frame here
if ($location_bytes > 0) {
#print("GOTLEAK: $location_bytes ($location_hits) $location_blame\n");
if ($location_blame eq "") {
$location_blame = $location_last;
}
if (!$contains_load_lib) {
$leaks{$location_blame} += $location_bytes;
}
$location_bytes = 0;
$location_blame = "";
$contains_load_lib = 0;
}
#print("stackframe " . $1 . ", " . $2 . "\n");
$location_hits = $2;
$location_bytes = $1;
}
elsif ($line =~ m/Total Bytes:[ ]*([0-9]*)/) {
$total_bytes += $1;
}
elsif ($line =~ m/LoadLibrary/) {
# skip these, they contain false positives.
$contains_load_lib = 1;
next;
}
elsif ($line =~ m/=============/) {
next;
}
elsif ($line =~ m/Untracking untracked/) {
next;
}
elsif ($line =~ m/[ ]*([a-z]:\\[a-z]*\\[a-zA-Z_\\0-9\.]*) /) {
my $filename = $1;
if ($filename =~ m/memory_watcher/) {
next;
}
if ($filename =~ m/skmemory_stdlib.cpp/) {
next;
}
if ($filename =~ m/stringimpl.cpp/) {
next;
}
if ($filename =~ m/stringbuffer.h/) {
next;
}
if ($filename =~ m/fastmalloc.h/) {
next;
}
if ($filename =~ m/microsoft visual studio 8/) {
next;
}
if ($filename =~ m/platformsdk_win2008_6_1/) {
next;
}
if ($location_blame eq "") {
# use this to blame the line
$location_blame = $line;
# use this to blame the file.
# $location_blame = $filename;
#print("blaming $location_blame\n");
}
} else {
# print("junk: " . $line . "\n");
if (! ($line =~ m/GetModuleFileNameA/) ) {
$location_last = $line;
}
}
}
# now dump our hash table
my $sum = 0;
my @keys = sort { $leaks{$b} <=> $leaks{$a} }keys %leaks;
for ($i=0; $i<@keys; $i++) {
my $key = @keys[$i];
if (0 == $total_bytes) { $total_bytes = 1; }
printf "%11s\t%3.2f%%\t%s\n", comma_print($leaks{$key}), (100* $leaks{$key} / $total_bytes), $key;
$sum += $leaks{$key};
}
printf("TOTAL: %s\n", comma_print($sum));
}
# Insert commas into an integer after each three digits for printing.
sub comma_print {
my $num = "$_[0]";
$num =~ s/(\d{1,3}?)(?=(\d{3})+$)/$1,/g;
return $num;
}
# ----- Main ------------------------------------------------
# Get the command line argument
my $filename = shift;
# Process the file.
process_raw($filename);
#!/usr/bin/perl
# Copyright (c) 2012 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.
#
# Read a memtrace logfile from stdin and group memory allocations by logical
# code component. The code component is guessed from the callstack, and
# is something like {v8, sqlite, disk cache, skia, etc..}
#
# Usage:
#
# summary.pl
#
# [STDIN] -- The memwatcher.logXXXX file to summarize.
#
sub process_stdin() {
my %leaks = ();
my $total_bytes = 0;
while(<STDIN>) {
my $line = $_;
chomp($line);
my $bytes, $loc;
($bytes, $loc) = ($line =~ m/[ \t]*([0-9]*)[ \t]*[0-9\.%]*[ \t]*(.*)/);
chomp($loc);
while(<STDIN>) {
my $cont = $_;
chomp($cont);
last if $cont =~ m/=====/;
$loc .= "\n" . $cont;
}
my $location_blame = "";
# print "Found: $bytes, $loc\n";
if ($loc =~ m/v8::internal::Snapshot::Deserialize/) {
$location_blame = "v8 Snapshot Deserialize";
} elsif ($loc =~ m/RenderStyle::create/) {
$location_blame = "RenderStyle::create";
} elsif ($loc =~ m/v8::internal::OldSpace::SlowAllocateRaw/) {
$location_blame = "v8 OldSpace";
} elsif ($loc =~ m/sqlite/) {
$location_blame = "sqlite";
} elsif ($loc =~ m/ TransportDIB::Map/) {
$location_blame = "Shared Memory Backing Store";
} elsif ($loc =~ m/imagedecoder/) {
$location_blame = "img decoder";
} elsif ($loc =~ m/SkBitmap/) {
$location_blame = "skia";
} elsif ($loc =~ m/disk_cache/) {
$location_blame = "disk cache";
} elsif ($loc =~ m/skia/) {
$location_blame = "skia";
} elsif ($loc =~ m/:WSA/) {
$location_blame = "net";
} elsif ($loc =~ m/dns/) {
$location_blame = "net";
} elsif ($loc =~ m/trunk\\net/) {
$location_blame = "net";
} elsif ($loc =~ m/WinHttp/) {
$location_blame = "WinHttp";
} elsif ($loc =~ m/:I_Crypt/) {
$location_blame = "WinHttpSSL";
} elsif ($loc =~ m/CryptGetTls/) {
$location_blame = "WinHttpSSL";
} elsif ($loc =~ m/WinVerifyTrust/) {
$location_blame = "WinHttpSSL";
} elsif ($loc =~ m/Cert/) {
$location_blame = "WinHttpSSL";
} elsif ($loc =~ m/plugin/) {
$location_blame = "plugin";
} elsif ($loc =~ m/NP_/) {
$location_blame = "plugin";
} elsif ($loc =~ m/hunspell/) {
$location_blame = "hunspell";
} elsif ($loc =~ m/TextCodec/) {
$location_blame = "fonts";
} elsif ($loc =~ m/glyph/) {
$location_blame = "fonts";
} elsif ($loc =~ m/cssparser/) {
$location_blame = "webkit css";
} elsif ($loc =~ m/::CSS/) {
$location_blame = "webkit css";
} elsif ($loc =~ m/Arena/) {
$location_blame = "webkit arenas";
} elsif ($loc =~ m/WebCore::.*ResourceLoader::addData/) {
$location_blame = "WebCore *ResourceLoader addData";
} elsif ($loc =~ m/OnUpdateVisitedLinks/) {
$location_blame = "OnUpdateVisitedLinks";
} elsif ($loc =~ m/IPC/) {
$location_blame = "ipc";
} elsif ($loc =~ m/trunk\\chrome\\browser/) {
$location_blame = "browser";
} elsif ($loc =~ m/trunk\\chrome\\renderer/) {
$location_blame = "renderer";
} elsif ($loc =~ m/webcore\\html/) {
$location_blame = "webkit webcore html";
} elsif ($loc =~ m/webkit.*string/) {
$location_blame = "webkit strings";
} elsif ($loc =~ m/htmltokenizer/) {
$location_blame = "webkit HTMLTokenizer";
} elsif ($loc =~ m/javascriptcore/) {
$location_blame = "webkit javascriptcore";
} elsif ($loc =~ m/webkit/) {
$location_blame = "webkit other";
} elsif ($loc =~ m/safe_browsing/) {
$location_blame = "safe_browsing";
} elsif ($loc =~ m/VisitedLinkMaster/) {
$location_blame = "VisitedLinkMaster";
} elsif ($loc =~ m/NewDOMUI/) {
$location_blame = "NewDOMUI";
} elsif ($loc =~ m/RegistryControlledDomainService/) {
$location_blame = "RegistryControlledDomainService";
} elsif ($loc =~ m/URLRequestChromeJob::DataAvailable/) {
$location_blame = "URLRequestChromeJob DataAvailable";
} else {
$location_blame = "unknown";
}
# Surface large outliers in an "interesting" group.
my $interesting_group = "unknown";
my $interesting_size = 10000000; # Make this smaller as needed.
# TODO(jar): Add this as a pair of shell arguments.
if ($bytes > $interesting_size && $location_blame eq $interesting_group) {
# Create a special group for the exact stack that contributed so much.
$location_blame = $loc;
}
$total_bytes += $bytes;
$leaks{$location_blame} += $bytes;
}
# now dump our hash table
my $sum = 0;
my @keys = sort { $leaks{$b} <=> $leaks{$a} }keys %leaks;
for ($i=0; $i<@keys; $i++) {
my $key = @keys[$i];
printf "%11s\t(%3.2f%%)\t%s\n", comma_print($leaks{$key}), (100* $leaks{$key} / $total_bytes), $key;
$sum += $leaks{$key};
}
printf("TOTAL: %s\n", comma_print($sum));
}
# Insert commas into an integer after each three digits for printing.
sub comma_print {
my $num = "$_[0]";
$num =~ s/(\d{1,3}?)(?=(\d{3})+$)/$1,/g;
return $num;
}
# ----- Main ------------------------------------------------
process_stdin();
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment