зеркало из https://github.com/mozilla/gecko-dev.git
1356 строки
37 KiB
C++
1356 строки
37 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nsTraceRefcntImpl.h"
|
|
#include "nsXPCOMPrivate.h"
|
|
#include "nscore.h"
|
|
#include "nsISupports.h"
|
|
#include "nsTArray.h"
|
|
#include "prenv.h"
|
|
#include "prprf.h"
|
|
#include "prlog.h"
|
|
#include "plstr.h"
|
|
#include "prlink.h"
|
|
#include <stdlib.h>
|
|
#include "nsCOMPtr.h"
|
|
#include "nsCRT.h"
|
|
#include <math.h>
|
|
#include "nsStackWalkPrivate.h"
|
|
#include "nsStackWalk.h"
|
|
#include "nsString.h"
|
|
|
|
#include "nsXULAppAPI.h"
|
|
#ifdef XP_WIN
|
|
#include <process.h>
|
|
#define getpid _getpid
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef NS_TRACE_MALLOC
|
|
#include "nsTraceMalloc.h"
|
|
#endif
|
|
|
|
#include "mozilla/BlockingResourceBase.h"
|
|
#include "mozilla/mozPoisonWrite.h"
|
|
|
|
#ifdef HAVE_DLOPEN
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void
|
|
NS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
|
|
double *meanResult, double *stdDevResult)
|
|
{
|
|
double mean = 0.0, var = 0.0, stdDev = 0.0;
|
|
if (n > 0.0 && sumOfValues >= 0) {
|
|
mean = sumOfValues / n;
|
|
double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
|
|
if (temp < 0.0 || n <= 1)
|
|
var = 0.0;
|
|
else
|
|
var = temp / (n * (n - 1));
|
|
// for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
|
|
stdDev = var != 0.0 ? sqrt(var) : 0.0;
|
|
}
|
|
*meanResult = mean;
|
|
*stdDevResult = stdDev;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#define NS_IMPL_REFCNT_LOGGING
|
|
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
#include "plhash.h"
|
|
#include "prmem.h"
|
|
|
|
#include "prlock.h"
|
|
|
|
// TraceRefcnt has to use bare PRLock instead of mozilla::Mutex
|
|
// because TraceRefcnt can be used very early in startup.
|
|
static PRLock* gTraceLock;
|
|
|
|
#define LOCK_TRACELOG() PR_Lock(gTraceLock)
|
|
#define UNLOCK_TRACELOG() PR_Unlock(gTraceLock)
|
|
|
|
static PLHashTable* gBloatView;
|
|
static PLHashTable* gTypesToLog;
|
|
static PLHashTable* gObjectsToLog;
|
|
static PLHashTable* gSerialNumbers;
|
|
static intptr_t gNextSerialNumber;
|
|
|
|
static bool gLogging;
|
|
static bool gLogToLeaky;
|
|
static bool gLogLeaksOnly;
|
|
|
|
static void (*leakyLogAddRef)(void* p, int oldrc, int newrc);
|
|
static void (*leakyLogRelease)(void* p, int oldrc, int newrc);
|
|
|
|
#define BAD_TLS_INDEX ((unsigned) -1)
|
|
|
|
// if gActivityTLS == BAD_TLS_INDEX, then we're
|
|
// unitialized... otherwise this points to a NSPR TLS thread index
|
|
// indicating whether addref activity is legal. If the PTR_TO_INT32 is 0 then
|
|
// activity is ok, otherwise not!
|
|
static unsigned gActivityTLS = BAD_TLS_INDEX;
|
|
|
|
static bool gInitialized;
|
|
static nsrefcnt gInitCount;
|
|
|
|
static FILE *gBloatLog = nullptr;
|
|
static FILE *gRefcntsLog = nullptr;
|
|
static FILE *gAllocLog = nullptr;
|
|
static FILE *gLeakyLog = nullptr;
|
|
static FILE *gCOMPtrLog = nullptr;
|
|
|
|
struct serialNumberRecord {
|
|
intptr_t serialNumber;
|
|
int32_t refCount;
|
|
int32_t COMPtrCount;
|
|
};
|
|
|
|
struct nsTraceRefcntStats {
|
|
uint64_t mAddRefs;
|
|
uint64_t mReleases;
|
|
uint64_t mCreates;
|
|
uint64_t mDestroys;
|
|
double mRefsOutstandingTotal;
|
|
double mRefsOutstandingSquared;
|
|
double mObjsOutstandingTotal;
|
|
double mObjsOutstandingSquared;
|
|
};
|
|
|
|
// I hope to turn this on for everybody once we hit it a little less.
|
|
#ifdef DEBUG
|
|
static const char kStaticCtorDtorWarning[] =
|
|
"XPCOM objects created/destroyed from static ctor/dtor";
|
|
|
|
static void
|
|
AssertActivityIsLegal()
|
|
{
|
|
if (gActivityTLS == BAD_TLS_INDEX ||
|
|
NS_PTR_TO_INT32(PR_GetThreadPrivate(gActivityTLS)) != 0) {
|
|
if (PR_GetEnv("MOZ_FATAL_STATIC_XPCOM_CTORS_DTORS")) {
|
|
NS_RUNTIMEABORT(kStaticCtorDtorWarning);
|
|
} else {
|
|
NS_WARNING(kStaticCtorDtorWarning);
|
|
}
|
|
}
|
|
}
|
|
# define ASSERT_ACTIVITY_IS_LEGAL \
|
|
PR_BEGIN_MACRO \
|
|
AssertActivityIsLegal(); \
|
|
PR_END_MACRO
|
|
#else
|
|
# define ASSERT_ACTIVITY_IS_LEGAL PR_BEGIN_MACRO PR_END_MACRO
|
|
#endif // DEBUG
|
|
|
|
// These functions are copied from nsprpub/lib/ds/plhash.c, with changes
|
|
// to the functions not called Default* to free the serialNumberRecord or
|
|
// the BloatEntry.
|
|
|
|
static void *
|
|
DefaultAllocTable(void *pool, size_t size)
|
|
{
|
|
return PR_MALLOC(size);
|
|
}
|
|
|
|
static void
|
|
DefaultFreeTable(void *pool, void *item)
|
|
{
|
|
PR_Free(item);
|
|
}
|
|
|
|
static PLHashEntry *
|
|
DefaultAllocEntry(void *pool, const void *key)
|
|
{
|
|
return PR_NEW(PLHashEntry);
|
|
}
|
|
|
|
static void
|
|
SerialNumberFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
|
|
{
|
|
if (flag == HT_FREE_ENTRY) {
|
|
PR_Free(reinterpret_cast<serialNumberRecord*>(he->value));
|
|
PR_Free(he);
|
|
}
|
|
}
|
|
|
|
static void
|
|
TypesToLogFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
|
|
{
|
|
if (flag == HT_FREE_ENTRY) {
|
|
nsCRT::free(const_cast<char*>
|
|
(reinterpret_cast<const char*>(he->key)));
|
|
PR_Free(he);
|
|
}
|
|
}
|
|
|
|
static const PLHashAllocOps serialNumberHashAllocOps = {
|
|
DefaultAllocTable, DefaultFreeTable,
|
|
DefaultAllocEntry, SerialNumberFreeEntry
|
|
};
|
|
|
|
static const PLHashAllocOps typesToLogHashAllocOps = {
|
|
DefaultAllocTable, DefaultFreeTable,
|
|
DefaultAllocEntry, TypesToLogFreeEntry
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class BloatEntry {
|
|
public:
|
|
BloatEntry(const char* className, uint32_t classSize)
|
|
: mClassSize(classSize) {
|
|
mClassName = PL_strdup(className);
|
|
Clear(&mNewStats);
|
|
Clear(&mAllStats);
|
|
mTotalLeaked = 0;
|
|
}
|
|
|
|
~BloatEntry() {
|
|
PL_strfree(mClassName);
|
|
}
|
|
|
|
uint32_t GetClassSize() { return (uint32_t)mClassSize; }
|
|
const char* GetClassName() { return mClassName; }
|
|
|
|
static void Clear(nsTraceRefcntStats* stats) {
|
|
stats->mAddRefs = 0;
|
|
stats->mReleases = 0;
|
|
stats->mCreates = 0;
|
|
stats->mDestroys = 0;
|
|
stats->mRefsOutstandingTotal = 0;
|
|
stats->mRefsOutstandingSquared = 0;
|
|
stats->mObjsOutstandingTotal = 0;
|
|
stats->mObjsOutstandingSquared = 0;
|
|
}
|
|
|
|
void Accumulate() {
|
|
mAllStats.mAddRefs += mNewStats.mAddRefs;
|
|
mAllStats.mReleases += mNewStats.mReleases;
|
|
mAllStats.mCreates += mNewStats.mCreates;
|
|
mAllStats.mDestroys += mNewStats.mDestroys;
|
|
mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal;
|
|
mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared;
|
|
mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal;
|
|
mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared;
|
|
Clear(&mNewStats);
|
|
}
|
|
|
|
void AddRef(nsrefcnt refcnt) {
|
|
mNewStats.mAddRefs++;
|
|
if (refcnt == 1) {
|
|
Ctor();
|
|
}
|
|
AccountRefs();
|
|
}
|
|
|
|
void Release(nsrefcnt refcnt) {
|
|
mNewStats.mReleases++;
|
|
if (refcnt == 0) {
|
|
Dtor();
|
|
}
|
|
AccountRefs();
|
|
}
|
|
|
|
void Ctor() {
|
|
mNewStats.mCreates++;
|
|
AccountObjs();
|
|
}
|
|
|
|
void Dtor() {
|
|
mNewStats.mDestroys++;
|
|
AccountObjs();
|
|
}
|
|
|
|
void AccountRefs() {
|
|
uint64_t cnt = (mNewStats.mAddRefs - mNewStats.mReleases);
|
|
mNewStats.mRefsOutstandingTotal += cnt;
|
|
mNewStats.mRefsOutstandingSquared += cnt * cnt;
|
|
}
|
|
|
|
void AccountObjs() {
|
|
uint64_t cnt = (mNewStats.mCreates - mNewStats.mDestroys);
|
|
mNewStats.mObjsOutstandingTotal += cnt;
|
|
mNewStats.mObjsOutstandingSquared += cnt * cnt;
|
|
}
|
|
|
|
static int DumpEntry(PLHashEntry *he, int i, void *arg) {
|
|
BloatEntry* entry = (BloatEntry*)he->value;
|
|
if (entry) {
|
|
entry->Accumulate();
|
|
static_cast<nsTArray<BloatEntry*>*>(arg)->AppendElement(entry);
|
|
}
|
|
return HT_ENUMERATE_NEXT;
|
|
}
|
|
|
|
static int TotalEntries(PLHashEntry *he, int i, void *arg) {
|
|
BloatEntry* entry = (BloatEntry*)he->value;
|
|
if (entry && nsCRT::strcmp(entry->mClassName, "TOTAL") != 0) {
|
|
entry->Total((BloatEntry*)arg);
|
|
}
|
|
return HT_ENUMERATE_NEXT;
|
|
}
|
|
|
|
void Total(BloatEntry* total) {
|
|
total->mAllStats.mAddRefs += mNewStats.mAddRefs + mAllStats.mAddRefs;
|
|
total->mAllStats.mReleases += mNewStats.mReleases + mAllStats.mReleases;
|
|
total->mAllStats.mCreates += mNewStats.mCreates + mAllStats.mCreates;
|
|
total->mAllStats.mDestroys += mNewStats.mDestroys + mAllStats.mDestroys;
|
|
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;
|
|
uint64_t count = (mNewStats.mCreates + mAllStats.mCreates);
|
|
total->mClassSize += mClassSize * count; // adjust for average in DumpTotal
|
|
total->mTotalLeaked += (uint64_t)(mClassSize *
|
|
((mNewStats.mCreates + mAllStats.mCreates)
|
|
-(mNewStats.mDestroys + mAllStats.mDestroys)));
|
|
}
|
|
|
|
void DumpTotal(FILE* out) {
|
|
mClassSize /= mAllStats.mCreates;
|
|
Dump(-1, out, nsTraceRefcntImpl::ALL_STATS);
|
|
}
|
|
|
|
static bool HaveLeaks(nsTraceRefcntStats* stats) {
|
|
return ((stats->mAddRefs != stats->mReleases) ||
|
|
(stats->mCreates != stats->mDestroys));
|
|
}
|
|
|
|
bool PrintDumpHeader(FILE* out, const char* msg, nsTraceRefcntImpl::StatisticsType type) {
|
|
fprintf(out, "\n== BloatView: %s, %s process %d\n", msg,
|
|
XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid());
|
|
nsTraceRefcntStats& stats =
|
|
(type == nsTraceRefcntImpl::NEW_STATS) ? mNewStats : mAllStats;
|
|
if (gLogLeaksOnly && !HaveLeaks(&stats))
|
|
return false;
|
|
|
|
fprintf(out,
|
|
"\n" \
|
|
" |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n" \
|
|
" Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
|
|
|
|
this->DumpTotal(out);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Dump(int i, FILE* out, nsTraceRefcntImpl::StatisticsType type) {
|
|
nsTraceRefcntStats* stats = (type == nsTraceRefcntImpl::NEW_STATS) ? &mNewStats : &mAllStats;
|
|
if (gLogLeaksOnly && !HaveLeaks(stats)) {
|
|
return;
|
|
}
|
|
|
|
double meanRefs, stddevRefs;
|
|
NS_MeanAndStdDev(stats->mAddRefs + stats->mReleases,
|
|
stats->mRefsOutstandingTotal,
|
|
stats->mRefsOutstandingSquared,
|
|
&meanRefs, &stddevRefs);
|
|
|
|
double meanObjs, stddevObjs;
|
|
NS_MeanAndStdDev(stats->mCreates + stats->mDestroys,
|
|
stats->mObjsOutstandingTotal,
|
|
stats->mObjsOutstandingSquared,
|
|
&meanObjs, &stddevObjs);
|
|
|
|
if ((stats->mAddRefs - stats->mReleases) != 0 ||
|
|
stats->mAddRefs != 0 ||
|
|
meanRefs != 0 ||
|
|
stddevRefs != 0 ||
|
|
(stats->mCreates - stats->mDestroys) != 0 ||
|
|
stats->mCreates != 0 ||
|
|
meanObjs != 0 ||
|
|
stddevObjs != 0) {
|
|
fprintf(out, "%4d %-40.40s %8d %8llu %8llu %8llu (%8.2f +/- %8.2f) %8llu %8llu (%8.2f +/- %8.2f)\n",
|
|
i+1, mClassName,
|
|
(int32_t)mClassSize,
|
|
(nsCRT::strcmp(mClassName, "TOTAL"))
|
|
?(uint64_t)((stats->mCreates - stats->mDestroys) * mClassSize)
|
|
:mTotalLeaked,
|
|
stats->mCreates,
|
|
(stats->mCreates - stats->mDestroys),
|
|
meanObjs,
|
|
stddevObjs,
|
|
stats->mAddRefs,
|
|
(stats->mAddRefs - stats->mReleases),
|
|
meanRefs,
|
|
stddevRefs);
|
|
}
|
|
}
|
|
|
|
protected:
|
|
char* mClassName;
|
|
double mClassSize; // this is stored as a double because of the way we compute the avg class size for total bloat
|
|
uint64_t mTotalLeaked; // used only for TOTAL entry
|
|
nsTraceRefcntStats mNewStats;
|
|
nsTraceRefcntStats mAllStats;
|
|
};
|
|
|
|
static void
|
|
BloatViewFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
|
|
{
|
|
if (flag == HT_FREE_ENTRY) {
|
|
BloatEntry* entry = reinterpret_cast<BloatEntry*>(he->value);
|
|
delete entry;
|
|
PR_Free(he);
|
|
}
|
|
}
|
|
|
|
const static PLHashAllocOps bloatViewHashAllocOps = {
|
|
DefaultAllocTable, DefaultFreeTable,
|
|
DefaultAllocEntry, BloatViewFreeEntry
|
|
};
|
|
|
|
static void
|
|
RecreateBloatView()
|
|
{
|
|
gBloatView = PL_NewHashTable(256,
|
|
PL_HashString,
|
|
PL_CompareStrings,
|
|
PL_CompareValues,
|
|
&bloatViewHashAllocOps, NULL);
|
|
}
|
|
|
|
static BloatEntry*
|
|
GetBloatEntry(const char* aTypeName, uint32_t aInstanceSize)
|
|
{
|
|
if (!gBloatView) {
|
|
RecreateBloatView();
|
|
}
|
|
BloatEntry* entry = NULL;
|
|
if (gBloatView) {
|
|
entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName);
|
|
if (entry == NULL && aInstanceSize > 0) {
|
|
|
|
entry = new BloatEntry(aTypeName, aInstanceSize);
|
|
PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry);
|
|
if (e == NULL) {
|
|
delete entry;
|
|
entry = NULL;
|
|
}
|
|
} else {
|
|
NS_ASSERTION(aInstanceSize == 0 ||
|
|
entry->GetClassSize() == aInstanceSize,
|
|
"bad size recorded");
|
|
}
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
static int DumpSerialNumbers(PLHashEntry* aHashEntry, int aIndex, void* aClosure)
|
|
{
|
|
serialNumberRecord* record = reinterpret_cast<serialNumberRecord *>(aHashEntry->value);
|
|
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
|
|
fprintf((FILE*) aClosure, "%ld @%p (%d references; %d from COMPtrs)\n",
|
|
record->serialNumber,
|
|
NS_INT32_TO_PTR(aHashEntry->key),
|
|
record->refCount,
|
|
record->COMPtrCount);
|
|
#else
|
|
fprintf((FILE*) aClosure, "%ld @%p (%d references)\n",
|
|
record->serialNumber,
|
|
NS_INT32_TO_PTR(aHashEntry->key),
|
|
record->refCount);
|
|
#endif
|
|
return HT_ENUMERATE_NEXT;
|
|
}
|
|
|
|
|
|
template <>
|
|
class nsDefaultComparator <BloatEntry*, BloatEntry*> {
|
|
public:
|
|
bool Equals(BloatEntry* const& aA, BloatEntry* const& aB) const {
|
|
return PL_strcmp(aA->GetClassName(), aB->GetClassName()) == 0;
|
|
}
|
|
bool LessThan(BloatEntry* const& aA, BloatEntry* const& aB) const {
|
|
return PL_strcmp(aA->GetClassName(), aB->GetClassName()) < 0;
|
|
}
|
|
};
|
|
|
|
#endif /* NS_IMPL_REFCNT_LOGGING */
|
|
|
|
nsresult
|
|
nsTraceRefcntImpl::DumpStatistics(StatisticsType type, FILE* out)
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
if (gBloatLog == nullptr || gBloatView == nullptr) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
if (out == nullptr) {
|
|
out = gBloatLog;
|
|
}
|
|
|
|
LOCK_TRACELOG();
|
|
|
|
bool wasLogging = gLogging;
|
|
gLogging = false; // turn off logging for this method
|
|
|
|
BloatEntry total("TOTAL", 0);
|
|
PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
|
|
const char* msg;
|
|
if (type == NEW_STATS) {
|
|
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";
|
|
}
|
|
const bool leaked = total.PrintDumpHeader(out, msg, type);
|
|
|
|
nsTArray<BloatEntry*> entries;
|
|
PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries);
|
|
const uint32_t count = entries.Length();
|
|
|
|
if (!gLogLeaksOnly || leaked) {
|
|
// Sort the entries alphabetically by classname.
|
|
entries.Sort();
|
|
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
BloatEntry* entry = entries[i];
|
|
entry->Dump(i, out, type);
|
|
}
|
|
|
|
fprintf(out, "\n");
|
|
}
|
|
|
|
fprintf(out, "nsTraceRefcntImpl::DumpStatistics: %d entries\n", count);
|
|
|
|
if (gSerialNumbers) {
|
|
fprintf(out, "\nSerial Numbers of Leaked Objects:\n");
|
|
PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, out);
|
|
}
|
|
|
|
gLogging = wasLogging;
|
|
UNLOCK_TRACELOG();
|
|
#endif
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsTraceRefcntImpl::ResetStatistics()
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
LOCK_TRACELOG();
|
|
if (gBloatView) {
|
|
PL_HashTableDestroy(gBloatView);
|
|
gBloatView = nullptr;
|
|
}
|
|
UNLOCK_TRACELOG();
|
|
#endif
|
|
}
|
|
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
static bool LogThisType(const char* aTypeName)
|
|
{
|
|
void* he = PL_HashTableLookup(gTypesToLog, aTypeName);
|
|
return nullptr != he;
|
|
}
|
|
|
|
static intptr_t GetSerialNumber(void* aPtr, bool aCreate)
|
|
{
|
|
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
|
|
if (hep && *hep) {
|
|
return reinterpret_cast<serialNumberRecord*>((*hep)->value)->serialNumber;
|
|
}
|
|
else if (aCreate) {
|
|
serialNumberRecord *record = PR_NEW(serialNumberRecord);
|
|
record->serialNumber = ++gNextSerialNumber;
|
|
record->refCount = 0;
|
|
record->COMPtrCount = 0;
|
|
PL_HashTableRawAdd(gSerialNumbers, hep, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr, reinterpret_cast<void*>(record));
|
|
return gNextSerialNumber;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int32_t* GetRefCount(void* aPtr)
|
|
{
|
|
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
|
|
if (hep && *hep) {
|
|
return &((reinterpret_cast<serialNumberRecord*>((*hep)->value))->refCount);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
#if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
|
|
static int32_t* GetCOMPtrCount(void* aPtr)
|
|
{
|
|
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
|
|
if (hep && *hep) {
|
|
return &((reinterpret_cast<serialNumberRecord*>((*hep)->value))->COMPtrCount);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void RecycleSerialNumberPtr(void* aPtr)
|
|
{
|
|
PL_HashTableRemove(gSerialNumbers, aPtr);
|
|
}
|
|
|
|
static bool LogThisObj(intptr_t aSerialNumber)
|
|
{
|
|
return nullptr != PL_HashTableLookup(gObjectsToLog, (const void*)aSerialNumber);
|
|
}
|
|
|
|
#ifdef XP_WIN
|
|
#define FOPEN_NO_INHERIT "N"
|
|
#else
|
|
#define FOPEN_NO_INHERIT
|
|
#endif
|
|
|
|
static bool InitLog(const char* envVar, const char* msg, FILE* *result)
|
|
{
|
|
const char* value = getenv(envVar);
|
|
if (value) {
|
|
if (nsCRT::strcmp(value, "1") == 0) {
|
|
*result = stdout;
|
|
fprintf(stdout, "### %s defined -- logging %s to stdout\n",
|
|
envVar, msg);
|
|
return true;
|
|
}
|
|
else if (nsCRT::strcmp(value, "2") == 0) {
|
|
*result = stderr;
|
|
fprintf(stdout, "### %s defined -- logging %s to stderr\n",
|
|
envVar, msg);
|
|
return true;
|
|
}
|
|
else {
|
|
FILE *stream;
|
|
nsAutoCString fname(value);
|
|
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
|
bool hasLogExtension =
|
|
fname.RFind(".log", true, -1, 4) == kNotFound ? false : true;
|
|
if (hasLogExtension)
|
|
fname.Cut(fname.Length() - 4, 4);
|
|
fname.AppendLiteral("_");
|
|
fname.Append((char*)XRE_ChildProcessTypeToString(XRE_GetProcessType()));
|
|
fname.AppendLiteral("_pid");
|
|
fname.AppendInt((uint32_t)getpid());
|
|
if (hasLogExtension)
|
|
fname.AppendLiteral(".log");
|
|
}
|
|
stream = ::fopen(fname.get(), "w" FOPEN_NO_INHERIT);
|
|
if (stream != NULL) {
|
|
MozillaRegisterDebugFD(fileno(stream));
|
|
*result = stream;
|
|
fprintf(stdout, "### %s defined -- logging %s to %s\n",
|
|
envVar, msg, fname.get());
|
|
}
|
|
else {
|
|
fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
|
|
envVar, msg, fname.get());
|
|
}
|
|
return stream != NULL;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static PLHashNumber HashNumber(const void* aKey)
|
|
{
|
|
return PLHashNumber(NS_PTR_TO_INT32(aKey));
|
|
}
|
|
|
|
static void InitTraceLog(void)
|
|
{
|
|
if (gInitialized) return;
|
|
gInitialized = true;
|
|
|
|
bool defined;
|
|
defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
|
|
if (!defined)
|
|
gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
|
|
if (defined || gLogLeaksOnly) {
|
|
RecreateBloatView();
|
|
if (!gBloatView) {
|
|
NS_WARNING("out of memory");
|
|
gBloatLog = nullptr;
|
|
gLogLeaksOnly = false;
|
|
}
|
|
}
|
|
|
|
(void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
|
|
|
|
(void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
|
|
|
|
defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog);
|
|
if (defined) {
|
|
gLogToLeaky = true;
|
|
PRFuncPtr p = nullptr, q = nullptr;
|
|
#ifdef HAVE_DLOPEN
|
|
{
|
|
PRLibrary *lib = nullptr;
|
|
p = PR_FindFunctionSymbolAndLibrary("__log_addref", &lib);
|
|
if (lib) {
|
|
PR_UnloadLibrary(lib);
|
|
lib = nullptr;
|
|
}
|
|
q = PR_FindFunctionSymbolAndLibrary("__log_release", &lib);
|
|
if (lib) {
|
|
PR_UnloadLibrary(lib);
|
|
}
|
|
}
|
|
#endif
|
|
if (p && q) {
|
|
leakyLogAddRef = (void (*)(void*,int,int)) p;
|
|
leakyLogRelease = (void (*)(void*,int,int)) q;
|
|
}
|
|
else {
|
|
gLogToLeaky = false;
|
|
fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
|
|
const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
|
|
|
|
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
|
|
if (classes) {
|
|
(void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
|
|
} else {
|
|
if (getenv("XPCOM_MEM_COMPTR_LOG")) {
|
|
fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
|
|
}
|
|
}
|
|
#else
|
|
const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
|
|
if (comptr_log) {
|
|
fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
|
|
}
|
|
#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
|
|
gTypesToLog = PL_NewHashTable(256,
|
|
PL_HashString,
|
|
PL_CompareStrings,
|
|
PL_CompareValues,
|
|
&typesToLogHashAllocOps, NULL);
|
|
if (!gTypesToLog) {
|
|
NS_WARNING("out of memory");
|
|
fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
|
|
}
|
|
else {
|
|
fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
|
|
const char* cp = classes;
|
|
for (;;) {
|
|
char* cm = (char*) strchr(cp, ',');
|
|
if (cm) {
|
|
*cm = '\0';
|
|
}
|
|
PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1);
|
|
fprintf(stdout, "%s ", cp);
|
|
if (!cm) break;
|
|
*cm = ',';
|
|
cp = cm + 1;
|
|
}
|
|
fprintf(stdout, "\n");
|
|
}
|
|
|
|
gSerialNumbers = PL_NewHashTable(256,
|
|
HashNumber,
|
|
PL_CompareValues,
|
|
PL_CompareValues,
|
|
&serialNumberHashAllocOps, NULL);
|
|
|
|
|
|
}
|
|
|
|
const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
|
|
if (objects) {
|
|
gObjectsToLog = PL_NewHashTable(256,
|
|
HashNumber,
|
|
PL_CompareValues,
|
|
PL_CompareValues,
|
|
NULL, NULL);
|
|
|
|
if (!gObjectsToLog) {
|
|
NS_WARNING("out of memory");
|
|
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
|
|
}
|
|
else if (! (gRefcntsLog || gAllocLog || gCOMPtrLog)) {
|
|
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
|
|
}
|
|
else {
|
|
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
|
|
const char* cp = objects;
|
|
for (;;) {
|
|
char* cm = (char*) strchr(cp, ',');
|
|
if (cm) {
|
|
*cm = '\0';
|
|
}
|
|
intptr_t top = 0;
|
|
intptr_t bottom = 0;
|
|
while (*cp) {
|
|
if (*cp == '-') {
|
|
bottom = top;
|
|
top = 0;
|
|
++cp;
|
|
}
|
|
top *= 10;
|
|
top += *cp - '0';
|
|
++cp;
|
|
}
|
|
if (!bottom) {
|
|
bottom = top;
|
|
}
|
|
for (intptr_t serialno = bottom; serialno <= top; serialno++) {
|
|
PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
|
|
fprintf(stdout, "%ld ", serialno);
|
|
}
|
|
if (!cm) break;
|
|
*cm = ',';
|
|
cp = cm + 1;
|
|
}
|
|
fprintf(stdout, "\n");
|
|
}
|
|
}
|
|
|
|
|
|
if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog || gCOMPtrLog) {
|
|
gLogging = true;
|
|
}
|
|
|
|
gTraceLock = PR_NewLock();
|
|
}
|
|
|
|
#endif
|
|
|
|
extern "C" {
|
|
|
|
static void PrintStackFrame(void *aPC, void *aSP, void *aClosure)
|
|
{
|
|
FILE *stream = (FILE*)aClosure;
|
|
nsCodeAddressDetails details;
|
|
char buf[1024];
|
|
|
|
NS_DescribeCodeAddress(aPC, &details);
|
|
NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf));
|
|
fputs(buf, stream);
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
|
|
{
|
|
NS_StackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, aStream,
|
|
0, nullptr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// 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
|
|
|
|
void
|
|
nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol,
|
|
char * aBuffer,
|
|
int aBufLen)
|
|
{
|
|
NS_ASSERTION(nullptr != aSymbol,"null symbol");
|
|
NS_ASSERTION(nullptr != aBuffer,"null buffer");
|
|
NS_ASSERTION(aBufLen >= 32 ,"pulled 32 out of you know where");
|
|
|
|
aBuffer[0] = '\0';
|
|
|
|
#if defined(MOZ_DEMANGLE_SYMBOLS)
|
|
/* See demangle.h in the gcc source for the voodoo */
|
|
char * demangled = abi::__cxa_demangle(aSymbol,0,0,0);
|
|
|
|
if (demangled)
|
|
{
|
|
strncpy(aBuffer,demangled,aBufLen);
|
|
free(demangled);
|
|
}
|
|
#endif // MOZ_DEMANGLE_SYMBOLS
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
EXPORT_XPCOM_API(void)
|
|
NS_LogInit()
|
|
{
|
|
// FIXME: This is called multiple times, we should probably not allow that.
|
|
StackWalkInitCriticalAddress();
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
if (++gInitCount)
|
|
nsTraceRefcntImpl::SetActivityIsLegal(true);
|
|
#endif
|
|
|
|
#ifdef NS_TRACE_MALLOC
|
|
// XXX we don't have to worry about shutting down trace-malloc; it
|
|
// handles this itself, through an atexit() callback.
|
|
if (!NS_TraceMallocHasStarted())
|
|
NS_TraceMallocStartup(-1); // -1 == no logging
|
|
#endif
|
|
}
|
|
|
|
EXPORT_XPCOM_API(void)
|
|
NS_LogTerm()
|
|
{
|
|
mozilla::LogTerm();
|
|
}
|
|
|
|
namespace mozilla {
|
|
void
|
|
LogTerm()
|
|
{
|
|
NS_ASSERTION(gInitCount > 0,
|
|
"NS_LogTerm without matching NS_LogInit");
|
|
|
|
if (--gInitCount == 0) {
|
|
#ifdef DEBUG
|
|
/* FIXME bug 491977: This is only going to operate on the
|
|
* BlockingResourceBase which is compiled into
|
|
* libxul/libxpcom_core.so. Anyone using external linkage will
|
|
* have their own copy of BlockingResourceBase statics which will
|
|
* not be freed by this method.
|
|
*
|
|
* It sounds like what we really want is to be able to register a
|
|
* callback function to call at XPCOM shutdown. Note that with
|
|
* this solution, however, we need to guarantee that
|
|
* BlockingResourceBase::Shutdown() runs after all other shutdown
|
|
* functions.
|
|
*/
|
|
BlockingResourceBase::Shutdown();
|
|
#endif
|
|
|
|
if (gInitialized) {
|
|
nsTraceRefcntImpl::DumpStatistics();
|
|
nsTraceRefcntImpl::ResetStatistics();
|
|
}
|
|
nsTraceRefcntImpl::Shutdown();
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
nsTraceRefcntImpl::SetActivityIsLegal(false);
|
|
gActivityTLS = BAD_TLS_INDEX;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
} // namespace mozilla
|
|
|
|
EXPORT_XPCOM_API(void)
|
|
NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt,
|
|
const char* aClazz, uint32_t classSize)
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
ASSERT_ACTIVITY_IS_LEGAL;
|
|
if (!gInitialized)
|
|
InitTraceLog();
|
|
if (gLogging) {
|
|
LOCK_TRACELOG();
|
|
|
|
if (gBloatLog) {
|
|
BloatEntry* entry = GetBloatEntry(aClazz, classSize);
|
|
if (entry) {
|
|
entry->AddRef(aRefcnt);
|
|
}
|
|
}
|
|
|
|
// Here's the case where MOZ_COUNT_CTOR was not used,
|
|
// yet we still want to see creation information:
|
|
|
|
bool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
|
|
intptr_t serialno = 0;
|
|
if (gSerialNumbers && loggingThisType) {
|
|
serialno = GetSerialNumber(aPtr, aRefcnt == 1);
|
|
NS_ASSERTION(serialno != 0,
|
|
"Serial number requested for unrecognized pointer! "
|
|
"Are you memmoving a refcounted object?");
|
|
int32_t* count = GetRefCount(aPtr);
|
|
if(count)
|
|
(*count)++;
|
|
|
|
}
|
|
|
|
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
|
if (aRefcnt == 1 && gAllocLog && loggingThisType && loggingThisObject) {
|
|
fprintf(gAllocLog, "\n<%s> 0x%08X %ld Create\n",
|
|
aClazz, NS_PTR_TO_INT32(aPtr), serialno);
|
|
nsTraceRefcntImpl::WalkTheStack(gAllocLog);
|
|
}
|
|
|
|
if (gRefcntsLog && loggingThisType && loggingThisObject) {
|
|
if (gLogToLeaky) {
|
|
(*leakyLogAddRef)(aPtr, aRefcnt - 1, aRefcnt);
|
|
}
|
|
else {
|
|
// Can't use PR_LOG(), b/c it truncates the line
|
|
fprintf(gRefcntsLog,
|
|
"\n<%s> 0x%08X %ld AddRef %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
|
|
nsTraceRefcntImpl::WalkTheStack(gRefcntsLog);
|
|
fflush(gRefcntsLog);
|
|
}
|
|
}
|
|
UNLOCK_TRACELOG();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
EXPORT_XPCOM_API(void)
|
|
NS_LogRelease(void* aPtr, nsrefcnt aRefcnt, const char* aClazz)
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
ASSERT_ACTIVITY_IS_LEGAL;
|
|
if (!gInitialized)
|
|
InitTraceLog();
|
|
if (gLogging) {
|
|
LOCK_TRACELOG();
|
|
|
|
if (gBloatLog) {
|
|
BloatEntry* entry = GetBloatEntry(aClazz, 0);
|
|
if (entry) {
|
|
entry->Release(aRefcnt);
|
|
}
|
|
}
|
|
|
|
bool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
|
|
intptr_t serialno = 0;
|
|
if (gSerialNumbers && loggingThisType) {
|
|
serialno = GetSerialNumber(aPtr, false);
|
|
NS_ASSERTION(serialno != 0,
|
|
"Serial number requested for unrecognized pointer! "
|
|
"Are you memmoving a refcounted object?");
|
|
int32_t* count = GetRefCount(aPtr);
|
|
if(count)
|
|
(*count)--;
|
|
|
|
}
|
|
|
|
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
|
if (gRefcntsLog && loggingThisType && loggingThisObject) {
|
|
if (gLogToLeaky) {
|
|
(*leakyLogRelease)(aPtr, aRefcnt + 1, aRefcnt);
|
|
}
|
|
else {
|
|
// Can't use PR_LOG(), b/c it truncates the line
|
|
fprintf(gRefcntsLog,
|
|
"\n<%s> 0x%08X %ld Release %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
|
|
nsTraceRefcntImpl::WalkTheStack(gRefcntsLog);
|
|
fflush(gRefcntsLog);
|
|
}
|
|
}
|
|
|
|
// Here's the case where MOZ_COUNT_DTOR was not used,
|
|
// yet we still want to see deletion information:
|
|
|
|
if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) {
|
|
fprintf(gAllocLog,
|
|
"\n<%s> 0x%08X %ld Destroy\n",
|
|
aClazz, NS_PTR_TO_INT32(aPtr), serialno);
|
|
nsTraceRefcntImpl::WalkTheStack(gAllocLog);
|
|
}
|
|
|
|
if (aRefcnt == 0 && gSerialNumbers && loggingThisType) {
|
|
RecycleSerialNumberPtr(aPtr);
|
|
}
|
|
|
|
UNLOCK_TRACELOG();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
EXPORT_XPCOM_API(void)
|
|
NS_LogCtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
ASSERT_ACTIVITY_IS_LEGAL;
|
|
if (!gInitialized)
|
|
InitTraceLog();
|
|
|
|
if (gLogging) {
|
|
LOCK_TRACELOG();
|
|
|
|
if (gBloatLog) {
|
|
BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
|
|
if (entry) {
|
|
entry->Ctor();
|
|
}
|
|
}
|
|
|
|
bool loggingThisType = (!gTypesToLog || LogThisType(aType));
|
|
intptr_t serialno = 0;
|
|
if (gSerialNumbers && loggingThisType) {
|
|
serialno = GetSerialNumber(aPtr, true);
|
|
}
|
|
|
|
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
|
if (gAllocLog && loggingThisType && loggingThisObject) {
|
|
fprintf(gAllocLog, "\n<%s> 0x%08X %ld Ctor (%d)\n",
|
|
aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
|
|
nsTraceRefcntImpl::WalkTheStack(gAllocLog);
|
|
}
|
|
|
|
UNLOCK_TRACELOG();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
EXPORT_XPCOM_API(void)
|
|
NS_LogDtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
ASSERT_ACTIVITY_IS_LEGAL;
|
|
if (!gInitialized)
|
|
InitTraceLog();
|
|
|
|
if (gLogging) {
|
|
LOCK_TRACELOG();
|
|
|
|
if (gBloatLog) {
|
|
BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
|
|
if (entry) {
|
|
entry->Dtor();
|
|
}
|
|
}
|
|
|
|
bool loggingThisType = (!gTypesToLog || LogThisType(aType));
|
|
intptr_t serialno = 0;
|
|
if (gSerialNumbers && loggingThisType) {
|
|
serialno = GetSerialNumber(aPtr, false);
|
|
RecycleSerialNumberPtr(aPtr);
|
|
}
|
|
|
|
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
|
|
|
// (If we're on a losing architecture, don't do this because we'll be
|
|
// using LogDeleteXPCOM instead to get file and line numbers.)
|
|
if (gAllocLog && loggingThisType && loggingThisObject) {
|
|
fprintf(gAllocLog, "\n<%s> 0x%08X %ld Dtor (%d)\n",
|
|
aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
|
|
nsTraceRefcntImpl::WalkTheStack(gAllocLog);
|
|
}
|
|
|
|
UNLOCK_TRACELOG();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
EXPORT_XPCOM_API(void)
|
|
NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject)
|
|
{
|
|
#if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
|
|
// Get the most-derived object.
|
|
void *object = dynamic_cast<void *>(aObject);
|
|
|
|
// 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;
|
|
}
|
|
intptr_t serialno = GetSerialNumber(object, false);
|
|
if (serialno == 0) {
|
|
return;
|
|
}
|
|
|
|
if (!gInitialized)
|
|
InitTraceLog();
|
|
if (gLogging) {
|
|
LOCK_TRACELOG();
|
|
|
|
int32_t* count = GetCOMPtrCount(object);
|
|
if(count)
|
|
(*count)++;
|
|
|
|
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
|
|
|
if (gCOMPtrLog && loggingThisObject) {
|
|
fprintf(gCOMPtrLog, "\n<?> 0x%08X %ld nsCOMPtrAddRef %d 0x%08X\n",
|
|
NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
|
|
nsTraceRefcntImpl::WalkTheStack(gCOMPtrLog);
|
|
}
|
|
|
|
UNLOCK_TRACELOG();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
EXPORT_XPCOM_API(void)
|
|
NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject)
|
|
{
|
|
#if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
|
|
// Get the most-derived object.
|
|
void *object = dynamic_cast<void *>(aObject);
|
|
|
|
// 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;
|
|
}
|
|
intptr_t serialno = GetSerialNumber(object, false);
|
|
if (serialno == 0) {
|
|
return;
|
|
}
|
|
|
|
if (!gInitialized)
|
|
InitTraceLog();
|
|
if (gLogging) {
|
|
LOCK_TRACELOG();
|
|
|
|
int32_t* count = GetCOMPtrCount(object);
|
|
if(count)
|
|
(*count)--;
|
|
|
|
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
|
|
|
if (gCOMPtrLog && loggingThisObject) {
|
|
fprintf(gCOMPtrLog, "\n<?> 0x%08X %ld nsCOMPtrRelease %d 0x%08X\n",
|
|
NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
|
|
nsTraceRefcntImpl::WalkTheStack(gCOMPtrLog);
|
|
}
|
|
|
|
UNLOCK_TRACELOG();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
nsTraceRefcntImpl::Startup()
|
|
{
|
|
}
|
|
|
|
static void maybeUnregisterAndCloseFile(FILE *&f) {
|
|
if (!f)
|
|
return;
|
|
|
|
MozillaUnRegisterDebugFILE(f);
|
|
fclose(f);
|
|
f = nullptr;
|
|
}
|
|
|
|
void
|
|
nsTraceRefcntImpl::Shutdown()
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
|
|
if (gBloatView) {
|
|
PL_HashTableDestroy(gBloatView);
|
|
gBloatView = nullptr;
|
|
}
|
|
if (gTypesToLog) {
|
|
PL_HashTableDestroy(gTypesToLog);
|
|
gTypesToLog = nullptr;
|
|
}
|
|
if (gObjectsToLog) {
|
|
PL_HashTableDestroy(gObjectsToLog);
|
|
gObjectsToLog = nullptr;
|
|
}
|
|
if (gSerialNumbers) {
|
|
PL_HashTableDestroy(gSerialNumbers);
|
|
gSerialNumbers = nullptr;
|
|
}
|
|
maybeUnregisterAndCloseFile(gBloatLog);
|
|
maybeUnregisterAndCloseFile(gRefcntsLog);
|
|
maybeUnregisterAndCloseFile(gAllocLog);
|
|
maybeUnregisterAndCloseFile(gLeakyLog);
|
|
maybeUnregisterAndCloseFile(gCOMPtrLog);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
nsTraceRefcntImpl::SetActivityIsLegal(bool aLegal)
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
if (gActivityTLS == BAD_TLS_INDEX)
|
|
PR_NewThreadPrivateIndex(&gActivityTLS, nullptr);
|
|
|
|
PR_SetThreadPrivate(gActivityTLS, NS_INT32_TO_PTR(!aLegal));
|
|
#endif
|
|
}
|
|
|
|
NS_IMPL_QUERY_INTERFACE1(nsTraceRefcntImpl, nsITraceRefcnt)
|
|
|
|
NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::AddRef(void)
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::Release(void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTraceRefcntImpl::LogAddRef(void *aPtr, nsrefcnt aNewRefcnt,
|
|
const char *aTypeName, uint32_t aSize)
|
|
{
|
|
NS_LogAddRef(aPtr, aNewRefcnt, aTypeName, aSize);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTraceRefcntImpl::LogRelease(void *aPtr, nsrefcnt aNewRefcnt,
|
|
const char *aTypeName)
|
|
{
|
|
NS_LogRelease(aPtr, aNewRefcnt, aTypeName);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTraceRefcntImpl::LogCtor(void *aPtr, const char *aTypeName, uint32_t aSize)
|
|
{
|
|
NS_LogCtor(aPtr, aTypeName, aSize);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTraceRefcntImpl::LogDtor(void *aPtr, const char *aTypeName, uint32_t aSize)
|
|
{
|
|
NS_LogDtor(aPtr, aTypeName, aSize);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTraceRefcntImpl::LogAddCOMPtr(void *aCOMPtr, nsISupports* aObject)
|
|
{
|
|
NS_LogCOMPtrAddRef(aCOMPtr, aObject);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTraceRefcntImpl::LogReleaseCOMPtr(void *aCOMPtr, nsISupports* aObject)
|
|
{
|
|
NS_LogCOMPtrRelease(aCOMPtr, aObject);
|
|
return NS_OK;
|
|
}
|
|
|
|
static const nsTraceRefcntImpl kTraceRefcntImpl;
|
|
|
|
NS_METHOD
|
|
nsTraceRefcntImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
|
|
{
|
|
return const_cast<nsTraceRefcntImpl*>(&kTraceRefcntImpl)->
|
|
QueryInterface(aIID, aInstancePtr);
|
|
}
|