nsTraceRefcntImpl.cpp revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* 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 the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* L. David Baron <dbaron@dbaron.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsTraceRefcntImpl.h"
#include "nscore.h"
#include "nsISupports.h"
#include "nsVoidArray.h"
#include "prprf.h"
#include "prlog.h"
#include "plstr.h"
#include <stdlib.h>
#include "nsCOMPtr.h"
#include "nsCRT.h"
#include <math.h>
#if defined(_WIN32)
#include <windows.h>
#include <setjmp.h>
//
// On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
// if __USE_GNU is defined. I suppose its some kind of standards
// adherence thing.
//
#define __USE_GNU
#endif
#include <dlfcn.h>
#endif
#ifdef HAVE_LIBDL
#include <dlfcn.h>
#endif
#if defined(XP_MAC) && !TARGET_CARBON
#include "macstdlibextras.h"
#endif
////////////////////////////////////////////////////////////////////////////////
NS_COM void
double *meanResult, double *stdDevResult)
{
if (n > 0.0 && sumOfValues >= 0) {
mean = sumOfValues / n;
var = 0.0;
else
// for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
}
*meanResult = mean;
*stdDevResult = stdDev;
}
////////////////////////////////////////////////////////////////////////////////
#ifdef NS_BUILD_REFCNT_LOGGING
#include "plhash.h"
#include "prmem.h"
#include "prlock.h"
static PRLock* gTraceLock;
static PLHashTable* gBloatView;
static PLHashTable* gTypesToLog;
static PLHashTable* gObjectsToLog;
static PLHashTable* gSerialNumbers;
static PRInt32 gNextSerialNumber;
static PRBool gLogToLeaky;
static PRBool gLogLeaksOnly;
struct serialNumberRecord {
};
struct nsTraceRefcntStats {
double mRefsOutstandingTotal;
double mRefsOutstandingSquared;
double mObjsOutstandingTotal;
double mObjsOutstandingSquared;
};
#ifdef DEBUG_dbaron_off
// I hope to turn this on for everybody once we hit it a little less.
#define ASSERT_ACTIVITY_IS_LEGAL \
#else
#define ASSERT_ACTIVITY_IS_LEGAL
#endif
// to the functions not called Default* to free the serialNumberRecord or
// the BloatEntry.
static void * PR_CALLBACK
{
#if defined(XP_MAC)
#endif
}
static void PR_CALLBACK
{
#if defined(XP_MAC)
#endif
}
static PLHashEntry * PR_CALLBACK
{
#if defined(XP_MAC)
#endif
return PR_NEW(PLHashEntry);
}
static void PR_CALLBACK
{
#if defined(XP_MAC)
#endif
if (flag == HT_FREE_ENTRY) {
}
}
static void PR_CALLBACK
{
#if defined(XP_MAC)
#endif
if (flag == HT_FREE_ENTRY) {
}
}
static const PLHashAllocOps serialNumberHashAllocOps = {
};
static const PLHashAllocOps typesToLogHashAllocOps = {
};
////////////////////////////////////////////////////////////////////////////////
class BloatEntry {
public:
: mClassSize(classSize) {
mTotalLeaked = 0;
}
~BloatEntry() {
}
const char* GetClassName() { return mClassName; }
stats->mRefsOutstandingTotal = 0;
stats->mRefsOutstandingSquared = 0;
stats->mObjsOutstandingTotal = 0;
stats->mObjsOutstandingSquared = 0;
}
void Accumulate() {
}
if (refcnt == 1) {
Ctor();
}
AccountRefs();
}
if (refcnt == 0) {
Dtor();
}
AccountRefs();
}
void Ctor() {
AccountObjs();
}
void Dtor() {
AccountObjs();
}
void AccountRefs() {
}
void AccountObjs() {
}
if (entry) {
entry->Accumulate();
}
return HT_ENUMERATE_NEXT;
}
}
return HT_ENUMERATE_NEXT;
}
total->mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal + mAllStats.mRefsOutstandingTotal;
total->mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared + mAllStats.mRefsOutstandingSquared;
total->mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal + mAllStats.mObjsOutstandingTotal;
total->mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared + mAllStats.mObjsOutstandingSquared;
}
}
}
" |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n");
" Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
return NS_OK;
}
return NS_OK;
}
double meanRefs, stddevRefs;
&meanRefs, &stddevRefs);
double meanObjs, stddevObjs;
&meanObjs, &stddevObjs);
meanRefs != 0 ||
stddevRefs != 0 ||
meanObjs != 0 ||
stddevObjs != 0) {
i+1, mClassName,
}
return NS_OK;
}
protected:
char* mClassName;
double mClassSize; // this is stored as a double because of the way we compute the avg class size for total bloat
};
static void PR_CALLBACK
{
#if defined(XP_MAC)
#endif
if (flag == HT_FREE_ENTRY) {
delete entry;
}
}
const static PLHashAllocOps bloatViewHashAllocOps = {
};
static void
{
}
static BloatEntry*
{
if (!gBloatView) {
}
if (gBloatView) {
if (e == NULL) {
delete entry;
}
} else {
NS_ASSERTION(aInstanceSize == 0 ||
"bad size recorded");
}
}
return entry;
}
{
#else
#endif
return HT_ENUMERATE_NEXT;
}
#endif /* NS_BUILD_REFCNT_LOGGING */
{
#ifdef NS_BUILD_REFCNT_LOGGING
return NS_ERROR_FAILURE;
}
}
const char* msg;
if (gLogLeaksOnly)
msg = "NEW (incremental) LEAK STATISTICS";
else
msg = "NEW (incremental) LEAK AND BLOAT STATISTICS";
}
else {
if (gLogLeaksOnly)
msg = "ALL (cumulative) LEAK STATISTICS";
else
msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
}
{
// Sort the entries alphabetically by classname.
PRInt32 i, j;
for (j = i - 1; j >= 0; --j) {
}
}
}
// Enumerate from back-to-front, so things come out in alpha order
}
}
if (gSerialNumbers) {
}
done:
#endif
return rv;
}
void
{
#ifdef NS_BUILD_REFCNT_LOGGING
if (gBloatView) {
gBloatView = nsnull;
}
#endif
}
#ifdef NS_BUILD_REFCNT_LOGGING
{
}
{
#ifdef GC_LEAK_DETECTOR
// need to disguise this pointer, so the table won't keep the object alive.
#endif
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
}
else if (aCreate) {
record->COMPtrCount = 0;
PL_HashTableRawAdd(gSerialNumbers, hep, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr, NS_REINTERPRET_CAST(void*,record));
return gNextSerialNumber;
}
else {
return 0;
}
}
{
#ifdef GC_LEAK_DETECTOR
// need to disguise this pointer, so the table won't keep the object alive.
#endif
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
} else {
return nsnull;
}
}
{
#ifdef GC_LEAK_DETECTOR
// need to disguise this pointer, so the table won't keep the object alive.
#endif
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
} else {
return nsnull;
}
}
static void RecycleSerialNumberPtr(void* aPtr)
{
#ifdef GC_LEAK_DETECTOR
// need to disguise this pointer, so the table won't keep the object alive.
#endif
}
{
}
{
if (value) {
return PR_TRUE;
}
return PR_TRUE;
}
else {
return PR_TRUE;
}
else {
return PR_FALSE;
}
}
}
return PR_FALSE;
}
{
}
static void InitTraceLog(void)
{
if (gInitialized) return;
#if defined(XP_MAC) && !TARGET_CARBON
// this can get called before Toolbox has been initialized.
#endif
PRBool defined;
if (!defined)
if (defined || gLogLeaksOnly) {
if (!gBloatView) {
NS_WARNING("out of memory");
}
}
if (defined) {
void* p = nsnull;
void* q = nsnull;
#ifdef HAVE_LIBDL
p = dlsym(0, "__log_addref");
q = dlsym(0, "__log_release");
#endif
if (p && q) {
leakyLogAddRef = (void (*)(void*,int,int)) p;
leakyLogRelease = (void (*)(void*,int,int)) q;
}
else {
fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
}
}
if (classes) {
} else {
if (getenv("XPCOM_MEM_COMPTR_LOG")) {
}
}
#else
if (comptr_log) {
}
#endif
if (classes) {
// if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
// as a list of class names to track
if (!gTypesToLog) {
NS_WARNING("out of memory");
}
else {
for (;;) {
if (cm) {
*cm = '\0';
}
if (!cm) break;
*cm = ',';
}
}
}
if (objects) {
if (!gObjectsToLog) {
NS_WARNING("out of memory");
}
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
}
else {
for (;;) {
if (cm) {
*cm = '\0';
}
while (*cp) {
if (*cp == '-') {
top = 0;
++cp;
}
top *= 10;
++cp;
}
if (!bottom) {
}
}
if (!cm) break;
*cm = ',';
}
}
}
}
gTraceLock = PR_NewLock();
}
#endif
#include "nsStackFrameWin.h"
void
{
}
// WIN32 x86 stack walking code
// i386 or PPC Linux stackwalking code or Solaris
#elif (defined(linux) && defined(__GLIBC__) && (defined(__i386) || defined(PPC))) || (defined(__sun) && (defined(__sparc) || defined(sparc) || defined(__i386) || defined(i386)))
#include "nsStackFrameUnix.h"
void
{
}
/**
* Stack walking code for the Mac OS.
*/
#include "gc_fragments.h"
#include <typeinfo>
extern "C" {
}
struct traceback_table {
long zero;
long magic;
long reserved;
long codeSize;
short nameLength;
char name[2];
};
{
name[0] = '\0';
// make sure pc is instruction aligned (at least).
long instructionsToLook = 4096;
long* instruction = (long*)pc;
// look for the traceback table.
while (instructionsToLook--) {
break;
}
++instruction;
}
}
return name;
}
struct stack_frame {
void* savedCR;
void* savedLR;
void* reserved0;
void* reserved1;
void* savedTOC;
};
static asm stack_frame* getStackFrame()
{
}
NS_COM void
{
while (true) {
// LR saved at 8(SP) in each frame. subtract 4 to get address of calling instruction.
// convert PC to name, unmangle it, and generate source location, if possible.
} else {
}
// the bottom-most frame is marked as pointing to NULL, or is ODD if a 68K transition frame.
break;
}
}
#else // unsupported platform.
void
{
}
#endif
//----------------------------------------------------------------------
// This thing is exported by libstdc++
// Yes, this is a gcc only hack
#if defined(MOZ_DEMANGLE_SYMBOLS)
#include <cxxabi.h>
#include <stdlib.h> // for free()
#endif // MOZ_DEMANGLE_SYMBOLS
NS_COM void
char * aBuffer,
int aBufLen)
{
aBuffer[0] = '\0';
#if defined(MOZ_DEMANGLE_SYMBOLS)
/* See demangle.h in the gcc source for the voodoo */
if (demangled)
{
}
#endif // MOZ_DEMANGLE_SYMBOLS
}
//----------------------------------------------------------------------
NS_COM void
void* aLibrayHandle)
{
#ifdef NS_BUILD_REFCNT_LOGGING
if (!gInitialized)
InitTraceLog();
if (gAllocLog || gRefcntsLog) {
if (ok) {
const char* baseName = aLibraryName;
// just get the base name of the library if a full path was given:
if (aLibraryName[i] == '\\') {
break;
}
}
NULL,
(char*)baseName,
(char*)baseName,
0,
0);
}
if (!ok) {
NULL,
GetLastError(),
0,
);
}
}
#endif
#endif
}
//----------------------------------------------------------------------
// don't use the logging ones. :-)
{
++mRefCnt;
return mRefCnt;
}
{
--mRefCnt;
if (mRefCnt == 0) {
delete this;
return 0;
}
return mRefCnt;
}
{
/* member initializers and constructor code */
}
const char* aClazz,
{
#ifdef NS_BUILD_REFCNT_LOGGING
if (!gInitialized)
InitTraceLog();
if (gLogging) {
if (gBloatLog) {
if (entry) {
}
}
// Here's the case where neither NS_NEWXPCOM nor MOZ_COUNT_CTOR were used,
// yet we still want to see creation information:
if (gSerialNumbers && loggingThisType) {
if(count)
(*count)++;
}
}
if (gLogToLeaky) {
}
else {
// Can't use PR_LOG(), b/c it truncates the line
}
}
}
#endif
return NS_OK;
}
const char* aClazz)
{
#ifdef NS_BUILD_REFCNT_LOGGING
if (!gInitialized)
InitTraceLog();
if (gLogging) {
if (gBloatLog) {
if (entry) {
}
}
if (gSerialNumbers && loggingThisType) {
if(count)
(*count)--;
}
if (gLogToLeaky) {
}
else {
// Can't use PR_LOG(), b/c it truncates the line
}
}
// Here's the case where neither NS_DELETEXPCOM nor MOZ_COUNT_DTOR were used,
// yet we still want to see deletion information:
"\n<%s> 0x%08X %d Destroy\n",
}
}
}
#endif
return NS_OK;
}
const char* aType,
{
#ifdef NS_BUILD_REFCNT_LOGGING
if (!gInitialized)
InitTraceLog();
if (gLogging) {
if (gBloatLog) {
if (entry) {
}
}
if (gSerialNumbers && loggingThisType) {
}
}
}
#endif
return NS_OK;
}
const char* aType,
{
#ifdef NS_BUILD_REFCNT_LOGGING
if (!gInitialized)
InitTraceLog();
if (gLogging) {
if (gBloatLog) {
if (entry) {
}
}
if (gSerialNumbers && loggingThisType) {
}
// (If we're on a losing architecture, don't do this because we'll be
// using LogDeleteXPCOM instead to get file and line numbers.)
}
}
#endif
return NS_OK;
}
{
#if defined(NS_BUILD_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
// Get the most-derived object.
// This is a very indirect way of finding out what the class is
// of the object being logged. If we're logging a specific type,
// then
if (!gTypesToLog || !gSerialNumbers) {
return NS_OK;
}
if (serialno == 0) {
return NS_OK;
}
if (!gInitialized)
InitTraceLog();
if (gLogging) {
if(count)
(*count)++;
if (gCOMPtrLog && loggingThisObject) {
}
}
#endif
return NS_OK;
}
{
#if defined(NS_BUILD_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
// Get the most-derived object.
// This is a very indirect way of finding out what the class is
// of the object being logged. If we're logging a specific type,
// then
if (!gTypesToLog || !gSerialNumbers) {
return NS_OK;
}
if (serialno == 0) {
return NS_OK;
}
if (!gInitialized)
InitTraceLog();
if (gLogging) {
if(count)
(*count)--;
if (gCOMPtrLog && loggingThisObject) {
}
}
#endif
return NS_OK;
}
NS_COM void
{
#ifdef NS_BUILD_REFCNT_LOGGING
#endif
}
NS_COM void
{
#ifdef NS_BUILD_REFCNT_LOGGING
if (gBloatView) {
gBloatView = nsnull;
}
if (gTypesToLog) {
}
if (gObjectsToLog) {
}
if (gSerialNumbers) {
}
#endif
}
NS_COM void
{
#ifdef NS_BUILD_REFCNT_LOGGING
#endif
}
{
*aInstancePtr = nsnull;
if (!tracer)
return NS_ERROR_OUT_OF_MEMORY;
delete tracer;
}
return rv;
}