/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is Mozilla Communicator client code. * * The Initial Developer of the Original Code is Netscape Communications * Corporation. Portions created by Netscape are Copyright (C) 1998 * Netscape Communications Corporation. All Rights Reserved. */ #include "nsISupports.h" #include "prprf.h" #include "prlog.h" #if defined(_WIN32) #include #endif static PRLogModuleInfo* gTraceRefcntLog; #if defined(NS_MT_SUPPORTED) #include "prlock.h" static PRLock* gTraceLock; #define LOCK_TRACELOG() PR_Lock(gTraceLock) #define UNLOCK_TRACELOG() PR_Unlock(gTraceLock) #else /* ! NT_MT_SUPPORTED */ #define LOCK_TRACELOG() #define UNLOCK_TRACELOG() #endif /* ! NS_MT_SUPPORTED */ static void InitTraceLog(void) { if (0 == gTraceRefcntLog) { gTraceRefcntLog = PR_NewLogModule("xpcomrefcnt"); #if defined(NS_MT_SUPPORTED) gTraceLock = PR_NewLock(); #endif /* NS_MT_SUPPORTED */ } } #if defined(_WIN32) #include "imagehlp.h" #include #if 0 static BOOL __stdcall EnumSymbolsCB(LPSTR aSymbolName, ULONG aSymbolAddress, ULONG aSymbolSize, PVOID aUserContext) { int* countp = (int*) aUserContext; int count = *countp; if (count < 3) { printf(" %p[%4d]: %s\n", aSymbolAddress, aSymbolSize, aSymbolName); count++; *countp = count++; } return TRUE; } static BOOL __stdcall EnumModulesCB(LPSTR aModuleName, ULONG aBaseOfDll, PVOID aUserContext) { HANDLE myProcess = ::GetCurrentProcess(); printf("module=%s dll=%x\n", aModuleName, aBaseOfDll); // int count = 0; // SymEnumerateSymbols(myProcess, aBaseOfDll, EnumSymbolsCB, (void*) &count); return TRUE; } #endif /** * Walk the stack, translating PC's found into strings and recording the * chain in aBuffer. For this to work properly, the dll's must be rebased * so that the address in the file agrees with the address in memory. * Otherwise StackWalk will return FALSE when it hits a frame in a dll's * whose in memory address doesn't match it's in-file address. * * Fortunately, there is a handy dandy routine in IMAGEHLP.DLL that does * the rebasing and accordingly I've made a tool to use it to rebase the * DLL's in one fell swoop (see xpcom/tools/windows/rebasedlls.cpp). */ void nsTraceRefcnt::WalkTheStack(char* aBuffer, int aBufLen) { CONTEXT context; STACKFRAME frame; char* cp = aBuffer; aBuffer[0] = '\0'; aBufLen--; // leave room for nul HANDLE myProcess = ::GetCurrentProcess(); HANDLE myThread = ::GetCurrentThread(); // Get the context information for this thread. That way we will // know where our sp, fp, pc, etc. are and can fill in the // STACKFRAME with the initial values. context.ContextFlags = CONTEXT_FULL; GetThreadContext(myThread, &context); if (!SymInitialize(myProcess, ".;..\\lib", TRUE)) { return; } #if 0 SymEnumerateModules(myProcess, EnumModulesCB, NULL); #endif // Setup initial stack frame to walk from memset(&frame, 0, sizeof(frame)); frame.AddrPC.Mode = AddrModeFlat; frame.AddrPC.Offset = context.Eip; frame.AddrReturn.Mode = AddrModeFlat; frame.AddrReturn.Offset = context.Ebp; frame.AddrFrame.Mode = AddrModeFlat; frame.AddrFrame.Offset = context.Ebp; frame.AddrStack.Mode = AddrModeFlat; frame.AddrStack.Offset = context.Esp; frame.Params[0] = context.Eax; frame.Params[1] = context.Ecx; frame.Params[2] = context.Edx; frame.Params[3] = context.Ebx; // Now walk the stack and map the pc's to symbol names that we stuff // append to *cp. int skip = 2; int syms = 0; while (aBufLen > 0) { char symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 512]; PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL) symbolBuffer; pSymbol->SizeOfStruct = sizeof(symbolBuffer); pSymbol->MaxNameLength = 512; DWORD oldAddress = frame.AddrPC.Offset; BOOL b = ::StackWalk(IMAGE_FILE_MACHINE_I386, myProcess, myThread, &frame, &context, NULL, SymFunctionTableAccess, SymGetModuleBase, NULL); if (!b || (0 == frame.AddrPC.Offset)) { if (syms <= 1) { skip = 7; } break; } if (--skip >= 0) { continue; } DWORD disp; if (SymGetSymFromAddr(myProcess, frame.AddrPC.Offset, &disp, pSymbol)) { int nameLen = strlen(pSymbol->Name); if (nameLen + 2 > aBufLen) { break; } memcpy(cp, pSymbol->Name, nameLen); cp += nameLen; *cp++ = ' '; aBufLen -= nameLen + 1; syms++; } else { if (11 > aBufLen) { break; } char tmp[30]; PR_snprintf(tmp, sizeof(tmp), "0x%08x ", frame.AddrPC.Offset); memcpy(cp, tmp, 11); cp += 11; aBufLen -= 11; syms++; } } *cp = 0; } #else /* _WIN32 */ void nsTraceRefcnt::WalkTheStack(char* aBuffer, int aBufLen) { aBuffer[0] = '\0'; } #endif /* _WIN32 */ NS_COM void nsTraceRefcnt::LoadLibrarySymbols(const char* aLibraryName, void* aLibrayHandle) { #ifdef MOZ_TRACE_XPCOM_REFCNT #if defined(_WIN32) InitTraceLog(); if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { HANDLE myProcess = ::GetCurrentProcess(); if (!SymInitialize(myProcess, ".;..\\lib", TRUE)) { return; } BOOL b = ::SymLoadModule(myProcess, NULL, (char*)aLibraryName, (char*)aLibraryName, 0, 0); // DWORD lastError = 0; // if (!b) lastError = ::GetLastError(); // printf("loading symbols for library %s => %s [%d]\n", aLibraryName, // b ? "true" : "false", lastError); } #endif #endif } NS_COM unsigned long nsTraceRefcnt::AddRef(void* aPtr, unsigned long aNewRefcnt, const char* aFile, PRIntn aLine) { #ifdef MOZ_TRACE_XPCOM_REFCNT InitTraceLog(); LOCK_TRACELOG(); if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { char sb[1000]; WalkTheStack(sb, sizeof(sb)); PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, ("AddRef: %p: %d=>%d [%s] in %s (line %d)", aPtr, aNewRefcnt-1, aNewRefcnt, sb, aFile, aLine)); } UNLOCK_TRACELOG(); #endif return aNewRefcnt; } NS_COM unsigned long nsTraceRefcnt::Release(void* aPtr, unsigned long aNewRefcnt, const char* aFile, PRIntn aLine) { #ifdef MOZ_TRACE_XPCOM_REFCNT InitTraceLog(); LOCK_TRACELOG(); if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { char sb[1000]; WalkTheStack(sb, sizeof(sb)); PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, ("Release: %p: %d=>%d [%s] in %s (line %d)", aPtr, aNewRefcnt+1, aNewRefcnt, sb, aFile, aLine)); } UNLOCK_TRACELOG(); #endif return aNewRefcnt; } NS_COM void nsTraceRefcnt::Create(void* aPtr, const char* aType, const char* aFile, PRIntn aLine) { #ifdef MOZ_TRACE_XPCOM_REFCNT InitTraceLog(); LOCK_TRACELOG(); if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { char sb[1000]; WalkTheStack(sb, sizeof(sb)); PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, ("Create: %p[%s]: [%s] in %s (line %d)", aPtr, aType, sb, aFile, aLine)); } UNLOCK_TRACELOG(); #endif } NS_COM void nsTraceRefcnt::Destroy(void* aPtr, const char* aFile, PRIntn aLine) { #ifdef MOZ_TRACE_XPCOM_REFCNT InitTraceLog(); LOCK_TRACELOG(); if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { char sb[1000]; WalkTheStack(sb, sizeof(sb)); PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, ("Destroy: %p: [%s] in %s (line %d)", aPtr, sb, aFile, aLine)); } UNLOCK_TRACELOG(); #endif }