mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-26 02:02:33 +00:00
440 lines
10 KiB
C++
440 lines
10 KiB
C++
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "prtypes.h"
|
|
#include "prlock.h"
|
|
#include "nscore.h"
|
|
#include "nsAutoLock.h"
|
|
|
|
#include "nsStackFrameWin.h"
|
|
#include "nsDebugHelpWin32.h"
|
|
#include "nsTraceMallocCallbacks.h"
|
|
|
|
// XXX These are *very* quick hacks and need improvement!
|
|
|
|
static PRBool GetSymbolFromAddress(uint32 addr, char* outBuf)
|
|
{
|
|
PRBool ok;
|
|
ok = EnsureSymInitialized();
|
|
if(!ok)
|
|
return ok;
|
|
|
|
char buf[sizeof(IMAGEHLP_SYMBOL) + 512];
|
|
PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL) buf;
|
|
symbol->SizeOfStruct = sizeof(buf);
|
|
symbol->MaxNameLength = 512;
|
|
|
|
DWORD displacement;
|
|
|
|
ok = SymGetSymFromAddr(::GetCurrentProcess(),
|
|
addr,
|
|
&displacement,
|
|
symbol);
|
|
|
|
if(ok)
|
|
{
|
|
char buf2[512];
|
|
sprintf(buf2, "%s+0x%08X", symbol->Name, displacement);
|
|
strcat(outBuf, buf2);
|
|
}
|
|
else
|
|
strcat(outBuf, "dunno");
|
|
return ok;
|
|
}
|
|
|
|
static PRBool GetModuleFromAddress(uint32 addr, char* outBuf)
|
|
{
|
|
PRBool ok;
|
|
ok = EnsureSymInitialized();
|
|
if(!ok)
|
|
return ok;
|
|
|
|
|
|
IMAGEHLP_MODULE module;
|
|
module.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
|
|
|
|
ok = SymGetModuleInfo(::GetCurrentProcess(),
|
|
addr,
|
|
&module);
|
|
|
|
if(ok)
|
|
strcat(outBuf, module.ModuleName);
|
|
else
|
|
strcat(outBuf, "dunno");
|
|
return ok;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
#ifdef VERBOSE
|
|
#define SHOW(x_, buf_) \
|
|
printf(#x_" = %#x... %s\n", x_, \
|
|
(buf_[0] = 0, \
|
|
GetModuleFromAddress((uint32)x_, buf_), \
|
|
strcat(buf," :: "), \
|
|
GetSymbolFromAddress((uint32)x_, buf_), \
|
|
buf_));
|
|
#else
|
|
#define SHOW(x_, buf_)
|
|
#endif //VERBOSE
|
|
|
|
|
|
/***************************************************************************/
|
|
|
|
#ifdef VERBOSE
|
|
// XXX This is a quick hack to show that x86 Win32 stack walking can be done
|
|
// with this sort of loop following the bp.
|
|
|
|
void dumpStack()
|
|
{
|
|
uint32* bp_;
|
|
uint32* bpdown;
|
|
uint32 pc;
|
|
|
|
char buf[512];
|
|
|
|
_asm { mov bp_ , ebp }
|
|
|
|
/* Stack walking code adapted from Kipp's "leaky". */
|
|
while (1) {
|
|
bpdown = (uint32*) *bp_++;
|
|
pc = *bp_;
|
|
// These addresses are iffy...
|
|
if (pc < 0x00400000 || pc > 0x7fffffff || bpdown < bp_)
|
|
break;
|
|
SHOW(pc, buf);
|
|
bp_ = bpdown;
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
|
|
char* _stdcall call2(void* v)
|
|
{
|
|
// dumpStack();
|
|
// return 0;
|
|
|
|
return (char *)malloc(123);
|
|
}
|
|
|
|
int call1(char c, int i, double d, ... )
|
|
{
|
|
free(call2(0));
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************/
|
|
// shows how to use the dhw stuff to hook imported functions
|
|
|
|
|
|
static BOOL g_lockOut = FALSE; //stop reentrancy
|
|
|
|
DHW_DECLARE_FUN_TYPE_AND_PROTO(dhw_malloc, void*, __cdecl, MALLOC_, (size_t));
|
|
|
|
DHWImportHooker &getMallocHooker()
|
|
{
|
|
static DHWImportHooker gMallocHooker("MSVCRTD.dll", "malloc", (PROC) dhw_malloc);
|
|
return gMallocHooker;
|
|
}
|
|
|
|
void * __cdecl dhw_malloc( size_t size )
|
|
{
|
|
PRUint32 start = PR_IntervalNow();
|
|
void* result = DHW_ORIGINAL(MALLOC_, getMallocHooker())(size);
|
|
PRUint32 end = PR_IntervalNow();
|
|
if (g_lockOut)
|
|
return result;
|
|
g_lockOut = TRUE;
|
|
#ifdef VERBOSE
|
|
printf("* malloc called to get %d bytes. returned %#x\n", size, result);
|
|
#endif
|
|
MallocCallback(result, size, start, end);
|
|
// dumpStack();
|
|
// printf("\n");
|
|
g_lockOut = FALSE;
|
|
return result;
|
|
}
|
|
|
|
DHW_DECLARE_FUN_TYPE_AND_PROTO(dhw_calloc, void*, __cdecl, CALLOC_, (size_t,size_t));
|
|
|
|
DHWImportHooker &getCallocHooker()
|
|
{
|
|
static DHWImportHooker gCallocHooker("MSVCRTD.dll", "calloc", (PROC) dhw_calloc);
|
|
return gCallocHooker;
|
|
}
|
|
|
|
void * __cdecl dhw_calloc( size_t count, size_t size )
|
|
{
|
|
PRUint32 start = PR_IntervalNow();
|
|
void* result = DHW_ORIGINAL(CALLOC_, getCallocHooker())(count,size);
|
|
PRUint32 end = PR_IntervalNow();
|
|
if (g_lockOut)
|
|
return result;
|
|
g_lockOut = TRUE;
|
|
#ifdef VERBOSE
|
|
printf("* calloc called to get %d many of %d bytes. returned %#x\n", count, size, result);
|
|
#endif
|
|
CallocCallback(result, count, size, start, end);
|
|
// dumpStack();
|
|
// printf("\n");
|
|
g_lockOut = FALSE;
|
|
return result;
|
|
}
|
|
|
|
DHW_DECLARE_FUN_TYPE_AND_PROTO(dhw_free, void, __cdecl, FREE_, (void*));
|
|
DHWImportHooker &getFreeHooker()
|
|
{
|
|
static DHWImportHooker gFreeHooker("MSVCRTD.dll", "free", (PROC) dhw_free);
|
|
return gFreeHooker;
|
|
}
|
|
|
|
void __cdecl dhw_free( void* p )
|
|
{
|
|
PRUint32 start = PR_IntervalNow();
|
|
DHW_ORIGINAL(FREE_, getFreeHooker())(p);
|
|
PRUint32 end = PR_IntervalNow();
|
|
if (g_lockOut)
|
|
return;
|
|
g_lockOut = TRUE;
|
|
#ifdef VERBOSE
|
|
printf("* free called for %#x\n", p);
|
|
#endif
|
|
FreeCallback(p, start, end);
|
|
// dumpStack();
|
|
// printf("\n");
|
|
g_lockOut = FALSE;
|
|
}
|
|
|
|
|
|
DHW_DECLARE_FUN_TYPE_AND_PROTO(dhw_realloc, void*, __cdecl, REALLOC_, (void*, size_t));
|
|
DHWImportHooker &getReallocHooker()
|
|
{
|
|
static DHWImportHooker gReallocHooker("MSVCRTD.dll", "realloc", (PROC) dhw_realloc);
|
|
return gReallocHooker;
|
|
}
|
|
|
|
void * __cdecl dhw_realloc(void * pin, size_t size)
|
|
{
|
|
PRUint32 start = PR_IntervalNow();
|
|
void* pout = DHW_ORIGINAL(REALLOC_, getReallocHooker())(pin, size);
|
|
PRUint32 end = PR_IntervalNow();
|
|
if (g_lockOut)
|
|
return pout;
|
|
g_lockOut = TRUE;
|
|
|
|
#ifdef VERBOSE
|
|
printf("* realloc called to resize to %d. old ptr: %#x. new ptr: %#x\n",
|
|
size, pin, pout);
|
|
#endif
|
|
ReallocCallback(pin, pout, size, start, end);
|
|
// dumpStack();
|
|
// printf("\n");
|
|
g_lockOut = FALSE;
|
|
return pout;
|
|
}
|
|
|
|
// Note the mangled name!
|
|
DHW_DECLARE_FUN_TYPE_AND_PROTO(dhw_new, void*, __cdecl, NEW_, (size_t));
|
|
DHWImportHooker &getNewHooker()
|
|
{
|
|
static DHWImportHooker gNewHooker("MSVCRTD.dll", "??2@YAPAXI@Z", (PROC) dhw_new);
|
|
return gNewHooker;
|
|
}
|
|
|
|
void * __cdecl dhw_new(size_t size)
|
|
{
|
|
PRUint32 start = PR_IntervalNow();
|
|
void* result = DHW_ORIGINAL(NEW_, getNewHooker())(size);
|
|
PRUint32 end = PR_IntervalNow();
|
|
if (g_lockOut)
|
|
return result;
|
|
g_lockOut = TRUE;
|
|
|
|
#ifdef VERBOSE
|
|
printf("* new called to get %d bytes. returned %#x\n", size, result);
|
|
dumpStack();
|
|
#endif
|
|
MallocCallback(result, size, start, end);//do we need a different one for new?
|
|
// printf("\n");
|
|
g_lockOut = FALSE;
|
|
return result;
|
|
}
|
|
|
|
// Note the mangled name!
|
|
DHW_DECLARE_FUN_TYPE_AND_PROTO(dhw_delete, void, __cdecl, DELETE_, (void*));
|
|
DHWImportHooker &getDeleteHooker()
|
|
{
|
|
static DHWImportHooker gDeleteHooker("MSVCRTD.dll", "??3@YAXPAX@Z", (PROC) dhw_delete);
|
|
return gDeleteHooker;
|
|
}
|
|
|
|
void __cdecl dhw_delete(void* p)
|
|
{
|
|
PRUint32 start = PR_IntervalNow();
|
|
DHW_ORIGINAL(DELETE_, getDeleteHooker())(p);
|
|
PRUint32 end = PR_IntervalNow();
|
|
if (g_lockOut)
|
|
return;
|
|
g_lockOut = TRUE;
|
|
#ifdef VERBOSE
|
|
printf("* delete called for %#x\n", p);
|
|
dumpStack();
|
|
#endif
|
|
FreeCallback(p, start, end);
|
|
// printf("\n");
|
|
g_lockOut = FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
/***************************************************************************/
|
|
// A demonstration of using the _CrtSetAllocHook based hooking.
|
|
// This system sucks because you don't get to see the allocated pointer.
|
|
#if 0
|
|
class myAllocationSizePrinter : public DHWAllocationSizeDebugHook
|
|
{
|
|
public:
|
|
PRBool AllocHook(size_t size)
|
|
{
|
|
alloc_calls++ ;
|
|
total_mem += size;
|
|
if(verbosity)
|
|
{
|
|
printf("alloc called to get %d bytes.\n", size);
|
|
dumpStack();
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool ReallocHook(size_t size, size_t sizeOld)
|
|
{
|
|
realloc_calls++ ;
|
|
total_mem += size;
|
|
total_mem -= sizeOld;
|
|
if(verbosity)
|
|
{
|
|
printf("realloc called to size to %d bytes. Old size: %d.\n",
|
|
size, sizeOld);
|
|
dumpStack();
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool FreeHook(size_t size)
|
|
{
|
|
free_calls++ ;
|
|
total_mem -= size;
|
|
if(verbosity)
|
|
{
|
|
printf("free called to release %d bytes.\n", size);
|
|
dumpStack();
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
myAllocationSizePrinter(int v)
|
|
: verbosity(v),
|
|
alloc_calls(0),
|
|
realloc_calls(0),
|
|
free_calls(0),
|
|
total_mem(0) {}
|
|
virtual ~myAllocationSizePrinter(){}
|
|
|
|
void report()
|
|
{
|
|
printf("%d allocs, %d reallocs, %d frees, %d bytes leaked\n",
|
|
alloc_calls, realloc_calls, free_calls, total_mem);
|
|
}
|
|
|
|
private:
|
|
void dumpStack()
|
|
{
|
|
if(verbosity == 2)
|
|
::dumpStack();
|
|
}
|
|
|
|
int verbosity;
|
|
int alloc_calls;
|
|
int realloc_calls;
|
|
int free_calls;
|
|
size_t total_mem;
|
|
};
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*C Callbacks*/
|
|
PR_IMPLEMENT(void)
|
|
StartupHooker()
|
|
{
|
|
if (!EnsureSymInitialized())
|
|
return;
|
|
|
|
//run through get all hookers
|
|
DHWImportHooker &loadlibraryW = DHWImportHooker::getLoadLibraryWHooker();
|
|
DHWImportHooker &loadlibraryExW = DHWImportHooker::getLoadLibraryExWHooker();
|
|
DHWImportHooker &loadlibraryA = DHWImportHooker::getLoadLibraryAHooker();
|
|
DHWImportHooker &loadlibraryExA = DHWImportHooker::getLoadLibraryExAHooker();
|
|
DHWImportHooker &mallochooker = getMallocHooker();
|
|
DHWImportHooker &reallochooker = getReallocHooker();
|
|
DHWImportHooker &callochooker = getCallocHooker();
|
|
DHWImportHooker &freehooker = getFreeHooker();
|
|
DHWImportHooker &newhooker = getNewHooker();
|
|
DHWImportHooker &deletehooker = getDeleteHooker();
|
|
printf("Startup Hooker\n");
|
|
}
|
|
|
|
PR_IMPLEMENT(void)
|
|
ShutdownHooker()
|
|
{
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
int main()
|
|
{
|
|
// A demonstration of using the (sucky) _CrtSetAllocHook based hooking.
|
|
myAllocationSizePrinter ap(0);
|
|
dhwSetAllocationSizeDebugHook(&ap);
|
|
|
|
// show that the ImportHooker is hooking calls from loaded dll
|
|
DHW_DECLARE_FUN_TYPE(void, __stdcall, SOMECALL_, (void));
|
|
HMODULE module = ::LoadLibrary("Other.dll");
|
|
if(module) {
|
|
SOMECALL_ _SomeCall = (SOMECALL_) GetProcAddress(module, "SomeCall");
|
|
if(_SomeCall)
|
|
_SomeCall();
|
|
}
|
|
|
|
// show that the ImportHooker is hooking sneaky calls made from this dll.
|
|
HMODULE module2 = ::LoadLibrary("MSVCRTD.dll");
|
|
if(module2) {
|
|
MALLOC_ _sneakyMalloc = (MALLOC_) GetProcAddress(module2, "malloc");
|
|
if(_sneakyMalloc)
|
|
{
|
|
void* p = _sneakyMalloc(987);
|
|
free(p);
|
|
}
|
|
}
|
|
|
|
call1('x', 1, 1.0, "hi there", 2);
|
|
|
|
char* p = new char[10];
|
|
delete p;
|
|
|
|
void* v = malloc(10);
|
|
v = realloc(v, 15);
|
|
v = realloc(v, 5);
|
|
free(v);`
|
|
|
|
ap.report();
|
|
dhwClearAllocationSizeDebugHook();
|
|
return 0;
|
|
}
|
|
#endif //0
|
|
|