Sat, 03 Jan 2015 20:18:00 +0100
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 | } |