xpcom/base/nsTraceRefcnt.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsTraceRefcnt.h"
michael@0 7 #include "mozilla/IntegerPrintfMacros.h"
michael@0 8 #include "nsXPCOMPrivate.h"
michael@0 9 #include "nscore.h"
michael@0 10 #include "nsISupports.h"
michael@0 11 #include "nsTArray.h"
michael@0 12 #include "prenv.h"
michael@0 13 #include "plstr.h"
michael@0 14 #include "prlink.h"
michael@0 15 #include "nsCRT.h"
michael@0 16 #include <math.h>
michael@0 17 #include "nsStackWalkPrivate.h"
michael@0 18 #include "nsStackWalk.h"
michael@0 19 #include "nsString.h"
michael@0 20
michael@0 21 #include "nsXULAppAPI.h"
michael@0 22 #ifdef XP_WIN
michael@0 23 #include <process.h>
michael@0 24 #define getpid _getpid
michael@0 25 #else
michael@0 26 #include <unistd.h>
michael@0 27 #endif
michael@0 28
michael@0 29 #ifdef NS_TRACE_MALLOC
michael@0 30 #include "nsTraceMalloc.h"
michael@0 31 #endif
michael@0 32
michael@0 33 #include "mozilla/BlockingResourceBase.h"
michael@0 34 #include "mozilla/PoisonIOInterposer.h"
michael@0 35
michael@0 36 #ifdef HAVE_DLOPEN
michael@0 37 #include <dlfcn.h>
michael@0 38 #endif
michael@0 39
michael@0 40 ////////////////////////////////////////////////////////////////////////////////
michael@0 41
michael@0 42 void
michael@0 43 NS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
michael@0 44 double *meanResult, double *stdDevResult)
michael@0 45 {
michael@0 46 double mean = 0.0, var = 0.0, stdDev = 0.0;
michael@0 47 if (n > 0.0 && sumOfValues >= 0) {
michael@0 48 mean = sumOfValues / n;
michael@0 49 double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
michael@0 50 if (temp < 0.0 || n <= 1)
michael@0 51 var = 0.0;
michael@0 52 else
michael@0 53 var = temp / (n * (n - 1));
michael@0 54 // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
michael@0 55 stdDev = var != 0.0 ? sqrt(var) : 0.0;
michael@0 56 }
michael@0 57 *meanResult = mean;
michael@0 58 *stdDevResult = stdDev;
michael@0 59 }
michael@0 60
michael@0 61 ////////////////////////////////////////////////////////////////////////////////
michael@0 62
michael@0 63 #if !defined(XP_WIN) || (!defined(MOZ_OPTIMIZE) || defined(MOZ_PROFILING) || defined(DEBUG))
michael@0 64 #define STACKWALKING_AVAILABLE
michael@0 65 #endif
michael@0 66
michael@0 67 #define NS_IMPL_REFCNT_LOGGING
michael@0 68
michael@0 69 #ifdef NS_IMPL_REFCNT_LOGGING
michael@0 70 #include "plhash.h"
michael@0 71 #include "prmem.h"
michael@0 72
michael@0 73 #include "prlock.h"
michael@0 74
michael@0 75 // TraceRefcnt has to use bare PRLock instead of mozilla::Mutex
michael@0 76 // because TraceRefcnt can be used very early in startup.
michael@0 77 static PRLock* gTraceLock;
michael@0 78
michael@0 79 #define LOCK_TRACELOG() PR_Lock(gTraceLock)
michael@0 80 #define UNLOCK_TRACELOG() PR_Unlock(gTraceLock)
michael@0 81
michael@0 82 static PLHashTable* gBloatView;
michael@0 83 static PLHashTable* gTypesToLog;
michael@0 84 static PLHashTable* gObjectsToLog;
michael@0 85 static PLHashTable* gSerialNumbers;
michael@0 86 static intptr_t gNextSerialNumber;
michael@0 87
michael@0 88 static bool gLogging;
michael@0 89 static bool gLogToLeaky;
michael@0 90 static bool gLogLeaksOnly;
michael@0 91
michael@0 92 static void (*leakyLogAddRef)(void* p, int oldrc, int newrc);
michael@0 93 static void (*leakyLogRelease)(void* p, int oldrc, int newrc);
michael@0 94
michael@0 95 #define BAD_TLS_INDEX ((unsigned) -1)
michael@0 96
michael@0 97 // if gActivityTLS == BAD_TLS_INDEX, then we're
michael@0 98 // unitialized... otherwise this points to a NSPR TLS thread index
michael@0 99 // indicating whether addref activity is legal. If the PTR_TO_INT32 is 0 then
michael@0 100 // activity is ok, otherwise not!
michael@0 101 static unsigned gActivityTLS = BAD_TLS_INDEX;
michael@0 102
michael@0 103 static bool gInitialized;
michael@0 104 static nsrefcnt gInitCount;
michael@0 105
michael@0 106 static FILE *gBloatLog = nullptr;
michael@0 107 static FILE *gRefcntsLog = nullptr;
michael@0 108 static FILE *gAllocLog = nullptr;
michael@0 109 static FILE *gLeakyLog = nullptr;
michael@0 110 static FILE *gCOMPtrLog = nullptr;
michael@0 111
michael@0 112 struct serialNumberRecord {
michael@0 113 intptr_t serialNumber;
michael@0 114 int32_t refCount;
michael@0 115 int32_t COMPtrCount;
michael@0 116 };
michael@0 117
michael@0 118 struct nsTraceRefcntStats {
michael@0 119 uint64_t mAddRefs;
michael@0 120 uint64_t mReleases;
michael@0 121 uint64_t mCreates;
michael@0 122 uint64_t mDestroys;
michael@0 123 double mRefsOutstandingTotal;
michael@0 124 double mRefsOutstandingSquared;
michael@0 125 double mObjsOutstandingTotal;
michael@0 126 double mObjsOutstandingSquared;
michael@0 127 };
michael@0 128
michael@0 129 // I hope to turn this on for everybody once we hit it a little less.
michael@0 130 #ifdef DEBUG
michael@0 131 static const char kStaticCtorDtorWarning[] =
michael@0 132 "XPCOM objects created/destroyed from static ctor/dtor";
michael@0 133
michael@0 134 static void
michael@0 135 AssertActivityIsLegal()
michael@0 136 {
michael@0 137 if (gActivityTLS == BAD_TLS_INDEX ||
michael@0 138 NS_PTR_TO_INT32(PR_GetThreadPrivate(gActivityTLS)) != 0) {
michael@0 139 if (PR_GetEnv("MOZ_FATAL_STATIC_XPCOM_CTORS_DTORS")) {
michael@0 140 NS_RUNTIMEABORT(kStaticCtorDtorWarning);
michael@0 141 } else {
michael@0 142 NS_WARNING(kStaticCtorDtorWarning);
michael@0 143 }
michael@0 144 }
michael@0 145 }
michael@0 146 # define ASSERT_ACTIVITY_IS_LEGAL \
michael@0 147 PR_BEGIN_MACRO \
michael@0 148 AssertActivityIsLegal(); \
michael@0 149 PR_END_MACRO
michael@0 150 #else
michael@0 151 # define ASSERT_ACTIVITY_IS_LEGAL PR_BEGIN_MACRO PR_END_MACRO
michael@0 152 #endif // DEBUG
michael@0 153
michael@0 154 // These functions are copied from nsprpub/lib/ds/plhash.c, with changes
michael@0 155 // to the functions not called Default* to free the serialNumberRecord or
michael@0 156 // the BloatEntry.
michael@0 157
michael@0 158 static void *
michael@0 159 DefaultAllocTable(void *pool, size_t size)
michael@0 160 {
michael@0 161 return PR_MALLOC(size);
michael@0 162 }
michael@0 163
michael@0 164 static void
michael@0 165 DefaultFreeTable(void *pool, void *item)
michael@0 166 {
michael@0 167 PR_Free(item);
michael@0 168 }
michael@0 169
michael@0 170 static PLHashEntry *
michael@0 171 DefaultAllocEntry(void *pool, const void *key)
michael@0 172 {
michael@0 173 return PR_NEW(PLHashEntry);
michael@0 174 }
michael@0 175
michael@0 176 static void
michael@0 177 SerialNumberFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
michael@0 178 {
michael@0 179 if (flag == HT_FREE_ENTRY) {
michael@0 180 PR_Free(reinterpret_cast<serialNumberRecord*>(he->value));
michael@0 181 PR_Free(he);
michael@0 182 }
michael@0 183 }
michael@0 184
michael@0 185 static void
michael@0 186 TypesToLogFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
michael@0 187 {
michael@0 188 if (flag == HT_FREE_ENTRY) {
michael@0 189 free(const_cast<char*>(reinterpret_cast<const char*>(he->key)));
michael@0 190 PR_Free(he);
michael@0 191 }
michael@0 192 }
michael@0 193
michael@0 194 static const PLHashAllocOps serialNumberHashAllocOps = {
michael@0 195 DefaultAllocTable, DefaultFreeTable,
michael@0 196 DefaultAllocEntry, SerialNumberFreeEntry
michael@0 197 };
michael@0 198
michael@0 199 static const PLHashAllocOps typesToLogHashAllocOps = {
michael@0 200 DefaultAllocTable, DefaultFreeTable,
michael@0 201 DefaultAllocEntry, TypesToLogFreeEntry
michael@0 202 };
michael@0 203
michael@0 204 ////////////////////////////////////////////////////////////////////////////////
michael@0 205
michael@0 206 class BloatEntry {
michael@0 207 public:
michael@0 208 BloatEntry(const char* className, uint32_t classSize)
michael@0 209 : mClassSize(classSize) {
michael@0 210 mClassName = PL_strdup(className);
michael@0 211 Clear(&mNewStats);
michael@0 212 Clear(&mAllStats);
michael@0 213 mTotalLeaked = 0;
michael@0 214 }
michael@0 215
michael@0 216 ~BloatEntry() {
michael@0 217 PL_strfree(mClassName);
michael@0 218 }
michael@0 219
michael@0 220 uint32_t GetClassSize() { return (uint32_t)mClassSize; }
michael@0 221 const char* GetClassName() { return mClassName; }
michael@0 222
michael@0 223 static void Clear(nsTraceRefcntStats* stats) {
michael@0 224 stats->mAddRefs = 0;
michael@0 225 stats->mReleases = 0;
michael@0 226 stats->mCreates = 0;
michael@0 227 stats->mDestroys = 0;
michael@0 228 stats->mRefsOutstandingTotal = 0;
michael@0 229 stats->mRefsOutstandingSquared = 0;
michael@0 230 stats->mObjsOutstandingTotal = 0;
michael@0 231 stats->mObjsOutstandingSquared = 0;
michael@0 232 }
michael@0 233
michael@0 234 void Accumulate() {
michael@0 235 mAllStats.mAddRefs += mNewStats.mAddRefs;
michael@0 236 mAllStats.mReleases += mNewStats.mReleases;
michael@0 237 mAllStats.mCreates += mNewStats.mCreates;
michael@0 238 mAllStats.mDestroys += mNewStats.mDestroys;
michael@0 239 mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal;
michael@0 240 mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared;
michael@0 241 mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal;
michael@0 242 mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared;
michael@0 243 Clear(&mNewStats);
michael@0 244 }
michael@0 245
michael@0 246 void AddRef(nsrefcnt refcnt) {
michael@0 247 mNewStats.mAddRefs++;
michael@0 248 if (refcnt == 1) {
michael@0 249 Ctor();
michael@0 250 }
michael@0 251 AccountRefs();
michael@0 252 }
michael@0 253
michael@0 254 void Release(nsrefcnt refcnt) {
michael@0 255 mNewStats.mReleases++;
michael@0 256 if (refcnt == 0) {
michael@0 257 Dtor();
michael@0 258 }
michael@0 259 AccountRefs();
michael@0 260 }
michael@0 261
michael@0 262 void Ctor() {
michael@0 263 mNewStats.mCreates++;
michael@0 264 AccountObjs();
michael@0 265 }
michael@0 266
michael@0 267 void Dtor() {
michael@0 268 mNewStats.mDestroys++;
michael@0 269 AccountObjs();
michael@0 270 }
michael@0 271
michael@0 272 void AccountRefs() {
michael@0 273 uint64_t cnt = (mNewStats.mAddRefs - mNewStats.mReleases);
michael@0 274 mNewStats.mRefsOutstandingTotal += cnt;
michael@0 275 mNewStats.mRefsOutstandingSquared += cnt * cnt;
michael@0 276 }
michael@0 277
michael@0 278 void AccountObjs() {
michael@0 279 uint64_t cnt = (mNewStats.mCreates - mNewStats.mDestroys);
michael@0 280 mNewStats.mObjsOutstandingTotal += cnt;
michael@0 281 mNewStats.mObjsOutstandingSquared += cnt * cnt;
michael@0 282 }
michael@0 283
michael@0 284 static int DumpEntry(PLHashEntry *he, int i, void *arg) {
michael@0 285 BloatEntry* entry = (BloatEntry*)he->value;
michael@0 286 if (entry) {
michael@0 287 entry->Accumulate();
michael@0 288 static_cast<nsTArray<BloatEntry*>*>(arg)->AppendElement(entry);
michael@0 289 }
michael@0 290 return HT_ENUMERATE_NEXT;
michael@0 291 }
michael@0 292
michael@0 293 static int TotalEntries(PLHashEntry *he, int i, void *arg) {
michael@0 294 BloatEntry* entry = (BloatEntry*)he->value;
michael@0 295 if (entry && nsCRT::strcmp(entry->mClassName, "TOTAL") != 0) {
michael@0 296 entry->Total((BloatEntry*)arg);
michael@0 297 }
michael@0 298 return HT_ENUMERATE_NEXT;
michael@0 299 }
michael@0 300
michael@0 301 void Total(BloatEntry* total) {
michael@0 302 total->mAllStats.mAddRefs += mNewStats.mAddRefs + mAllStats.mAddRefs;
michael@0 303 total->mAllStats.mReleases += mNewStats.mReleases + mAllStats.mReleases;
michael@0 304 total->mAllStats.mCreates += mNewStats.mCreates + mAllStats.mCreates;
michael@0 305 total->mAllStats.mDestroys += mNewStats.mDestroys + mAllStats.mDestroys;
michael@0 306 total->mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal + mAllStats.mRefsOutstandingTotal;
michael@0 307 total->mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared + mAllStats.mRefsOutstandingSquared;
michael@0 308 total->mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal + mAllStats.mObjsOutstandingTotal;
michael@0 309 total->mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared + mAllStats.mObjsOutstandingSquared;
michael@0 310 uint64_t count = (mNewStats.mCreates + mAllStats.mCreates);
michael@0 311 total->mClassSize += mClassSize * count; // adjust for average in DumpTotal
michael@0 312 total->mTotalLeaked += (uint64_t)(mClassSize *
michael@0 313 ((mNewStats.mCreates + mAllStats.mCreates)
michael@0 314 -(mNewStats.mDestroys + mAllStats.mDestroys)));
michael@0 315 }
michael@0 316
michael@0 317 void DumpTotal(FILE* out) {
michael@0 318 mClassSize /= mAllStats.mCreates;
michael@0 319 Dump(-1, out, nsTraceRefcnt::ALL_STATS);
michael@0 320 }
michael@0 321
michael@0 322 static bool HaveLeaks(nsTraceRefcntStats* stats) {
michael@0 323 return ((stats->mAddRefs != stats->mReleases) ||
michael@0 324 (stats->mCreates != stats->mDestroys));
michael@0 325 }
michael@0 326
michael@0 327 bool PrintDumpHeader(FILE* out, const char* msg, nsTraceRefcnt::StatisticsType type) {
michael@0 328 fprintf(out, "\n== BloatView: %s, %s process %d\n", msg,
michael@0 329 XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid());
michael@0 330 nsTraceRefcntStats& stats =
michael@0 331 (type == nsTraceRefcnt::NEW_STATS) ? mNewStats : mAllStats;
michael@0 332 if (gLogLeaksOnly && !HaveLeaks(&stats))
michael@0 333 return false;
michael@0 334
michael@0 335 fprintf(out,
michael@0 336 "\n" \
michael@0 337 " |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n" \
michael@0 338 " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
michael@0 339
michael@0 340 this->DumpTotal(out);
michael@0 341
michael@0 342 return true;
michael@0 343 }
michael@0 344
michael@0 345 void Dump(int i, FILE* out, nsTraceRefcnt::StatisticsType type) {
michael@0 346 nsTraceRefcntStats* stats = (type == nsTraceRefcnt::NEW_STATS) ? &mNewStats : &mAllStats;
michael@0 347 if (gLogLeaksOnly && !HaveLeaks(stats)) {
michael@0 348 return;
michael@0 349 }
michael@0 350
michael@0 351 double meanRefs, stddevRefs;
michael@0 352 NS_MeanAndStdDev(stats->mAddRefs + stats->mReleases,
michael@0 353 stats->mRefsOutstandingTotal,
michael@0 354 stats->mRefsOutstandingSquared,
michael@0 355 &meanRefs, &stddevRefs);
michael@0 356
michael@0 357 double meanObjs, stddevObjs;
michael@0 358 NS_MeanAndStdDev(stats->mCreates + stats->mDestroys,
michael@0 359 stats->mObjsOutstandingTotal,
michael@0 360 stats->mObjsOutstandingSquared,
michael@0 361 &meanObjs, &stddevObjs);
michael@0 362
michael@0 363 if ((stats->mAddRefs - stats->mReleases) != 0 ||
michael@0 364 stats->mAddRefs != 0 ||
michael@0 365 meanRefs != 0 ||
michael@0 366 stddevRefs != 0 ||
michael@0 367 (stats->mCreates - stats->mDestroys) != 0 ||
michael@0 368 stats->mCreates != 0 ||
michael@0 369 meanObjs != 0 ||
michael@0 370 stddevObjs != 0) {
michael@0 371 fprintf(out, "%4d %-40.40s %8d %8" PRIu64 " %8" PRIu64 " %8" PRIu64 " (%8.2f +/- %8.2f) %8" PRIu64 " %8" PRIu64 " (%8.2f +/- %8.2f)\n",
michael@0 372 i+1, mClassName,
michael@0 373 (int32_t)mClassSize,
michael@0 374 (nsCRT::strcmp(mClassName, "TOTAL"))
michael@0 375 ?(uint64_t)((stats->mCreates - stats->mDestroys) * mClassSize)
michael@0 376 :mTotalLeaked,
michael@0 377 stats->mCreates,
michael@0 378 (stats->mCreates - stats->mDestroys),
michael@0 379 meanObjs,
michael@0 380 stddevObjs,
michael@0 381 stats->mAddRefs,
michael@0 382 (stats->mAddRefs - stats->mReleases),
michael@0 383 meanRefs,
michael@0 384 stddevRefs);
michael@0 385 }
michael@0 386 }
michael@0 387
michael@0 388 protected:
michael@0 389 char* mClassName;
michael@0 390 double mClassSize; // this is stored as a double because of the way we compute the avg class size for total bloat
michael@0 391 uint64_t mTotalLeaked; // used only for TOTAL entry
michael@0 392 nsTraceRefcntStats mNewStats;
michael@0 393 nsTraceRefcntStats mAllStats;
michael@0 394 };
michael@0 395
michael@0 396 static void
michael@0 397 BloatViewFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
michael@0 398 {
michael@0 399 if (flag == HT_FREE_ENTRY) {
michael@0 400 BloatEntry* entry = reinterpret_cast<BloatEntry*>(he->value);
michael@0 401 delete entry;
michael@0 402 PR_Free(he);
michael@0 403 }
michael@0 404 }
michael@0 405
michael@0 406 const static PLHashAllocOps bloatViewHashAllocOps = {
michael@0 407 DefaultAllocTable, DefaultFreeTable,
michael@0 408 DefaultAllocEntry, BloatViewFreeEntry
michael@0 409 };
michael@0 410
michael@0 411 static void
michael@0 412 RecreateBloatView()
michael@0 413 {
michael@0 414 gBloatView = PL_NewHashTable(256,
michael@0 415 PL_HashString,
michael@0 416 PL_CompareStrings,
michael@0 417 PL_CompareValues,
michael@0 418 &bloatViewHashAllocOps, nullptr);
michael@0 419 }
michael@0 420
michael@0 421 static BloatEntry*
michael@0 422 GetBloatEntry(const char* aTypeName, uint32_t aInstanceSize)
michael@0 423 {
michael@0 424 if (!gBloatView) {
michael@0 425 RecreateBloatView();
michael@0 426 }
michael@0 427 BloatEntry* entry = nullptr;
michael@0 428 if (gBloatView) {
michael@0 429 entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName);
michael@0 430 if (entry == nullptr && aInstanceSize > 0) {
michael@0 431
michael@0 432 entry = new BloatEntry(aTypeName, aInstanceSize);
michael@0 433 PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry);
michael@0 434 if (e == nullptr) {
michael@0 435 delete entry;
michael@0 436 entry = nullptr;
michael@0 437 }
michael@0 438 } else {
michael@0 439 NS_ASSERTION(aInstanceSize == 0 ||
michael@0 440 entry->GetClassSize() == aInstanceSize,
michael@0 441 "bad size recorded");
michael@0 442 }
michael@0 443 }
michael@0 444 return entry;
michael@0 445 }
michael@0 446
michael@0 447 static int DumpSerialNumbers(PLHashEntry* aHashEntry, int aIndex, void* aClosure)
michael@0 448 {
michael@0 449 serialNumberRecord* record = reinterpret_cast<serialNumberRecord *>(aHashEntry->value);
michael@0 450 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
michael@0 451 fprintf((FILE*) aClosure, "%" PRIdPTR
michael@0 452 " @%p (%d references; %d from COMPtrs)\n",
michael@0 453 record->serialNumber,
michael@0 454 NS_INT32_TO_PTR(aHashEntry->key),
michael@0 455 record->refCount,
michael@0 456 record->COMPtrCount);
michael@0 457 #else
michael@0 458 fprintf((FILE*) aClosure, "%" PRIdPTR
michael@0 459 " @%p (%d references)\n",
michael@0 460 record->serialNumber,
michael@0 461 NS_INT32_TO_PTR(aHashEntry->key),
michael@0 462 record->refCount);
michael@0 463 #endif
michael@0 464 return HT_ENUMERATE_NEXT;
michael@0 465 }
michael@0 466
michael@0 467
michael@0 468 template <>
michael@0 469 class nsDefaultComparator <BloatEntry*, BloatEntry*> {
michael@0 470 public:
michael@0 471 bool Equals(BloatEntry* const& aA, BloatEntry* const& aB) const {
michael@0 472 return PL_strcmp(aA->GetClassName(), aB->GetClassName()) == 0;
michael@0 473 }
michael@0 474 bool LessThan(BloatEntry* const& aA, BloatEntry* const& aB) const {
michael@0 475 return PL_strcmp(aA->GetClassName(), aB->GetClassName()) < 0;
michael@0 476 }
michael@0 477 };
michael@0 478
michael@0 479 #endif /* NS_IMPL_REFCNT_LOGGING */
michael@0 480
michael@0 481 nsresult
michael@0 482 nsTraceRefcnt::DumpStatistics(StatisticsType type, FILE* out)
michael@0 483 {
michael@0 484 #ifdef NS_IMPL_REFCNT_LOGGING
michael@0 485 if (gBloatLog == nullptr || gBloatView == nullptr) {
michael@0 486 return NS_ERROR_FAILURE;
michael@0 487 }
michael@0 488 if (out == nullptr) {
michael@0 489 out = gBloatLog;
michael@0 490 }
michael@0 491
michael@0 492 LOCK_TRACELOG();
michael@0 493
michael@0 494 bool wasLogging = gLogging;
michael@0 495 gLogging = false; // turn off logging for this method
michael@0 496
michael@0 497 BloatEntry total("TOTAL", 0);
michael@0 498 PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
michael@0 499 const char* msg;
michael@0 500 if (type == NEW_STATS) {
michael@0 501 if (gLogLeaksOnly)
michael@0 502 msg = "NEW (incremental) LEAK STATISTICS";
michael@0 503 else
michael@0 504 msg = "NEW (incremental) LEAK AND BLOAT STATISTICS";
michael@0 505 }
michael@0 506 else {
michael@0 507 if (gLogLeaksOnly)
michael@0 508 msg = "ALL (cumulative) LEAK STATISTICS";
michael@0 509 else
michael@0 510 msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
michael@0 511 }
michael@0 512 const bool leaked = total.PrintDumpHeader(out, msg, type);
michael@0 513
michael@0 514 nsTArray<BloatEntry*> entries;
michael@0 515 PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries);
michael@0 516 const uint32_t count = entries.Length();
michael@0 517
michael@0 518 if (!gLogLeaksOnly || leaked) {
michael@0 519 // Sort the entries alphabetically by classname.
michael@0 520 entries.Sort();
michael@0 521
michael@0 522 for (uint32_t i = 0; i < count; ++i) {
michael@0 523 BloatEntry* entry = entries[i];
michael@0 524 entry->Dump(i, out, type);
michael@0 525 }
michael@0 526
michael@0 527 fprintf(out, "\n");
michael@0 528 }
michael@0 529
michael@0 530 fprintf(out, "nsTraceRefcnt::DumpStatistics: %d entries\n", count);
michael@0 531
michael@0 532 if (gSerialNumbers) {
michael@0 533 fprintf(out, "\nSerial Numbers of Leaked Objects:\n");
michael@0 534 PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, out);
michael@0 535 }
michael@0 536
michael@0 537 gLogging = wasLogging;
michael@0 538 UNLOCK_TRACELOG();
michael@0 539 #endif
michael@0 540
michael@0 541 return NS_OK;
michael@0 542 }
michael@0 543
michael@0 544 void
michael@0 545 nsTraceRefcnt::ResetStatistics()
michael@0 546 {
michael@0 547 #ifdef NS_IMPL_REFCNT_LOGGING
michael@0 548 LOCK_TRACELOG();
michael@0 549 if (gBloatView) {
michael@0 550 PL_HashTableDestroy(gBloatView);
michael@0 551 gBloatView = nullptr;
michael@0 552 }
michael@0 553 UNLOCK_TRACELOG();
michael@0 554 #endif
michael@0 555 }
michael@0 556
michael@0 557 #ifdef NS_IMPL_REFCNT_LOGGING
michael@0 558 static bool LogThisType(const char* aTypeName)
michael@0 559 {
michael@0 560 void* he = PL_HashTableLookup(gTypesToLog, aTypeName);
michael@0 561 return nullptr != he;
michael@0 562 }
michael@0 563
michael@0 564 static intptr_t GetSerialNumber(void* aPtr, bool aCreate)
michael@0 565 {
michael@0 566 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
michael@0 567 if (hep && *hep) {
michael@0 568 return reinterpret_cast<serialNumberRecord*>((*hep)->value)->serialNumber;
michael@0 569 }
michael@0 570 else if (aCreate) {
michael@0 571 serialNumberRecord *record = PR_NEW(serialNumberRecord);
michael@0 572 record->serialNumber = ++gNextSerialNumber;
michael@0 573 record->refCount = 0;
michael@0 574 record->COMPtrCount = 0;
michael@0 575 PL_HashTableRawAdd(gSerialNumbers, hep, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr, reinterpret_cast<void*>(record));
michael@0 576 return gNextSerialNumber;
michael@0 577 }
michael@0 578 else {
michael@0 579 return 0;
michael@0 580 }
michael@0 581 }
michael@0 582
michael@0 583 static int32_t* GetRefCount(void* aPtr)
michael@0 584 {
michael@0 585 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
michael@0 586 if (hep && *hep) {
michael@0 587 return &((reinterpret_cast<serialNumberRecord*>((*hep)->value))->refCount);
michael@0 588 } else {
michael@0 589 return nullptr;
michael@0 590 }
michael@0 591 }
michael@0 592
michael@0 593 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
michael@0 594 static int32_t* GetCOMPtrCount(void* aPtr)
michael@0 595 {
michael@0 596 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
michael@0 597 if (hep && *hep) {
michael@0 598 return &((reinterpret_cast<serialNumberRecord*>((*hep)->value))->COMPtrCount);
michael@0 599 } else {
michael@0 600 return nullptr;
michael@0 601 }
michael@0 602 }
michael@0 603 #endif
michael@0 604
michael@0 605 static void RecycleSerialNumberPtr(void* aPtr)
michael@0 606 {
michael@0 607 PL_HashTableRemove(gSerialNumbers, aPtr);
michael@0 608 }
michael@0 609
michael@0 610 static bool LogThisObj(intptr_t aSerialNumber)
michael@0 611 {
michael@0 612 return nullptr != PL_HashTableLookup(gObjectsToLog, (const void*)aSerialNumber);
michael@0 613 }
michael@0 614
michael@0 615 #ifdef XP_WIN
michael@0 616 #define FOPEN_NO_INHERIT "N"
michael@0 617 #else
michael@0 618 #define FOPEN_NO_INHERIT
michael@0 619 #endif
michael@0 620
michael@0 621 static bool InitLog(const char* envVar, const char* msg, FILE* *result)
michael@0 622 {
michael@0 623 const char* value = getenv(envVar);
michael@0 624 if (value) {
michael@0 625 if (nsCRT::strcmp(value, "1") == 0) {
michael@0 626 *result = stdout;
michael@0 627 fprintf(stdout, "### %s defined -- logging %s to stdout\n",
michael@0 628 envVar, msg);
michael@0 629 return true;
michael@0 630 }
michael@0 631 else if (nsCRT::strcmp(value, "2") == 0) {
michael@0 632 *result = stderr;
michael@0 633 fprintf(stdout, "### %s defined -- logging %s to stderr\n",
michael@0 634 envVar, msg);
michael@0 635 return true;
michael@0 636 }
michael@0 637 else {
michael@0 638 FILE *stream;
michael@0 639 nsAutoCString fname(value);
michael@0 640 if (XRE_GetProcessType() != GeckoProcessType_Default) {
michael@0 641 bool hasLogExtension =
michael@0 642 fname.RFind(".log", true, -1, 4) == kNotFound ? false : true;
michael@0 643 if (hasLogExtension)
michael@0 644 fname.Cut(fname.Length() - 4, 4);
michael@0 645 fname.AppendLiteral("_");
michael@0 646 fname.Append((char*)XRE_ChildProcessTypeToString(XRE_GetProcessType()));
michael@0 647 fname.AppendLiteral("_pid");
michael@0 648 fname.AppendInt((uint32_t)getpid());
michael@0 649 if (hasLogExtension)
michael@0 650 fname.AppendLiteral(".log");
michael@0 651 }
michael@0 652 stream = ::fopen(fname.get(), "w" FOPEN_NO_INHERIT);
michael@0 653 if (stream != nullptr) {
michael@0 654 MozillaRegisterDebugFD(fileno(stream));
michael@0 655 *result = stream;
michael@0 656 fprintf(stdout, "### %s defined -- logging %s to %s\n",
michael@0 657 envVar, msg, fname.get());
michael@0 658 }
michael@0 659 else {
michael@0 660 fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
michael@0 661 envVar, msg, fname.get());
michael@0 662 }
michael@0 663 return stream != nullptr;
michael@0 664 }
michael@0 665 }
michael@0 666 return false;
michael@0 667 }
michael@0 668
michael@0 669
michael@0 670 static PLHashNumber HashNumber(const void* aKey)
michael@0 671 {
michael@0 672 return PLHashNumber(NS_PTR_TO_INT32(aKey));
michael@0 673 }
michael@0 674
michael@0 675 static void InitTraceLog(void)
michael@0 676 {
michael@0 677 if (gInitialized) return;
michael@0 678 gInitialized = true;
michael@0 679
michael@0 680 bool defined;
michael@0 681 defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
michael@0 682 if (!defined)
michael@0 683 gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
michael@0 684 if (defined || gLogLeaksOnly) {
michael@0 685 RecreateBloatView();
michael@0 686 if (!gBloatView) {
michael@0 687 NS_WARNING("out of memory");
michael@0 688 gBloatLog = nullptr;
michael@0 689 gLogLeaksOnly = false;
michael@0 690 }
michael@0 691 }
michael@0 692
michael@0 693 (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
michael@0 694
michael@0 695 (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
michael@0 696
michael@0 697 defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog);
michael@0 698 if (defined) {
michael@0 699 gLogToLeaky = true;
michael@0 700 PRFuncPtr p = nullptr, q = nullptr;
michael@0 701 #ifdef HAVE_DLOPEN
michael@0 702 {
michael@0 703 PRLibrary *lib = nullptr;
michael@0 704 p = PR_FindFunctionSymbolAndLibrary("__log_addref", &lib);
michael@0 705 if (lib) {
michael@0 706 PR_UnloadLibrary(lib);
michael@0 707 lib = nullptr;
michael@0 708 }
michael@0 709 q = PR_FindFunctionSymbolAndLibrary("__log_release", &lib);
michael@0 710 if (lib) {
michael@0 711 PR_UnloadLibrary(lib);
michael@0 712 }
michael@0 713 }
michael@0 714 #endif
michael@0 715 if (p && q) {
michael@0 716 leakyLogAddRef = (void (*)(void*,int,int)) p;
michael@0 717 leakyLogRelease = (void (*)(void*,int,int)) q;
michael@0 718 }
michael@0 719 else {
michael@0 720 gLogToLeaky = false;
michael@0 721 fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
michael@0 722 fflush(stdout);
michael@0 723 }
michael@0 724 }
michael@0 725
michael@0 726 const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
michael@0 727
michael@0 728 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
michael@0 729 if (classes) {
michael@0 730 (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
michael@0 731 } else {
michael@0 732 if (getenv("XPCOM_MEM_COMPTR_LOG")) {
michael@0 733 fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
michael@0 734 }
michael@0 735 }
michael@0 736 #else
michael@0 737 const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
michael@0 738 if (comptr_log) {
michael@0 739 fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
michael@0 740 }
michael@0 741 #endif
michael@0 742
michael@0 743 if (classes) {
michael@0 744 // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
michael@0 745 // as a list of class names to track
michael@0 746 gTypesToLog = PL_NewHashTable(256,
michael@0 747 PL_HashString,
michael@0 748 PL_CompareStrings,
michael@0 749 PL_CompareValues,
michael@0 750 &typesToLogHashAllocOps, nullptr);
michael@0 751 if (!gTypesToLog) {
michael@0 752 NS_WARNING("out of memory");
michael@0 753 fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
michael@0 754 }
michael@0 755 else {
michael@0 756 fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
michael@0 757 const char* cp = classes;
michael@0 758 for (;;) {
michael@0 759 char* cm = (char*) strchr(cp, ',');
michael@0 760 if (cm) {
michael@0 761 *cm = '\0';
michael@0 762 }
michael@0 763 PL_HashTableAdd(gTypesToLog, strdup(cp), (void*)1);
michael@0 764 fprintf(stdout, "%s ", cp);
michael@0 765 if (!cm) break;
michael@0 766 *cm = ',';
michael@0 767 cp = cm + 1;
michael@0 768 }
michael@0 769 fprintf(stdout, "\n");
michael@0 770 }
michael@0 771
michael@0 772 gSerialNumbers = PL_NewHashTable(256,
michael@0 773 HashNumber,
michael@0 774 PL_CompareValues,
michael@0 775 PL_CompareValues,
michael@0 776 &serialNumberHashAllocOps, nullptr);
michael@0 777
michael@0 778
michael@0 779 }
michael@0 780
michael@0 781 const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
michael@0 782 if (objects) {
michael@0 783 gObjectsToLog = PL_NewHashTable(256,
michael@0 784 HashNumber,
michael@0 785 PL_CompareValues,
michael@0 786 PL_CompareValues,
michael@0 787 nullptr, nullptr);
michael@0 788
michael@0 789 if (!gObjectsToLog) {
michael@0 790 NS_WARNING("out of memory");
michael@0 791 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
michael@0 792 }
michael@0 793 else if (! (gRefcntsLog || gAllocLog || gCOMPtrLog)) {
michael@0 794 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
michael@0 795 }
michael@0 796 else {
michael@0 797 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
michael@0 798 const char* cp = objects;
michael@0 799 for (;;) {
michael@0 800 char* cm = (char*) strchr(cp, ',');
michael@0 801 if (cm) {
michael@0 802 *cm = '\0';
michael@0 803 }
michael@0 804 intptr_t top = 0;
michael@0 805 intptr_t bottom = 0;
michael@0 806 while (*cp) {
michael@0 807 if (*cp == '-') {
michael@0 808 bottom = top;
michael@0 809 top = 0;
michael@0 810 ++cp;
michael@0 811 }
michael@0 812 top *= 10;
michael@0 813 top += *cp - '0';
michael@0 814 ++cp;
michael@0 815 }
michael@0 816 if (!bottom) {
michael@0 817 bottom = top;
michael@0 818 }
michael@0 819 for (intptr_t serialno = bottom; serialno <= top; serialno++) {
michael@0 820 PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
michael@0 821 fprintf(stdout, "%" PRIdPTR " ", serialno);
michael@0 822 }
michael@0 823 if (!cm) break;
michael@0 824 *cm = ',';
michael@0 825 cp = cm + 1;
michael@0 826 }
michael@0 827 fprintf(stdout, "\n");
michael@0 828 }
michael@0 829 }
michael@0 830
michael@0 831
michael@0 832 if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog || gCOMPtrLog) {
michael@0 833 gLogging = true;
michael@0 834 }
michael@0 835
michael@0 836 gTraceLock = PR_NewLock();
michael@0 837 }
michael@0 838
michael@0 839 #endif
michael@0 840
michael@0 841 extern "C" {
michael@0 842
michael@0 843 #ifdef STACKWALKING_AVAILABLE
michael@0 844 static void PrintStackFrame(void *aPC, void *aSP, void *aClosure)
michael@0 845 {
michael@0 846 FILE *stream = (FILE*)aClosure;
michael@0 847 nsCodeAddressDetails details;
michael@0 848 char buf[1024];
michael@0 849
michael@0 850 NS_DescribeCodeAddress(aPC, &details);
michael@0 851 NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf));
michael@0 852 fputs(buf, stream);
michael@0 853 }
michael@0 854 #endif
michael@0 855
michael@0 856 }
michael@0 857
michael@0 858 void
michael@0 859 nsTraceRefcnt::WalkTheStack(FILE* aStream)
michael@0 860 {
michael@0 861 #ifdef STACKWALKING_AVAILABLE
michael@0 862 NS_StackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, aStream,
michael@0 863 0, nullptr);
michael@0 864 #endif
michael@0 865 }
michael@0 866
michael@0 867 //----------------------------------------------------------------------
michael@0 868
michael@0 869 // This thing is exported by libstdc++
michael@0 870 // Yes, this is a gcc only hack
michael@0 871 #if defined(MOZ_DEMANGLE_SYMBOLS)
michael@0 872 #include <cxxabi.h>
michael@0 873 #include <stdlib.h> // for free()
michael@0 874 #endif // MOZ_DEMANGLE_SYMBOLS
michael@0 875
michael@0 876 void
michael@0 877 nsTraceRefcnt::DemangleSymbol(const char * aSymbol,
michael@0 878 char * aBuffer,
michael@0 879 int aBufLen)
michael@0 880 {
michael@0 881 NS_ASSERTION(nullptr != aSymbol,"null symbol");
michael@0 882 NS_ASSERTION(nullptr != aBuffer,"null buffer");
michael@0 883 NS_ASSERTION(aBufLen >= 32 ,"pulled 32 out of you know where");
michael@0 884
michael@0 885 aBuffer[0] = '\0';
michael@0 886
michael@0 887 #if defined(MOZ_DEMANGLE_SYMBOLS)
michael@0 888 /* See demangle.h in the gcc source for the voodoo */
michael@0 889 char * demangled = abi::__cxa_demangle(aSymbol,0,0,0);
michael@0 890
michael@0 891 if (demangled)
michael@0 892 {
michael@0 893 strncpy(aBuffer,demangled,aBufLen);
michael@0 894 free(demangled);
michael@0 895 }
michael@0 896 #endif // MOZ_DEMANGLE_SYMBOLS
michael@0 897 }
michael@0 898
michael@0 899
michael@0 900 //----------------------------------------------------------------------
michael@0 901
michael@0 902 EXPORT_XPCOM_API(void)
michael@0 903 NS_LogInit()
michael@0 904 {
michael@0 905 // FIXME: This is called multiple times, we should probably not allow that.
michael@0 906 #ifdef STACKWALKING_AVAILABLE
michael@0 907 StackWalkInitCriticalAddress();
michael@0 908 #endif
michael@0 909 #ifdef NS_IMPL_REFCNT_LOGGING
michael@0 910 if (++gInitCount)
michael@0 911 nsTraceRefcnt::SetActivityIsLegal(true);
michael@0 912 #endif
michael@0 913
michael@0 914 #ifdef NS_TRACE_MALLOC
michael@0 915 // XXX we don't have to worry about shutting down trace-malloc; it
michael@0 916 // handles this itself, through an atexit() callback.
michael@0 917 if (!NS_TraceMallocHasStarted())
michael@0 918 NS_TraceMallocStartup(-1); // -1 == no logging
michael@0 919 #endif
michael@0 920 }
michael@0 921
michael@0 922 EXPORT_XPCOM_API(void)
michael@0 923 NS_LogTerm()
michael@0 924 {
michael@0 925 mozilla::LogTerm();
michael@0 926 }
michael@0 927
michael@0 928 namespace mozilla {
michael@0 929 void
michael@0 930 LogTerm()
michael@0 931 {
michael@0 932 NS_ASSERTION(gInitCount > 0,
michael@0 933 "NS_LogTerm without matching NS_LogInit");
michael@0 934
michael@0 935 if (--gInitCount == 0) {
michael@0 936 #ifdef DEBUG
michael@0 937 /* FIXME bug 491977: This is only going to operate on the
michael@0 938 * BlockingResourceBase which is compiled into
michael@0 939 * libxul/libxpcom_core.so. Anyone using external linkage will
michael@0 940 * have their own copy of BlockingResourceBase statics which will
michael@0 941 * not be freed by this method.
michael@0 942 *
michael@0 943 * It sounds like what we really want is to be able to register a
michael@0 944 * callback function to call at XPCOM shutdown. Note that with
michael@0 945 * this solution, however, we need to guarantee that
michael@0 946 * BlockingResourceBase::Shutdown() runs after all other shutdown
michael@0 947 * functions.
michael@0 948 */
michael@0 949 BlockingResourceBase::Shutdown();
michael@0 950 #endif
michael@0 951
michael@0 952 if (gInitialized) {
michael@0 953 nsTraceRefcnt::DumpStatistics();
michael@0 954 nsTraceRefcnt::ResetStatistics();
michael@0 955 }
michael@0 956 nsTraceRefcnt::Shutdown();
michael@0 957 #ifdef NS_IMPL_REFCNT_LOGGING
michael@0 958 nsTraceRefcnt::SetActivityIsLegal(false);
michael@0 959 gActivityTLS = BAD_TLS_INDEX;
michael@0 960 #endif
michael@0 961 }
michael@0 962 }
michael@0 963
michael@0 964 } // namespace mozilla
michael@0 965
michael@0 966 EXPORT_XPCOM_API(void)
michael@0 967 NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt,
michael@0 968 const char* aClazz, uint32_t classSize)
michael@0 969 {
michael@0 970 #ifdef NS_IMPL_REFCNT_LOGGING
michael@0 971 ASSERT_ACTIVITY_IS_LEGAL;
michael@0 972 if (!gInitialized)
michael@0 973 InitTraceLog();
michael@0 974 if (gLogging) {
michael@0 975 LOCK_TRACELOG();
michael@0 976
michael@0 977 if (gBloatLog) {
michael@0 978 BloatEntry* entry = GetBloatEntry(aClazz, classSize);
michael@0 979 if (entry) {
michael@0 980 entry->AddRef(aRefcnt);
michael@0 981 }
michael@0 982 }
michael@0 983
michael@0 984 // Here's the case where MOZ_COUNT_CTOR was not used,
michael@0 985 // yet we still want to see creation information:
michael@0 986
michael@0 987 bool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
michael@0 988 intptr_t serialno = 0;
michael@0 989 if (gSerialNumbers && loggingThisType) {
michael@0 990 serialno = GetSerialNumber(aPtr, aRefcnt == 1);
michael@0 991 NS_ASSERTION(serialno != 0,
michael@0 992 "Serial number requested for unrecognized pointer! "
michael@0 993 "Are you memmoving a refcounted object?");
michael@0 994 int32_t* count = GetRefCount(aPtr);
michael@0 995 if(count)
michael@0 996 (*count)++;
michael@0 997
michael@0 998 }
michael@0 999
michael@0 1000 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
michael@0 1001 if (aRefcnt == 1 && gAllocLog && loggingThisType && loggingThisObject) {
michael@0 1002 fprintf(gAllocLog, "\n<%s> 0x%08X %" PRIdPTR " Create\n",
michael@0 1003 aClazz, NS_PTR_TO_INT32(aPtr), serialno);
michael@0 1004 nsTraceRefcnt::WalkTheStack(gAllocLog);
michael@0 1005 }
michael@0 1006
michael@0 1007 if (gRefcntsLog && loggingThisType && loggingThisObject) {
michael@0 1008 if (gLogToLeaky) {
michael@0 1009 (*leakyLogAddRef)(aPtr, aRefcnt - 1, aRefcnt);
michael@0 1010 }
michael@0 1011 else {
michael@0 1012 // Can't use PR_LOG(), b/c it truncates the line
michael@0 1013 fprintf(gRefcntsLog,
michael@0 1014 "\n<%s> 0x%08X %" PRIuPTR " AddRef %" PRIuPTR "\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
michael@0 1015 nsTraceRefcnt::WalkTheStack(gRefcntsLog);
michael@0 1016 fflush(gRefcntsLog);
michael@0 1017 }
michael@0 1018 }
michael@0 1019 UNLOCK_TRACELOG();
michael@0 1020 }
michael@0 1021 #endif
michael@0 1022 }
michael@0 1023
michael@0 1024 EXPORT_XPCOM_API(void)
michael@0 1025 NS_LogRelease(void* aPtr, nsrefcnt aRefcnt, const char* aClazz)
michael@0 1026 {
michael@0 1027 #ifdef NS_IMPL_REFCNT_LOGGING
michael@0 1028 ASSERT_ACTIVITY_IS_LEGAL;
michael@0 1029 if (!gInitialized)
michael@0 1030 InitTraceLog();
michael@0 1031 if (gLogging) {
michael@0 1032 LOCK_TRACELOG();
michael@0 1033
michael@0 1034 if (gBloatLog) {
michael@0 1035 BloatEntry* entry = GetBloatEntry(aClazz, 0);
michael@0 1036 if (entry) {
michael@0 1037 entry->Release(aRefcnt);
michael@0 1038 }
michael@0 1039 }
michael@0 1040
michael@0 1041 bool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
michael@0 1042 intptr_t serialno = 0;
michael@0 1043 if (gSerialNumbers && loggingThisType) {
michael@0 1044 serialno = GetSerialNumber(aPtr, false);
michael@0 1045 NS_ASSERTION(serialno != 0,
michael@0 1046 "Serial number requested for unrecognized pointer! "
michael@0 1047 "Are you memmoving a refcounted object?");
michael@0 1048 int32_t* count = GetRefCount(aPtr);
michael@0 1049 if(count)
michael@0 1050 (*count)--;
michael@0 1051
michael@0 1052 }
michael@0 1053
michael@0 1054 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
michael@0 1055 if (gRefcntsLog && loggingThisType && loggingThisObject) {
michael@0 1056 if (gLogToLeaky) {
michael@0 1057 (*leakyLogRelease)(aPtr, aRefcnt + 1, aRefcnt);
michael@0 1058 }
michael@0 1059 else {
michael@0 1060 // Can't use PR_LOG(), b/c it truncates the line
michael@0 1061 fprintf(gRefcntsLog,
michael@0 1062 "\n<%s> 0x%08X %" PRIuPTR " Release %" PRIuPTR "\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
michael@0 1063 nsTraceRefcnt::WalkTheStack(gRefcntsLog);
michael@0 1064 fflush(gRefcntsLog);
michael@0 1065 }
michael@0 1066 }
michael@0 1067
michael@0 1068 // Here's the case where MOZ_COUNT_DTOR was not used,
michael@0 1069 // yet we still want to see deletion information:
michael@0 1070
michael@0 1071 if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) {
michael@0 1072 fprintf(gAllocLog,
michael@0 1073 "\n<%s> 0x%08X %" PRIdPTR " Destroy\n",
michael@0 1074 aClazz, NS_PTR_TO_INT32(aPtr), serialno);
michael@0 1075 nsTraceRefcnt::WalkTheStack(gAllocLog);
michael@0 1076 }
michael@0 1077
michael@0 1078 if (aRefcnt == 0 && gSerialNumbers && loggingThisType) {
michael@0 1079 RecycleSerialNumberPtr(aPtr);
michael@0 1080 }
michael@0 1081
michael@0 1082 UNLOCK_TRACELOG();
michael@0 1083 }
michael@0 1084 #endif
michael@0 1085 }
michael@0 1086
michael@0 1087 EXPORT_XPCOM_API(void)
michael@0 1088 NS_LogCtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
michael@0 1089 {
michael@0 1090 #ifdef NS_IMPL_REFCNT_LOGGING
michael@0 1091 ASSERT_ACTIVITY_IS_LEGAL;
michael@0 1092 if (!gInitialized)
michael@0 1093 InitTraceLog();
michael@0 1094
michael@0 1095 if (gLogging) {
michael@0 1096 LOCK_TRACELOG();
michael@0 1097
michael@0 1098 if (gBloatLog) {
michael@0 1099 BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
michael@0 1100 if (entry) {
michael@0 1101 entry->Ctor();
michael@0 1102 }
michael@0 1103 }
michael@0 1104
michael@0 1105 bool loggingThisType = (!gTypesToLog || LogThisType(aType));
michael@0 1106 intptr_t serialno = 0;
michael@0 1107 if (gSerialNumbers && loggingThisType) {
michael@0 1108 serialno = GetSerialNumber(aPtr, true);
michael@0 1109 }
michael@0 1110
michael@0 1111 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
michael@0 1112 if (gAllocLog && loggingThisType && loggingThisObject) {
michael@0 1113 fprintf(gAllocLog, "\n<%s> 0x%08X %" PRIdPTR " Ctor (%d)\n",
michael@0 1114 aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
michael@0 1115 nsTraceRefcnt::WalkTheStack(gAllocLog);
michael@0 1116 }
michael@0 1117
michael@0 1118 UNLOCK_TRACELOG();
michael@0 1119 }
michael@0 1120 #endif
michael@0 1121 }
michael@0 1122
michael@0 1123
michael@0 1124 EXPORT_XPCOM_API(void)
michael@0 1125 NS_LogDtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
michael@0 1126 {
michael@0 1127 #ifdef NS_IMPL_REFCNT_LOGGING
michael@0 1128 ASSERT_ACTIVITY_IS_LEGAL;
michael@0 1129 if (!gInitialized)
michael@0 1130 InitTraceLog();
michael@0 1131
michael@0 1132 if (gLogging) {
michael@0 1133 LOCK_TRACELOG();
michael@0 1134
michael@0 1135 if (gBloatLog) {
michael@0 1136 BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
michael@0 1137 if (entry) {
michael@0 1138 entry->Dtor();
michael@0 1139 }
michael@0 1140 }
michael@0 1141
michael@0 1142 bool loggingThisType = (!gTypesToLog || LogThisType(aType));
michael@0 1143 intptr_t serialno = 0;
michael@0 1144 if (gSerialNumbers && loggingThisType) {
michael@0 1145 serialno = GetSerialNumber(aPtr, false);
michael@0 1146 RecycleSerialNumberPtr(aPtr);
michael@0 1147 }
michael@0 1148
michael@0 1149 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
michael@0 1150
michael@0 1151 // (If we're on a losing architecture, don't do this because we'll be
michael@0 1152 // using LogDeleteXPCOM instead to get file and line numbers.)
michael@0 1153 if (gAllocLog && loggingThisType && loggingThisObject) {
michael@0 1154 fprintf(gAllocLog, "\n<%s> 0x%08X %" PRIdPTR " Dtor (%d)\n",
michael@0 1155 aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
michael@0 1156 nsTraceRefcnt::WalkTheStack(gAllocLog);
michael@0 1157 }
michael@0 1158
michael@0 1159 UNLOCK_TRACELOG();
michael@0 1160 }
michael@0 1161 #endif
michael@0 1162 }
michael@0 1163
michael@0 1164
michael@0 1165 EXPORT_XPCOM_API(void)
michael@0 1166 NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject)
michael@0 1167 {
michael@0 1168 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
michael@0 1169 // Get the most-derived object.
michael@0 1170 void *object = dynamic_cast<void *>(aObject);
michael@0 1171
michael@0 1172 // This is a very indirect way of finding out what the class is
michael@0 1173 // of the object being logged. If we're logging a specific type,
michael@0 1174 // then
michael@0 1175 if (!gTypesToLog || !gSerialNumbers) {
michael@0 1176 return;
michael@0 1177 }
michael@0 1178 intptr_t serialno = GetSerialNumber(object, false);
michael@0 1179 if (serialno == 0) {
michael@0 1180 return;
michael@0 1181 }
michael@0 1182
michael@0 1183 if (!gInitialized)
michael@0 1184 InitTraceLog();
michael@0 1185 if (gLogging) {
michael@0 1186 LOCK_TRACELOG();
michael@0 1187
michael@0 1188 int32_t* count = GetCOMPtrCount(object);
michael@0 1189 if(count)
michael@0 1190 (*count)++;
michael@0 1191
michael@0 1192 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
michael@0 1193
michael@0 1194 if (gCOMPtrLog && loggingThisObject) {
michael@0 1195 fprintf(gCOMPtrLog, "\n<?> 0x%08X %" PRIdPTR " nsCOMPtrAddRef %d 0x%08X\n",
michael@0 1196 NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
michael@0 1197 nsTraceRefcnt::WalkTheStack(gCOMPtrLog);
michael@0 1198 }
michael@0 1199
michael@0 1200 UNLOCK_TRACELOG();
michael@0 1201 }
michael@0 1202 #endif
michael@0 1203 }
michael@0 1204
michael@0 1205
michael@0 1206 EXPORT_XPCOM_API(void)
michael@0 1207 NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject)
michael@0 1208 {
michael@0 1209 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
michael@0 1210 // Get the most-derived object.
michael@0 1211 void *object = dynamic_cast<void *>(aObject);
michael@0 1212
michael@0 1213 // This is a very indirect way of finding out what the class is
michael@0 1214 // of the object being logged. If we're logging a specific type,
michael@0 1215 // then
michael@0 1216 if (!gTypesToLog || !gSerialNumbers) {
michael@0 1217 return;
michael@0 1218 }
michael@0 1219 intptr_t serialno = GetSerialNumber(object, false);
michael@0 1220 if (serialno == 0) {
michael@0 1221 return;
michael@0 1222 }
michael@0 1223
michael@0 1224 if (!gInitialized)
michael@0 1225 InitTraceLog();
michael@0 1226 if (gLogging) {
michael@0 1227 LOCK_TRACELOG();
michael@0 1228
michael@0 1229 int32_t* count = GetCOMPtrCount(object);
michael@0 1230 if(count)
michael@0 1231 (*count)--;
michael@0 1232
michael@0 1233 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
michael@0 1234
michael@0 1235 if (gCOMPtrLog && loggingThisObject) {
michael@0 1236 fprintf(gCOMPtrLog, "\n<?> 0x%08X %" PRIdPTR " nsCOMPtrRelease %d 0x%08X\n",
michael@0 1237 NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
michael@0 1238 nsTraceRefcnt::WalkTheStack(gCOMPtrLog);
michael@0 1239 }
michael@0 1240
michael@0 1241 UNLOCK_TRACELOG();
michael@0 1242 }
michael@0 1243 #endif
michael@0 1244 }
michael@0 1245
michael@0 1246 void
michael@0 1247 nsTraceRefcnt::Startup()
michael@0 1248 {
michael@0 1249 }
michael@0 1250
michael@0 1251 static void maybeUnregisterAndCloseFile(FILE *&f) {
michael@0 1252 if (!f)
michael@0 1253 return;
michael@0 1254
michael@0 1255 MozillaUnRegisterDebugFILE(f);
michael@0 1256 fclose(f);
michael@0 1257 f = nullptr;
michael@0 1258 }
michael@0 1259
michael@0 1260 void
michael@0 1261 nsTraceRefcnt::Shutdown()
michael@0 1262 {
michael@0 1263 #ifdef NS_IMPL_REFCNT_LOGGING
michael@0 1264
michael@0 1265 if (gBloatView) {
michael@0 1266 PL_HashTableDestroy(gBloatView);
michael@0 1267 gBloatView = nullptr;
michael@0 1268 }
michael@0 1269 if (gTypesToLog) {
michael@0 1270 PL_HashTableDestroy(gTypesToLog);
michael@0 1271 gTypesToLog = nullptr;
michael@0 1272 }
michael@0 1273 if (gObjectsToLog) {
michael@0 1274 PL_HashTableDestroy(gObjectsToLog);
michael@0 1275 gObjectsToLog = nullptr;
michael@0 1276 }
michael@0 1277 if (gSerialNumbers) {
michael@0 1278 PL_HashTableDestroy(gSerialNumbers);
michael@0 1279 gSerialNumbers = nullptr;
michael@0 1280 }
michael@0 1281 maybeUnregisterAndCloseFile(gBloatLog);
michael@0 1282 maybeUnregisterAndCloseFile(gRefcntsLog);
michael@0 1283 maybeUnregisterAndCloseFile(gAllocLog);
michael@0 1284 maybeUnregisterAndCloseFile(gLeakyLog);
michael@0 1285 maybeUnregisterAndCloseFile(gCOMPtrLog);
michael@0 1286 #endif
michael@0 1287 }
michael@0 1288
michael@0 1289 void
michael@0 1290 nsTraceRefcnt::SetActivityIsLegal(bool aLegal)
michael@0 1291 {
michael@0 1292 #ifdef NS_IMPL_REFCNT_LOGGING
michael@0 1293 if (gActivityTLS == BAD_TLS_INDEX)
michael@0 1294 PR_NewThreadPrivateIndex(&gActivityTLS, nullptr);
michael@0 1295
michael@0 1296 PR_SetThreadPrivate(gActivityTLS, NS_INT32_TO_PTR(!aLegal));
michael@0 1297 #endif
michael@0 1298 }

mercurial