mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
9489c945bf
- Bug 1169798 - Refresh the marionette server's window reference when switching between windows to avoid intermittent exception.;r=ato (606c3f22d8) - Bug 1169600 - Avoid misleading exception in message listeners in marionette server. r=ato (ec8d12becf) - Bug 1174941: Update server capabilities that are set on startup; r=ato (9ca7a8be79) - Bug 1174941: Remove all capabilities from desired capabilities if they are in the session capabilitiesand in requiredCapabilities; r=ato (453c905a90) - Bug 1174941: Update conditional to switch statement to allow more processing of capabilities; r=ato (1460802be1) - NO BUG: correct import in marionette about_pages tests r=me DONTBUILD (cad6eecb22) - Bug 1169600 - Remove message listeners intended to coordinate registering a new browser with marionette once the browser has been registered. r=ato (86fed67200) - Bug 1165449: Add the ability to set a proxy via capabilities on Marionette session start; r=jgriffin (b2107767bb) - Bug 1178468 - Update marionette cookie support to use nsICookie2. r=jgriffin (90a140c915) - Bug 1196920: Add specificationLevel capability to Marionette; r=jgriffin (d64f1b7b2e) - Bug 1197131: Use dispatcher for listener getCurrentUrl (b2ca32f3d0) - Bug 1197146: Part 2: Use dispatcher for listener findElementsContent (f324b851a6) - Bug 1197146: Part 1: Prime dispatcher in listener to resolve promises (947dc5bd29) - Bug 1197133: Use dispatcher for listener getTitle (76b884bb21) - Bug 1197141: Use dispatcher for listener getPageSource (7ad171b976) - Bug 1197143: Use dispatcher for listener goBack (c8302aa896) - Bug 1191432 - improving coverage for marionette accessibility checks. r=automatedtester (4ce11c5110) - Bug 1197146: Part 3: Use dispatcher for listener findElementContent (26fe7c4198) - Bug 1197146: Part 4: Use dispatcher for listener isElementSelected (de51ad7e17) - Bug 1197146: Part 5: Use dispatcher for listener getElementLocation (5c0620ad1d) - Bug 1197146: Part 6: Use dispatcher for listener clearElement (811670ed39) - Bug 1155716: Part 1: Remove submitElement from Marionette (52122d817e) - Bug 1197146: Part 7: Use dispatcher for listener isElementDisplayed (e7408e94ce) - Bug 1197146: Part 8: Use dispatcher for listener getElementValueOfCssProperty (591c71ba82) - Bug 1196987: Update UUID returned to be a valid UUID 4; r=jgriffin (e0b19efc78) - Bug 1152682: Correct invalid selector errors in elements.js (5466b28ff7) - Bug 1194224 - adding support for Shadow DOM in marionette. r=automatedtester (0f2b2580b9) - Bug 1200420 - Better handling of startup_timeout, r=AutomatedTester (0ae69578ab) - Bug 1139158 - Actions made availabe via marionette_driver object. r=jgriffin (4713e8fc1e) - goanna -> gecko (fa2a0d674f) - Bug 1143565: Change requirements to not be fixed to a specific marionette transport and driver; r=chmanchester (cbc9e7eab6) - Bug 1150050 - Bump manifestparser to v1.1 and marionette-client to v0.9.3 to pick up tagging feature, r=AutomatedTester (f63a01da3b) - Bug 1157823 - Update dependency to reflect that the marionette client requires a more recent mozhttpd. r=ato (594cde8a5e) - Bug 1155260: Fix base URL override in Marionette runner (a99bd14cac) - Bug 1159816 - Bump marionette_client to 0.12. DONTBUILD. CLOSED TREE. r=chmanchester (779b4c9866) - Bug 1161209 - Bump marionette-driver to 0.7, marionette-client to 0.13, r=AutomatedTester (175cc7adea) - Bug 1163833 - Add integration with browsermob-proxy, r=dburns (bd90184ee1) - Bug 983821 - 'marionette' cli entrypoint doesn't have access to proper packages. r=dburns (17d4abf193) - Bug 1168997 - Bump marionette-client to 0.14, r=dburns (5c10bd3404) - Bug 1169751 - Bump marionette-driver to 0.8, marionette-client to 0.15, r=dburns (95b3c45faa) - Bug 1177513 - Bump marionette-client to 0.16, r=dburns (d1228ed4bb) - Bug 902125 - Add a version config/flag for Python Marionette. r=dburns (f72e7463d7) - Bug 1177780 - remove useless stuff in automation.py. r=jgriffin (d0d0baf473) - partial of Bug 1170332 - Fix |mach robocop SINGLE_TEST|. (0838005e10) - goanna->gecko (ef97631963) - Bug 1162285 - Remove unused environment vars on Android; r=jmaher (bf6b44a3ae) - Bug 1160324 - Handle missing robocopApk files in runtestsremote.py. r=gbrown (3d5c874630) - Bug 1169476 -- Implement |mach robocop --serve|. r=gbrown (11039daa39) - Bug 1160351 - Improve newline handling in getLogcat; r=bc Bug 1160351 - Bustage fix for 57c6c589cfa1 on a CLOSED TREE (d64b410ef4) - Bug 1137289 - Guard against dumpsys failure in DroidADB; r=jmaher (4a39532464) - Bug 1175540 - Reduce timeouts for many adb devicemanager calls; r=mcote (528209a4d6) - bit of Bug 1160662 - Refer to robocop.ini in $OBJDIR/_tests, not $OBJDIR (c288d73c16) - Bug 1162479 - Fix mochitest make target regression with duplicate extraProfilePath, r=chmanchester (53251cff28) - Bug 1151533 - Upgrade manually set tc xre r=me (3bb5f8fecd) - Bug 1161709 - Pull from artifacts for xulrunner instead of s3 directly r=me (6314094083) - Bug 1144528 - Use hg share on testers r=garndt ON CLOSED TREE (46779ab50c) - Bug 1142565 - Update tester images to include same fonts as test slaves r=garndt (cf9e546dfa) - bits of Bug 1144927 - Directly bake in linux64-minidump_stackwak to tester images r=garndt (8b1f3ff968) - Bug 1157308 - part 1 - Reduce the leak threshold for content processes more. r=erahm (177289cf32) - Bug 1157308 - part 2 - Reduce the content process leak limit on OS X. r=erahm (98d14f78cd) - Bug 1173114 - Fallback to chunk-by-dir if runtimes file not found, r=ahal (c7cb797636) - Bug 1026290 - Avoid TypeError during Android mochitest-chrome; r=chmanchester (a8112e6ca1) - Bug 1173971 - Force core Xlib events on GTK3. r=karlt (17fc2475e0) - Bug 1144194 - Only parse test manifests once in mochitest, r=jmaher (37f5c3a764) - Bug 1171971 - Move test_paths argument out of mach and into mochitest; remove --test-path, r=chmanchester (3ab9acf758) - Bug 1178154 - move ShutdownLeaks and LSANLeaks from automationutils to mochitest. r=jgriffin (e8ec293a91) - Bug 1156982 - Add separators to BloatView output. r=froydnj (ac92a67ba9) - Bug 1152872 - Don't attempt to leak-check the tools that we run as part of the setup for the mochitest suite; r=mccr8 (dd78bcc8bc) - Bug 1158227 - part 1 - don't run TSan on test tools or the xpcshell HTTP server in mochitests; r=jmaher (59bbf448e5) - Bug 1158227 - part 2 - set TSAN_OPTIONS environment variable in automationutils.py for TSan; r=jmaher (66e607b25e) - Bug 1091284 - Remove systemMemory, environment from automationutils. r=jgriffin (817860ab08)
1295 lines
34 KiB
C++
1295 lines
34 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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 "nsTraceRefcnt.h"
|
|
#include "mozilla/IntegerPrintfMacros.h"
|
|
#include "mozilla/StaticPtr.h"
|
|
#include "nsXPCOMPrivate.h"
|
|
#include "nscore.h"
|
|
#include "nsISupports.h"
|
|
#include "nsTArray.h"
|
|
#include "nsTHashtable.h"
|
|
#include "prenv.h"
|
|
#include "plstr.h"
|
|
#include "prlink.h"
|
|
#include "nsCRT.h"
|
|
#include <math.h>
|
|
#include "nsHashKeys.h"
|
|
#include "mozilla/StackWalk.h"
|
|
#include "nsString.h"
|
|
#include "nsThreadUtils.h"
|
|
#include "CodeAddressService.h"
|
|
|
|
#include "nsXULAppAPI.h"
|
|
#ifdef XP_WIN
|
|
#include <process.h>
|
|
#define getpid _getpid
|
|
#else
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "mozilla/BlockingResourceBase.h"
|
|
#include "mozilla/PoisonIOInterposer.h"
|
|
|
|
#ifdef HAVE_DLOPEN
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#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;
|
|
|
|
// By default, debug builds only do bloat logging. Bloat logging
|
|
// only tries to record when an object is created or destroyed, so we
|
|
// optimize the common case in NS_LogAddRef and NS_LogRelease where
|
|
// only bloat logging is enabled and no logging needs to be done.
|
|
enum LoggingType
|
|
{
|
|
NoLogging,
|
|
OnlyBloatLogging,
|
|
FullLogging
|
|
};
|
|
|
|
static LoggingType gLogging;
|
|
|
|
static bool gLogLeaksOnly;
|
|
|
|
#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* gCOMPtrLog = nullptr;
|
|
|
|
struct serialNumberRecord
|
|
{
|
|
intptr_t serialNumber;
|
|
int32_t refCount;
|
|
int32_t COMPtrCount;
|
|
};
|
|
|
|
struct nsTraceRefcntStats
|
|
{
|
|
uint64_t mCreates;
|
|
uint64_t mDestroys;
|
|
};
|
|
|
|
#ifdef DEBUG
|
|
static const char kStaticCtorDtorWarning[] =
|
|
"XPCOM objects created/destroyed from static ctor/dtor";
|
|
|
|
static void
|
|
AssertActivityIsLegal()
|
|
{
|
|
if (gActivityTLS == BAD_TLS_INDEX || PR_GetThreadPrivate(gActivityTLS)) {
|
|
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* aPool, size_t aSize)
|
|
{
|
|
return PR_MALLOC(aSize);
|
|
}
|
|
|
|
static void
|
|
DefaultFreeTable(void* aPool, void* aItem)
|
|
{
|
|
PR_Free(aItem);
|
|
}
|
|
|
|
static PLHashEntry*
|
|
DefaultAllocEntry(void* aPool, const void* aKey)
|
|
{
|
|
return PR_NEW(PLHashEntry);
|
|
}
|
|
|
|
static void
|
|
SerialNumberFreeEntry(void* aPool, PLHashEntry* aHashEntry, unsigned aFlag)
|
|
{
|
|
if (aFlag == HT_FREE_ENTRY) {
|
|
PR_Free(reinterpret_cast<serialNumberRecord*>(aHashEntry->value));
|
|
PR_Free(aHashEntry);
|
|
}
|
|
}
|
|
|
|
static void
|
|
TypesToLogFreeEntry(void* aPool, PLHashEntry* aHashEntry, unsigned aFlag)
|
|
{
|
|
if (aFlag == HT_FREE_ENTRY) {
|
|
free(const_cast<char*>(reinterpret_cast<const char*>(aHashEntry->key)));
|
|
PR_Free(aHashEntry);
|
|
}
|
|
}
|
|
|
|
static const PLHashAllocOps serialNumberHashAllocOps = {
|
|
DefaultAllocTable, DefaultFreeTable,
|
|
DefaultAllocEntry, SerialNumberFreeEntry
|
|
};
|
|
|
|
static const PLHashAllocOps typesToLogHashAllocOps = {
|
|
DefaultAllocTable, DefaultFreeTable,
|
|
DefaultAllocEntry, TypesToLogFreeEntry
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef MOZ_STACKWALKING
|
|
|
|
class CodeAddressServiceStringTable final
|
|
{
|
|
public:
|
|
CodeAddressServiceStringTable() : mSet(32) {}
|
|
|
|
const char* Intern(const char* aString)
|
|
{
|
|
nsCharPtrHashKey* e = mSet.PutEntry(aString);
|
|
return e->GetKey();
|
|
}
|
|
|
|
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
|
{
|
|
return mSet.SizeOfExcludingThis(aMallocSizeOf);
|
|
}
|
|
|
|
private:
|
|
typedef nsTHashtable<nsCharPtrHashKey> StringSet;
|
|
StringSet mSet;
|
|
};
|
|
|
|
struct CodeAddressServiceStringAlloc final
|
|
{
|
|
static char* copy(const char* aStr) { return strdup(aStr); }
|
|
static void free(char* aPtr) { ::free(aPtr); }
|
|
};
|
|
|
|
// WalkTheStack does not hold any locks needed by MozDescribeCodeAddress, so
|
|
// this class does not need to do anything.
|
|
struct CodeAddressServiceLock final
|
|
{
|
|
static void Unlock() {}
|
|
static void Lock() {}
|
|
static bool IsLocked() { return true; }
|
|
};
|
|
|
|
typedef mozilla::CodeAddressService<CodeAddressServiceStringTable,
|
|
CodeAddressServiceStringAlloc,
|
|
CodeAddressServiceLock> WalkTheStackCodeAddressService;
|
|
|
|
mozilla::StaticAutoPtr<WalkTheStackCodeAddressService> gCodeAddressService;
|
|
|
|
#endif // MOZ_STACKWALKING
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class BloatEntry
|
|
{
|
|
public:
|
|
BloatEntry(const char* aClassName, uint32_t aClassSize)
|
|
: mClassSize(aClassSize)
|
|
{
|
|
mClassName = PL_strdup(aClassName);
|
|
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* aStats)
|
|
{
|
|
aStats->mCreates = 0;
|
|
aStats->mDestroys = 0;
|
|
}
|
|
|
|
void Accumulate()
|
|
{
|
|
mAllStats.mCreates += mNewStats.mCreates;
|
|
mAllStats.mDestroys += mNewStats.mDestroys;
|
|
Clear(&mNewStats);
|
|
}
|
|
|
|
void Ctor()
|
|
{
|
|
mNewStats.mCreates++;
|
|
}
|
|
|
|
void Dtor()
|
|
{
|
|
mNewStats.mDestroys++;
|
|
}
|
|
|
|
static int DumpEntry(PLHashEntry* aHashEntry, int aIndex, void* aArg)
|
|
{
|
|
BloatEntry* entry = (BloatEntry*)aHashEntry->value;
|
|
if (entry) {
|
|
entry->Accumulate();
|
|
static_cast<nsTArray<BloatEntry*>*>(aArg)->AppendElement(entry);
|
|
}
|
|
return HT_ENUMERATE_NEXT;
|
|
}
|
|
|
|
static int TotalEntries(PLHashEntry* aHashEntry, int aIndex, void* aArg)
|
|
{
|
|
BloatEntry* entry = (BloatEntry*)aHashEntry->value;
|
|
if (entry && nsCRT::strcmp(entry->mClassName, "TOTAL") != 0) {
|
|
entry->Total((BloatEntry*)aArg);
|
|
}
|
|
return HT_ENUMERATE_NEXT;
|
|
}
|
|
|
|
void Total(BloatEntry* aTotal)
|
|
{
|
|
aTotal->mAllStats.mCreates += mNewStats.mCreates + mAllStats.mCreates;
|
|
aTotal->mAllStats.mDestroys += mNewStats.mDestroys + mAllStats.mDestroys;
|
|
uint64_t count = (mNewStats.mCreates + mAllStats.mCreates);
|
|
aTotal->mClassSize += mClassSize * count; // adjust for average in DumpTotal
|
|
aTotal->mTotalLeaked += (uint64_t)(mClassSize *
|
|
((mNewStats.mCreates + mAllStats.mCreates)
|
|
- (mNewStats.mDestroys + mAllStats.mDestroys)));
|
|
}
|
|
|
|
void DumpTotal(FILE* aOut)
|
|
{
|
|
mClassSize /= mAllStats.mCreates;
|
|
Dump(-1, aOut, nsTraceRefcnt::ALL_STATS);
|
|
}
|
|
|
|
static bool HaveLeaks(nsTraceRefcntStats* aStats)
|
|
{
|
|
return aStats->mCreates != aStats->mDestroys;
|
|
}
|
|
|
|
bool PrintDumpHeader(FILE* aOut, const char* aMsg,
|
|
nsTraceRefcnt::StatisticsType aType)
|
|
{
|
|
fprintf(aOut, "\n== BloatView: %s, %s process %d\n", aMsg,
|
|
XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid());
|
|
nsTraceRefcntStats& stats =
|
|
(aType == nsTraceRefcnt::NEW_STATS) ? mNewStats : mAllStats;
|
|
if (gLogLeaksOnly && !HaveLeaks(&stats)) {
|
|
return false;
|
|
}
|
|
|
|
fprintf(aOut,
|
|
"\n" \
|
|
" |<----------------Class--------------->|<-----Bytes------>|<----Objects---->|\n" \
|
|
" | | Per-Inst Leaked| Total Rem|\n");
|
|
|
|
this->DumpTotal(aOut);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Dump(int aIndex, FILE* aOut, nsTraceRefcnt::StatisticsType aType)
|
|
{
|
|
nsTraceRefcntStats* stats =
|
|
(aType == nsTraceRefcnt::NEW_STATS) ? &mNewStats : &mAllStats;
|
|
if (gLogLeaksOnly && !HaveLeaks(stats)) {
|
|
return;
|
|
}
|
|
|
|
if ((stats->mCreates - stats->mDestroys) != 0 ||
|
|
stats->mCreates != 0) {
|
|
fprintf(aOut, "%4d |%-38.38s| %8d %8" PRIu64 "|%8" PRIu64 " %8" PRIu64"|\n",
|
|
aIndex + 1, mClassName,
|
|
(int32_t)mClassSize,
|
|
(nsCRT::strcmp(mClassName, "TOTAL"))
|
|
? (uint64_t)((stats->mCreates - stats->mDestroys) * mClassSize)
|
|
: mTotalLeaked,
|
|
stats->mCreates,
|
|
(stats->mCreates - stats->mDestroys));
|
|
}
|
|
}
|
|
|
|
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* aPool, PLHashEntry* aHashEntry, unsigned aFlag)
|
|
{
|
|
if (aFlag == HT_FREE_ENTRY) {
|
|
BloatEntry* entry = reinterpret_cast<BloatEntry*>(aHashEntry->value);
|
|
delete entry;
|
|
PR_Free(aHashEntry);
|
|
}
|
|
}
|
|
|
|
const static PLHashAllocOps bloatViewHashAllocOps = {
|
|
DefaultAllocTable, DefaultFreeTable,
|
|
DefaultAllocEntry, BloatViewFreeEntry
|
|
};
|
|
|
|
static void
|
|
RecreateBloatView()
|
|
{
|
|
gBloatView = PL_NewHashTable(256,
|
|
PL_HashString,
|
|
PL_CompareStrings,
|
|
PL_CompareValues,
|
|
&bloatViewHashAllocOps, nullptr);
|
|
}
|
|
|
|
static BloatEntry*
|
|
GetBloatEntry(const char* aTypeName, uint32_t aInstanceSize)
|
|
{
|
|
if (!gBloatView) {
|
|
RecreateBloatView();
|
|
}
|
|
BloatEntry* entry = nullptr;
|
|
if (gBloatView) {
|
|
entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName);
|
|
if (!entry && aInstanceSize > 0) {
|
|
|
|
entry = new BloatEntry(aTypeName, aInstanceSize);
|
|
PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry);
|
|
if (!e) {
|
|
delete entry;
|
|
entry = nullptr;
|
|
}
|
|
} 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, "%" PRIdPTR
|
|
" @%p (%d references; %d from COMPtrs)\n",
|
|
record->serialNumber,
|
|
NS_INT32_TO_PTR(aHashEntry->key),
|
|
record->refCount,
|
|
record->COMPtrCount);
|
|
#else
|
|
fprintf((FILE*)aClosure, "%" PRIdPTR
|
|
" @%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& aEntry1, BloatEntry* const& aEntry2) const
|
|
{
|
|
return PL_strcmp(aEntry1->GetClassName(), aEntry2->GetClassName()) == 0;
|
|
}
|
|
bool LessThan(BloatEntry* const& aEntry1, BloatEntry* const& aEntry2) const
|
|
{
|
|
return PL_strcmp(aEntry1->GetClassName(), aEntry2->GetClassName()) < 0;
|
|
}
|
|
};
|
|
|
|
#endif /* NS_IMPL_REFCNT_LOGGING */
|
|
|
|
nsresult
|
|
nsTraceRefcnt::DumpStatistics(StatisticsType aType, FILE* aOut)
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
if (!gBloatLog || !gBloatView) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
if (!aOut) {
|
|
aOut = gBloatLog;
|
|
}
|
|
|
|
LOCK_TRACELOG();
|
|
|
|
LoggingType wasLogging = gLogging;
|
|
gLogging = NoLogging; // turn off logging for this method
|
|
|
|
BloatEntry total("TOTAL", 0);
|
|
PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
|
|
const char* msg;
|
|
if (aType == 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(aOut, msg, aType);
|
|
|
|
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, aOut, aType);
|
|
}
|
|
|
|
fprintf(aOut, "\n");
|
|
}
|
|
|
|
fprintf(aOut, "nsTraceRefcnt::DumpStatistics: %d entries\n", count);
|
|
|
|
if (gSerialNumbers) {
|
|
fprintf(aOut, "\nSerial Numbers of Leaked Objects:\n");
|
|
PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, aOut);
|
|
}
|
|
|
|
gLogging = wasLogging;
|
|
UNLOCK_TRACELOG();
|
|
#endif
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsTraceRefcnt::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 he != nullptr;
|
|
}
|
|
|
|
static PLHashNumber
|
|
HashNumber(const void* aKey)
|
|
{
|
|
return PLHashNumber(NS_PTR_TO_INT32(aKey));
|
|
}
|
|
|
|
static intptr_t
|
|
GetSerialNumber(void* aPtr, bool aCreate)
|
|
{
|
|
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers,
|
|
HashNumber(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, HashNumber(aPtr),
|
|
aPtr, reinterpret_cast<void*>(record));
|
|
return gNextSerialNumber;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int32_t*
|
|
GetRefCount(void* aPtr)
|
|
{
|
|
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers,
|
|
HashNumber(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,
|
|
HashNumber(aPtr),
|
|
aPtr);
|
|
if (hep && *hep) {
|
|
return &((reinterpret_cast<serialNumberRecord*>((*hep)->value))->COMPtrCount);
|
|
}
|
|
return nullptr;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
RecycleSerialNumberPtr(void* aPtr)
|
|
{
|
|
PL_HashTableRemove(gSerialNumbers, aPtr);
|
|
}
|
|
|
|
static bool
|
|
LogThisObj(intptr_t aSerialNumber)
|
|
{
|
|
return (bool)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* aEnvVar, const char* aMsg, FILE** aResult)
|
|
{
|
|
const char* value = getenv(aEnvVar);
|
|
if (value) {
|
|
if (nsCRT::strcmp(value, "1") == 0) {
|
|
*aResult = stdout;
|
|
fprintf(stdout, "### %s defined -- logging %s to stdout\n",
|
|
aEnvVar, aMsg);
|
|
return true;
|
|
} else if (nsCRT::strcmp(value, "2") == 0) {
|
|
*aResult = stderr;
|
|
fprintf(stdout, "### %s defined -- logging %s to stderr\n",
|
|
aEnvVar, aMsg);
|
|
return true;
|
|
} else {
|
|
FILE* stream;
|
|
nsAutoCString fname(value);
|
|
if (!XRE_IsParentProcess()) {
|
|
bool hasLogExtension =
|
|
fname.RFind(".log", true, -1, 4) == kNotFound ? false : true;
|
|
if (hasLogExtension) {
|
|
fname.Cut(fname.Length() - 4, 4);
|
|
}
|
|
fname.Append('_');
|
|
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) {
|
|
MozillaRegisterDebugFD(fileno(stream));
|
|
*aResult = stream;
|
|
fprintf(stdout, "### %s defined -- logging %s to %s\n",
|
|
aEnvVar, aMsg, fname.get());
|
|
} else {
|
|
fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
|
|
aEnvVar, aMsg, fname.get());
|
|
}
|
|
return stream != nullptr;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static void
|
|
maybeUnregisterAndCloseFile(FILE*& aFile)
|
|
{
|
|
if (!aFile) {
|
|
return;
|
|
}
|
|
|
|
MozillaUnRegisterDebugFILE(aFile);
|
|
fclose(aFile);
|
|
aFile = nullptr;
|
|
}
|
|
|
|
|
|
static void
|
|
InitTraceLog()
|
|
{
|
|
if (gInitialized) {
|
|
return;
|
|
}
|
|
gInitialized = true;
|
|
|
|
bool 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");
|
|
maybeUnregisterAndCloseFile(gBloatLog);
|
|
gLogLeaksOnly = false;
|
|
}
|
|
}
|
|
|
|
InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
|
|
|
|
InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
|
|
|
|
const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
|
|
|
|
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
|
|
if (classes) {
|
|
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, nullptr);
|
|
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, 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, nullptr);
|
|
|
|
|
|
}
|
|
|
|
const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
|
|
if (objects) {
|
|
gObjectsToLog = PL_NewHashTable(256,
|
|
HashNumber,
|
|
PL_CompareValues,
|
|
PL_CompareValues,
|
|
nullptr, nullptr);
|
|
|
|
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, "%" PRIdPTR " ", serialno);
|
|
}
|
|
if (!cm) {
|
|
break;
|
|
}
|
|
*cm = ',';
|
|
cp = cm + 1;
|
|
}
|
|
fprintf(stdout, "\n");
|
|
}
|
|
}
|
|
|
|
|
|
if (gBloatLog) {
|
|
gLogging = OnlyBloatLogging;
|
|
}
|
|
|
|
if (gRefcntsLog || gAllocLog || gCOMPtrLog) {
|
|
gLogging = FullLogging;
|
|
}
|
|
|
|
gTraceLock = PR_NewLock();
|
|
}
|
|
|
|
#endif
|
|
|
|
extern "C" {
|
|
|
|
#ifdef MOZ_STACKWALKING
|
|
static void
|
|
PrintStackFrame(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
|
|
{
|
|
FILE* stream = (FILE*)aClosure;
|
|
MozCodeAddressDetails details;
|
|
char buf[1024];
|
|
|
|
MozDescribeCodeAddress(aPC, &details);
|
|
MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details);
|
|
fprintf(stream, "%s\n", buf);
|
|
fflush(stream);
|
|
}
|
|
|
|
static void
|
|
PrintStackFrameCached(uint32_t aFrameNumber, void* aPC, void* aSP,
|
|
void* aClosure)
|
|
{
|
|
auto stream = static_cast<FILE*>(aClosure);
|
|
static const size_t buflen = 1024;
|
|
char buf[buflen];
|
|
gCodeAddressService->GetLocation(aFrameNumber, aPC, buf, buflen);
|
|
fprintf(stream, " %s\n", buf);
|
|
fflush(stream);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
void
|
|
nsTraceRefcnt::WalkTheStack(FILE* aStream)
|
|
{
|
|
#ifdef MOZ_STACKWALKING
|
|
MozStackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, aStream,
|
|
0, nullptr);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
nsTraceRefcnt::WalkTheStackCached(FILE* aStream)
|
|
{
|
|
#ifdef MOZ_STACKWALKING
|
|
if (!gCodeAddressService) {
|
|
gCodeAddressService = new WalkTheStackCodeAddressService();
|
|
}
|
|
MozStackWalk(PrintStackFrameCached, /* skipFrames */ 2, /* maxFrames */ 0,
|
|
aStream, 0, nullptr);
|
|
#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
|
|
|
|
void
|
|
nsTraceRefcnt::DemangleSymbol(const char* aSymbol,
|
|
char* aBuffer,
|
|
int aBufLen)
|
|
{
|
|
NS_ASSERTION(aSymbol, "null symbol");
|
|
NS_ASSERTION(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()
|
|
{
|
|
NS_SetMainThread();
|
|
|
|
// FIXME: This is called multiple times, we should probably not allow that.
|
|
#ifdef MOZ_STACKWALKING
|
|
StackWalkInitCriticalAddress();
|
|
#endif
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
if (++gInitCount) {
|
|
nsTraceRefcnt::SetActivityIsLegal(true);
|
|
}
|
|
#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) {
|
|
nsTraceRefcnt::DumpStatistics();
|
|
nsTraceRefcnt::ResetStatistics();
|
|
}
|
|
nsTraceRefcnt::Shutdown();
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
nsTraceRefcnt::SetActivityIsLegal(false);
|
|
gActivityTLS = BAD_TLS_INDEX;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
} // namespace mozilla
|
|
|
|
EXPORT_XPCOM_API(void)
|
|
NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt,
|
|
const char* aClass, uint32_t aClassSize)
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
ASSERT_ACTIVITY_IS_LEGAL;
|
|
if (!gInitialized) {
|
|
InitTraceLog();
|
|
}
|
|
if (gLogging == NoLogging) {
|
|
return;
|
|
}
|
|
if (aRefcnt == 1 || gLogging == FullLogging) {
|
|
LOCK_TRACELOG();
|
|
|
|
if (aRefcnt == 1 && gBloatLog) {
|
|
BloatEntry* entry = GetBloatEntry(aClass, aClassSize);
|
|
if (entry) {
|
|
entry->Ctor();
|
|
}
|
|
}
|
|
|
|
// Here's the case where MOZ_COUNT_CTOR was not used,
|
|
// yet we still want to see creation information:
|
|
|
|
bool loggingThisType = (!gTypesToLog || LogThisType(aClass));
|
|
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> %p %" PRIdPTR " Create\n", aClass, aPtr, serialno);
|
|
nsTraceRefcnt::WalkTheStackCached(gAllocLog);
|
|
}
|
|
|
|
if (gRefcntsLog && loggingThisType && loggingThisObject) {
|
|
// Can't use MOZ_LOG(), b/c it truncates the line
|
|
fprintf(gRefcntsLog, "\n<%s> %p %" PRIuPTR " AddRef %" PRIuPTR "\n",
|
|
aClass, aPtr, serialno, aRefcnt);
|
|
nsTraceRefcnt::WalkTheStackCached(gRefcntsLog);
|
|
fflush(gRefcntsLog);
|
|
}
|
|
UNLOCK_TRACELOG();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
EXPORT_XPCOM_API(void)
|
|
NS_LogRelease(void* aPtr, nsrefcnt aRefcnt, const char* aClass)
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
ASSERT_ACTIVITY_IS_LEGAL;
|
|
if (!gInitialized) {
|
|
InitTraceLog();
|
|
}
|
|
if (gLogging == NoLogging) {
|
|
return;
|
|
}
|
|
if (aRefcnt == 0 || gLogging == FullLogging) {
|
|
LOCK_TRACELOG();
|
|
|
|
if (aRefcnt == 0 && gBloatLog) {
|
|
BloatEntry* entry = GetBloatEntry(aClass, 0);
|
|
if (entry) {
|
|
entry->Dtor();
|
|
}
|
|
}
|
|
|
|
bool loggingThisType = (!gTypesToLog || LogThisType(aClass));
|
|
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) {
|
|
// Can't use MOZ_LOG(), b/c it truncates the line
|
|
fprintf(gRefcntsLog,
|
|
"\n<%s> %p %" PRIuPTR " Release %" PRIuPTR "\n",
|
|
aClass, aPtr, serialno, aRefcnt);
|
|
nsTraceRefcnt::WalkTheStackCached(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> %p %" PRIdPTR " Destroy\n", aClass, aPtr, serialno);
|
|
nsTraceRefcnt::WalkTheStackCached(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 != NoLogging) {
|
|
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> %p %" PRIdPTR " Ctor (%d)\n",
|
|
aType, aPtr, serialno, aInstanceSize);
|
|
nsTraceRefcnt::WalkTheStackCached(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 != NoLogging) {
|
|
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> %p %" PRIdPTR " Dtor (%d)\n",
|
|
aType, aPtr, serialno, aInstanceSize);
|
|
nsTraceRefcnt::WalkTheStackCached(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 == FullLogging) {
|
|
LOCK_TRACELOG();
|
|
|
|
int32_t* count = GetCOMPtrCount(object);
|
|
if (count) {
|
|
(*count)++;
|
|
}
|
|
|
|
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
|
|
|
if (gCOMPtrLog && loggingThisObject) {
|
|
fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrAddRef %d %p\n",
|
|
object, serialno, count ? (*count) : -1, aCOMPtr);
|
|
nsTraceRefcnt::WalkTheStackCached(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 == FullLogging) {
|
|
LOCK_TRACELOG();
|
|
|
|
int32_t* count = GetCOMPtrCount(object);
|
|
if (count) {
|
|
(*count)--;
|
|
}
|
|
|
|
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
|
|
|
if (gCOMPtrLog && loggingThisObject) {
|
|
fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrRelease %d %p\n",
|
|
object, serialno, count ? (*count) : -1, aCOMPtr);
|
|
nsTraceRefcnt::WalkTheStackCached(gCOMPtrLog);
|
|
}
|
|
|
|
UNLOCK_TRACELOG();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
nsTraceRefcnt::Shutdown()
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
#ifdef MOZ_STACKWALKING
|
|
gCodeAddressService = nullptr;
|
|
#endif
|
|
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(gCOMPtrLog);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
nsTraceRefcnt::SetActivityIsLegal(bool aLegal)
|
|
{
|
|
#ifdef NS_IMPL_REFCNT_LOGGING
|
|
if (gActivityTLS == BAD_TLS_INDEX) {
|
|
PR_NewThreadPrivateIndex(&gActivityTLS, nullptr);
|
|
}
|
|
|
|
PR_SetThreadPrivate(gActivityTLS, reinterpret_cast<void*>(!aLegal));
|
|
#endif
|
|
}
|