Bug 959405 - Please update the Buri Moz-central, 1.3, 1.2 with the latest blobs from...
[gecko.git] / xpcom / base / nsTraceRefcntImpl.cpp
blob025bb7019d4e9fb26acfd761d95e7ff780dedfbd
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsTraceRefcntImpl.h"
7 #include "mozilla/IntegerPrintfMacros.h"
8 #include "nsXPCOMPrivate.h"
9 #include "nscore.h"
10 #include "nsISupports.h"
11 #include "nsTArray.h"
12 #include "prenv.h"
13 #include "plstr.h"
14 #include "prlink.h"
15 #include "nsCRT.h"
16 #include <math.h>
17 #include "nsStackWalkPrivate.h"
18 #include "nsStackWalk.h"
19 #include "nsString.h"
21 #include "nsXULAppAPI.h"
22 #ifdef XP_WIN
23 #include <process.h>
24 #define getpid _getpid
25 #else
26 #include <unistd.h>
27 #endif
29 #ifdef NS_TRACE_MALLOC
30 #include "nsTraceMalloc.h"
31 #endif
33 #include "mozilla/BlockingResourceBase.h"
34 #include "mozilla/PoisonIOInterposer.h"
36 #ifdef HAVE_DLOPEN
37 #include <dlfcn.h>
38 #endif
40 ////////////////////////////////////////////////////////////////////////////////
42 void
43 NS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
44 double *meanResult, double *stdDevResult)
46 double mean = 0.0, var = 0.0, stdDev = 0.0;
47 if (n > 0.0 && sumOfValues >= 0) {
48 mean = sumOfValues / n;
49 double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
50 if (temp < 0.0 || n <= 1)
51 var = 0.0;
52 else
53 var = temp / (n * (n - 1));
54 // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
55 stdDev = var != 0.0 ? sqrt(var) : 0.0;
57 *meanResult = mean;
58 *stdDevResult = stdDev;
61 ////////////////////////////////////////////////////////////////////////////////
63 #if !defined(XP_WIN) || (!defined(MOZ_OPTIMIZE) || defined(MOZ_PROFILING) || defined(DEBUG))
64 #define STACKWALKING_AVAILABLE
65 #endif
67 #define NS_IMPL_REFCNT_LOGGING
69 #ifdef NS_IMPL_REFCNT_LOGGING
70 #include "plhash.h"
71 #include "prmem.h"
73 #include "prlock.h"
75 // TraceRefcnt has to use bare PRLock instead of mozilla::Mutex
76 // because TraceRefcnt can be used very early in startup.
77 static PRLock* gTraceLock;
79 #define LOCK_TRACELOG() PR_Lock(gTraceLock)
80 #define UNLOCK_TRACELOG() PR_Unlock(gTraceLock)
82 static PLHashTable* gBloatView;
83 static PLHashTable* gTypesToLog;
84 static PLHashTable* gObjectsToLog;
85 static PLHashTable* gSerialNumbers;
86 static intptr_t gNextSerialNumber;
88 static bool gLogging;
89 static bool gLogToLeaky;
90 static bool gLogLeaksOnly;
92 static void (*leakyLogAddRef)(void* p, int oldrc, int newrc);
93 static void (*leakyLogRelease)(void* p, int oldrc, int newrc);
95 #define BAD_TLS_INDEX ((unsigned) -1)
97 // if gActivityTLS == BAD_TLS_INDEX, then we're
98 // unitialized... otherwise this points to a NSPR TLS thread index
99 // indicating whether addref activity is legal. If the PTR_TO_INT32 is 0 then
100 // activity is ok, otherwise not!
101 static unsigned gActivityTLS = BAD_TLS_INDEX;
103 static bool gInitialized;
104 static nsrefcnt gInitCount;
106 static FILE *gBloatLog = nullptr;
107 static FILE *gRefcntsLog = nullptr;
108 static FILE *gAllocLog = nullptr;
109 static FILE *gLeakyLog = nullptr;
110 static FILE *gCOMPtrLog = nullptr;
112 struct serialNumberRecord {
113 intptr_t serialNumber;
114 int32_t refCount;
115 int32_t COMPtrCount;
118 struct nsTraceRefcntStats {
119 uint64_t mAddRefs;
120 uint64_t mReleases;
121 uint64_t mCreates;
122 uint64_t mDestroys;
123 double mRefsOutstandingTotal;
124 double mRefsOutstandingSquared;
125 double mObjsOutstandingTotal;
126 double mObjsOutstandingSquared;
129 // I hope to turn this on for everybody once we hit it a little less.
130 #ifdef DEBUG
131 static const char kStaticCtorDtorWarning[] =
132 "XPCOM objects created/destroyed from static ctor/dtor";
134 static void
135 AssertActivityIsLegal()
137 if (gActivityTLS == BAD_TLS_INDEX ||
138 NS_PTR_TO_INT32(PR_GetThreadPrivate(gActivityTLS)) != 0) {
139 if (PR_GetEnv("MOZ_FATAL_STATIC_XPCOM_CTORS_DTORS")) {
140 NS_RUNTIMEABORT(kStaticCtorDtorWarning);
141 } else {
142 NS_WARNING(kStaticCtorDtorWarning);
146 # define ASSERT_ACTIVITY_IS_LEGAL \
147 PR_BEGIN_MACRO \
148 AssertActivityIsLegal(); \
149 PR_END_MACRO
150 #else
151 # define ASSERT_ACTIVITY_IS_LEGAL PR_BEGIN_MACRO PR_END_MACRO
152 #endif // DEBUG
154 // These functions are copied from nsprpub/lib/ds/plhash.c, with changes
155 // to the functions not called Default* to free the serialNumberRecord or
156 // the BloatEntry.
158 static void *
159 DefaultAllocTable(void *pool, size_t size)
161 return PR_MALLOC(size);
164 static void
165 DefaultFreeTable(void *pool, void *item)
167 PR_Free(item);
170 static PLHashEntry *
171 DefaultAllocEntry(void *pool, const void *key)
173 return PR_NEW(PLHashEntry);
176 static void
177 SerialNumberFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
179 if (flag == HT_FREE_ENTRY) {
180 PR_Free(reinterpret_cast<serialNumberRecord*>(he->value));
181 PR_Free(he);
185 static void
186 TypesToLogFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
188 if (flag == HT_FREE_ENTRY) {
189 free(const_cast<char*>(reinterpret_cast<const char*>(he->key)));
190 PR_Free(he);
194 static const PLHashAllocOps serialNumberHashAllocOps = {
195 DefaultAllocTable, DefaultFreeTable,
196 DefaultAllocEntry, SerialNumberFreeEntry
199 static const PLHashAllocOps typesToLogHashAllocOps = {
200 DefaultAllocTable, DefaultFreeTable,
201 DefaultAllocEntry, TypesToLogFreeEntry
204 ////////////////////////////////////////////////////////////////////////////////
206 class BloatEntry {
207 public:
208 BloatEntry(const char* className, uint32_t classSize)
209 : mClassSize(classSize) {
210 mClassName = PL_strdup(className);
211 Clear(&mNewStats);
212 Clear(&mAllStats);
213 mTotalLeaked = 0;
216 ~BloatEntry() {
217 PL_strfree(mClassName);
220 uint32_t GetClassSize() { return (uint32_t)mClassSize; }
221 const char* GetClassName() { return mClassName; }
223 static void Clear(nsTraceRefcntStats* stats) {
224 stats->mAddRefs = 0;
225 stats->mReleases = 0;
226 stats->mCreates = 0;
227 stats->mDestroys = 0;
228 stats->mRefsOutstandingTotal = 0;
229 stats->mRefsOutstandingSquared = 0;
230 stats->mObjsOutstandingTotal = 0;
231 stats->mObjsOutstandingSquared = 0;
234 void Accumulate() {
235 mAllStats.mAddRefs += mNewStats.mAddRefs;
236 mAllStats.mReleases += mNewStats.mReleases;
237 mAllStats.mCreates += mNewStats.mCreates;
238 mAllStats.mDestroys += mNewStats.mDestroys;
239 mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal;
240 mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared;
241 mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal;
242 mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared;
243 Clear(&mNewStats);
246 void AddRef(nsrefcnt refcnt) {
247 mNewStats.mAddRefs++;
248 if (refcnt == 1) {
249 Ctor();
251 AccountRefs();
254 void Release(nsrefcnt refcnt) {
255 mNewStats.mReleases++;
256 if (refcnt == 0) {
257 Dtor();
259 AccountRefs();
262 void Ctor() {
263 mNewStats.mCreates++;
264 AccountObjs();
267 void Dtor() {
268 mNewStats.mDestroys++;
269 AccountObjs();
272 void AccountRefs() {
273 uint64_t cnt = (mNewStats.mAddRefs - mNewStats.mReleases);
274 mNewStats.mRefsOutstandingTotal += cnt;
275 mNewStats.mRefsOutstandingSquared += cnt * cnt;
278 void AccountObjs() {
279 uint64_t cnt = (mNewStats.mCreates - mNewStats.mDestroys);
280 mNewStats.mObjsOutstandingTotal += cnt;
281 mNewStats.mObjsOutstandingSquared += cnt * cnt;
284 static int DumpEntry(PLHashEntry *he, int i, void *arg) {
285 BloatEntry* entry = (BloatEntry*)he->value;
286 if (entry) {
287 entry->Accumulate();
288 static_cast<nsTArray<BloatEntry*>*>(arg)->AppendElement(entry);
290 return HT_ENUMERATE_NEXT;
293 static int TotalEntries(PLHashEntry *he, int i, void *arg) {
294 BloatEntry* entry = (BloatEntry*)he->value;
295 if (entry && nsCRT::strcmp(entry->mClassName, "TOTAL") != 0) {
296 entry->Total((BloatEntry*)arg);
298 return HT_ENUMERATE_NEXT;
301 void Total(BloatEntry* total) {
302 total->mAllStats.mAddRefs += mNewStats.mAddRefs + mAllStats.mAddRefs;
303 total->mAllStats.mReleases += mNewStats.mReleases + mAllStats.mReleases;
304 total->mAllStats.mCreates += mNewStats.mCreates + mAllStats.mCreates;
305 total->mAllStats.mDestroys += mNewStats.mDestroys + mAllStats.mDestroys;
306 total->mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal + mAllStats.mRefsOutstandingTotal;
307 total->mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared + mAllStats.mRefsOutstandingSquared;
308 total->mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal + mAllStats.mObjsOutstandingTotal;
309 total->mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared + mAllStats.mObjsOutstandingSquared;
310 uint64_t count = (mNewStats.mCreates + mAllStats.mCreates);
311 total->mClassSize += mClassSize * count; // adjust for average in DumpTotal
312 total->mTotalLeaked += (uint64_t)(mClassSize *
313 ((mNewStats.mCreates + mAllStats.mCreates)
314 -(mNewStats.mDestroys + mAllStats.mDestroys)));
317 void DumpTotal(FILE* out) {
318 mClassSize /= mAllStats.mCreates;
319 Dump(-1, out, nsTraceRefcntImpl::ALL_STATS);
322 static bool HaveLeaks(nsTraceRefcntStats* stats) {
323 return ((stats->mAddRefs != stats->mReleases) ||
324 (stats->mCreates != stats->mDestroys));
327 bool PrintDumpHeader(FILE* out, const char* msg, nsTraceRefcntImpl::StatisticsType type) {
328 fprintf(out, "\n== BloatView: %s, %s process %d\n", msg,
329 XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid());
330 nsTraceRefcntStats& stats =
331 (type == nsTraceRefcntImpl::NEW_STATS) ? mNewStats : mAllStats;
332 if (gLogLeaksOnly && !HaveLeaks(&stats))
333 return false;
335 fprintf(out,
336 "\n" \
337 " |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n" \
338 " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
340 this->DumpTotal(out);
342 return true;
345 void Dump(int i, FILE* out, nsTraceRefcntImpl::StatisticsType type) {
346 nsTraceRefcntStats* stats = (type == nsTraceRefcntImpl::NEW_STATS) ? &mNewStats : &mAllStats;
347 if (gLogLeaksOnly && !HaveLeaks(stats)) {
348 return;
351 double meanRefs, stddevRefs;
352 NS_MeanAndStdDev(stats->mAddRefs + stats->mReleases,
353 stats->mRefsOutstandingTotal,
354 stats->mRefsOutstandingSquared,
355 &meanRefs, &stddevRefs);
357 double meanObjs, stddevObjs;
358 NS_MeanAndStdDev(stats->mCreates + stats->mDestroys,
359 stats->mObjsOutstandingTotal,
360 stats->mObjsOutstandingSquared,
361 &meanObjs, &stddevObjs);
363 if ((stats->mAddRefs - stats->mReleases) != 0 ||
364 stats->mAddRefs != 0 ||
365 meanRefs != 0 ||
366 stddevRefs != 0 ||
367 (stats->mCreates - stats->mDestroys) != 0 ||
368 stats->mCreates != 0 ||
369 meanObjs != 0 ||
370 stddevObjs != 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",
372 i+1, mClassName,
373 (int32_t)mClassSize,
374 (nsCRT::strcmp(mClassName, "TOTAL"))
375 ?(uint64_t)((stats->mCreates - stats->mDestroys) * mClassSize)
376 :mTotalLeaked,
377 stats->mCreates,
378 (stats->mCreates - stats->mDestroys),
379 meanObjs,
380 stddevObjs,
381 stats->mAddRefs,
382 (stats->mAddRefs - stats->mReleases),
383 meanRefs,
384 stddevRefs);
388 protected:
389 char* mClassName;
390 double mClassSize; // this is stored as a double because of the way we compute the avg class size for total bloat
391 uint64_t mTotalLeaked; // used only for TOTAL entry
392 nsTraceRefcntStats mNewStats;
393 nsTraceRefcntStats mAllStats;
396 static void
397 BloatViewFreeEntry(void *pool, PLHashEntry *he, unsigned flag)
399 if (flag == HT_FREE_ENTRY) {
400 BloatEntry* entry = reinterpret_cast<BloatEntry*>(he->value);
401 delete entry;
402 PR_Free(he);
406 const static PLHashAllocOps bloatViewHashAllocOps = {
407 DefaultAllocTable, DefaultFreeTable,
408 DefaultAllocEntry, BloatViewFreeEntry
411 static void
412 RecreateBloatView()
414 gBloatView = PL_NewHashTable(256,
415 PL_HashString,
416 PL_CompareStrings,
417 PL_CompareValues,
418 &bloatViewHashAllocOps, nullptr);
421 static BloatEntry*
422 GetBloatEntry(const char* aTypeName, uint32_t aInstanceSize)
424 if (!gBloatView) {
425 RecreateBloatView();
427 BloatEntry* entry = nullptr;
428 if (gBloatView) {
429 entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName);
430 if (entry == nullptr && aInstanceSize > 0) {
432 entry = new BloatEntry(aTypeName, aInstanceSize);
433 PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry);
434 if (e == nullptr) {
435 delete entry;
436 entry = nullptr;
438 } else {
439 NS_ASSERTION(aInstanceSize == 0 ||
440 entry->GetClassSize() == aInstanceSize,
441 "bad size recorded");
444 return entry;
447 static int DumpSerialNumbers(PLHashEntry* aHashEntry, int aIndex, void* aClosure)
449 serialNumberRecord* record = reinterpret_cast<serialNumberRecord *>(aHashEntry->value);
450 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
451 fprintf((FILE*) aClosure, "%" PRIdPTR
452 " @%p (%d references; %d from COMPtrs)\n",
453 record->serialNumber,
454 NS_INT32_TO_PTR(aHashEntry->key),
455 record->refCount,
456 record->COMPtrCount);
457 #else
458 fprintf((FILE*) aClosure, "%" PRIdPTR
459 " @%p (%d references)\n",
460 record->serialNumber,
461 NS_INT32_TO_PTR(aHashEntry->key),
462 record->refCount);
463 #endif
464 return HT_ENUMERATE_NEXT;
468 template <>
469 class nsDefaultComparator <BloatEntry*, BloatEntry*> {
470 public:
471 bool Equals(BloatEntry* const& aA, BloatEntry* const& aB) const {
472 return PL_strcmp(aA->GetClassName(), aB->GetClassName()) == 0;
474 bool LessThan(BloatEntry* const& aA, BloatEntry* const& aB) const {
475 return PL_strcmp(aA->GetClassName(), aB->GetClassName()) < 0;
479 #endif /* NS_IMPL_REFCNT_LOGGING */
481 nsresult
482 nsTraceRefcntImpl::DumpStatistics(StatisticsType type, FILE* out)
484 #ifdef NS_IMPL_REFCNT_LOGGING
485 if (gBloatLog == nullptr || gBloatView == nullptr) {
486 return NS_ERROR_FAILURE;
488 if (out == nullptr) {
489 out = gBloatLog;
492 LOCK_TRACELOG();
494 bool wasLogging = gLogging;
495 gLogging = false; // turn off logging for this method
497 BloatEntry total("TOTAL", 0);
498 PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
499 const char* msg;
500 if (type == NEW_STATS) {
501 if (gLogLeaksOnly)
502 msg = "NEW (incremental) LEAK STATISTICS";
503 else
504 msg = "NEW (incremental) LEAK AND BLOAT STATISTICS";
506 else {
507 if (gLogLeaksOnly)
508 msg = "ALL (cumulative) LEAK STATISTICS";
509 else
510 msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
512 const bool leaked = total.PrintDumpHeader(out, msg, type);
514 nsTArray<BloatEntry*> entries;
515 PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries);
516 const uint32_t count = entries.Length();
518 if (!gLogLeaksOnly || leaked) {
519 // Sort the entries alphabetically by classname.
520 entries.Sort();
522 for (uint32_t i = 0; i < count; ++i) {
523 BloatEntry* entry = entries[i];
524 entry->Dump(i, out, type);
527 fprintf(out, "\n");
530 fprintf(out, "nsTraceRefcntImpl::DumpStatistics: %d entries\n", count);
532 if (gSerialNumbers) {
533 fprintf(out, "\nSerial Numbers of Leaked Objects:\n");
534 PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, out);
537 gLogging = wasLogging;
538 UNLOCK_TRACELOG();
539 #endif
541 return NS_OK;
544 void
545 nsTraceRefcntImpl::ResetStatistics()
547 #ifdef NS_IMPL_REFCNT_LOGGING
548 LOCK_TRACELOG();
549 if (gBloatView) {
550 PL_HashTableDestroy(gBloatView);
551 gBloatView = nullptr;
553 UNLOCK_TRACELOG();
554 #endif
557 #ifdef NS_IMPL_REFCNT_LOGGING
558 static bool LogThisType(const char* aTypeName)
560 void* he = PL_HashTableLookup(gTypesToLog, aTypeName);
561 return nullptr != he;
564 static intptr_t GetSerialNumber(void* aPtr, bool aCreate)
566 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
567 if (hep && *hep) {
568 return reinterpret_cast<serialNumberRecord*>((*hep)->value)->serialNumber;
570 else if (aCreate) {
571 serialNumberRecord *record = PR_NEW(serialNumberRecord);
572 record->serialNumber = ++gNextSerialNumber;
573 record->refCount = 0;
574 record->COMPtrCount = 0;
575 PL_HashTableRawAdd(gSerialNumbers, hep, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr, reinterpret_cast<void*>(record));
576 return gNextSerialNumber;
578 else {
579 return 0;
583 static int32_t* GetRefCount(void* aPtr)
585 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
586 if (hep && *hep) {
587 return &((reinterpret_cast<serialNumberRecord*>((*hep)->value))->refCount);
588 } else {
589 return nullptr;
593 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
594 static int32_t* GetCOMPtrCount(void* aPtr)
596 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
597 if (hep && *hep) {
598 return &((reinterpret_cast<serialNumberRecord*>((*hep)->value))->COMPtrCount);
599 } else {
600 return nullptr;
603 #endif
605 static void RecycleSerialNumberPtr(void* aPtr)
607 PL_HashTableRemove(gSerialNumbers, aPtr);
610 static bool LogThisObj(intptr_t aSerialNumber)
612 return nullptr != PL_HashTableLookup(gObjectsToLog, (const void*)aSerialNumber);
615 #ifdef XP_WIN
616 #define FOPEN_NO_INHERIT "N"
617 #else
618 #define FOPEN_NO_INHERIT
619 #endif
621 static bool InitLog(const char* envVar, const char* msg, FILE* *result)
623 const char* value = getenv(envVar);
624 if (value) {
625 if (nsCRT::strcmp(value, "1") == 0) {
626 *result = stdout;
627 fprintf(stdout, "### %s defined -- logging %s to stdout\n",
628 envVar, msg);
629 return true;
631 else if (nsCRT::strcmp(value, "2") == 0) {
632 *result = stderr;
633 fprintf(stdout, "### %s defined -- logging %s to stderr\n",
634 envVar, msg);
635 return true;
637 else {
638 FILE *stream;
639 nsAutoCString fname(value);
640 if (XRE_GetProcessType() != GeckoProcessType_Default) {
641 bool hasLogExtension =
642 fname.RFind(".log", true, -1, 4) == kNotFound ? false : true;
643 if (hasLogExtension)
644 fname.Cut(fname.Length() - 4, 4);
645 fname.AppendLiteral("_");
646 fname.Append((char*)XRE_ChildProcessTypeToString(XRE_GetProcessType()));
647 fname.AppendLiteral("_pid");
648 fname.AppendInt((uint32_t)getpid());
649 if (hasLogExtension)
650 fname.AppendLiteral(".log");
652 stream = ::fopen(fname.get(), "w" FOPEN_NO_INHERIT);
653 if (stream != nullptr) {
654 MozillaRegisterDebugFD(fileno(stream));
655 *result = stream;
656 fprintf(stdout, "### %s defined -- logging %s to %s\n",
657 envVar, msg, fname.get());
659 else {
660 fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
661 envVar, msg, fname.get());
663 return stream != nullptr;
666 return false;
670 static PLHashNumber HashNumber(const void* aKey)
672 return PLHashNumber(NS_PTR_TO_INT32(aKey));
675 static void InitTraceLog(void)
677 if (gInitialized) return;
678 gInitialized = true;
680 bool defined;
681 defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
682 if (!defined)
683 gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
684 if (defined || gLogLeaksOnly) {
685 RecreateBloatView();
686 if (!gBloatView) {
687 NS_WARNING("out of memory");
688 gBloatLog = nullptr;
689 gLogLeaksOnly = false;
693 (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
695 (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
697 defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog);
698 if (defined) {
699 gLogToLeaky = true;
700 PRFuncPtr p = nullptr, q = nullptr;
701 #ifdef HAVE_DLOPEN
703 PRLibrary *lib = nullptr;
704 p = PR_FindFunctionSymbolAndLibrary("__log_addref", &lib);
705 if (lib) {
706 PR_UnloadLibrary(lib);
707 lib = nullptr;
709 q = PR_FindFunctionSymbolAndLibrary("__log_release", &lib);
710 if (lib) {
711 PR_UnloadLibrary(lib);
714 #endif
715 if (p && q) {
716 leakyLogAddRef = (void (*)(void*,int,int)) p;
717 leakyLogRelease = (void (*)(void*,int,int)) q;
719 else {
720 gLogToLeaky = false;
721 fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
722 fflush(stdout);
726 const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
728 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
729 if (classes) {
730 (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
731 } else {
732 if (getenv("XPCOM_MEM_COMPTR_LOG")) {
733 fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
736 #else
737 const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
738 if (comptr_log) {
739 fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
741 #endif
743 if (classes) {
744 // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
745 // as a list of class names to track
746 gTypesToLog = PL_NewHashTable(256,
747 PL_HashString,
748 PL_CompareStrings,
749 PL_CompareValues,
750 &typesToLogHashAllocOps, nullptr);
751 if (!gTypesToLog) {
752 NS_WARNING("out of memory");
753 fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
755 else {
756 fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
757 const char* cp = classes;
758 for (;;) {
759 char* cm = (char*) strchr(cp, ',');
760 if (cm) {
761 *cm = '\0';
763 PL_HashTableAdd(gTypesToLog, strdup(cp), (void*)1);
764 fprintf(stdout, "%s ", cp);
765 if (!cm) break;
766 *cm = ',';
767 cp = cm + 1;
769 fprintf(stdout, "\n");
772 gSerialNumbers = PL_NewHashTable(256,
773 HashNumber,
774 PL_CompareValues,
775 PL_CompareValues,
776 &serialNumberHashAllocOps, nullptr);
781 const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
782 if (objects) {
783 gObjectsToLog = PL_NewHashTable(256,
784 HashNumber,
785 PL_CompareValues,
786 PL_CompareValues,
787 nullptr, nullptr);
789 if (!gObjectsToLog) {
790 NS_WARNING("out of memory");
791 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
793 else if (! (gRefcntsLog || gAllocLog || gCOMPtrLog)) {
794 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
796 else {
797 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
798 const char* cp = objects;
799 for (;;) {
800 char* cm = (char*) strchr(cp, ',');
801 if (cm) {
802 *cm = '\0';
804 intptr_t top = 0;
805 intptr_t bottom = 0;
806 while (*cp) {
807 if (*cp == '-') {
808 bottom = top;
809 top = 0;
810 ++cp;
812 top *= 10;
813 top += *cp - '0';
814 ++cp;
816 if (!bottom) {
817 bottom = top;
819 for (intptr_t serialno = bottom; serialno <= top; serialno++) {
820 PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
821 fprintf(stdout, "%" PRIdPTR " ", serialno);
823 if (!cm) break;
824 *cm = ',';
825 cp = cm + 1;
827 fprintf(stdout, "\n");
832 if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog || gCOMPtrLog) {
833 gLogging = true;
836 gTraceLock = PR_NewLock();
839 #endif
841 extern "C" {
843 #ifdef STACKWALKING_AVAILABLE
844 static void PrintStackFrame(void *aPC, void *aSP, void *aClosure)
846 FILE *stream = (FILE*)aClosure;
847 nsCodeAddressDetails details;
848 char buf[1024];
850 NS_DescribeCodeAddress(aPC, &details);
851 NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf));
852 fputs(buf, stream);
854 #endif
858 void
859 nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
861 #ifdef STACKWALKING_AVAILABLE
862 NS_StackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, aStream,
863 0, nullptr);
864 #endif
867 //----------------------------------------------------------------------
869 // This thing is exported by libstdc++
870 // Yes, this is a gcc only hack
871 #if defined(MOZ_DEMANGLE_SYMBOLS)
872 #include <cxxabi.h>
873 #include <stdlib.h> // for free()
874 #endif // MOZ_DEMANGLE_SYMBOLS
876 void
877 nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol,
878 char * aBuffer,
879 int aBufLen)
881 NS_ASSERTION(nullptr != aSymbol,"null symbol");
882 NS_ASSERTION(nullptr != aBuffer,"null buffer");
883 NS_ASSERTION(aBufLen >= 32 ,"pulled 32 out of you know where");
885 aBuffer[0] = '\0';
887 #if defined(MOZ_DEMANGLE_SYMBOLS)
888 /* See demangle.h in the gcc source for the voodoo */
889 char * demangled = abi::__cxa_demangle(aSymbol,0,0,0);
891 if (demangled)
893 strncpy(aBuffer,demangled,aBufLen);
894 free(demangled);
896 #endif // MOZ_DEMANGLE_SYMBOLS
900 //----------------------------------------------------------------------
902 EXPORT_XPCOM_API(void)
903 NS_LogInit()
905 // FIXME: This is called multiple times, we should probably not allow that.
906 #ifdef STACKWALKING_AVAILABLE
907 StackWalkInitCriticalAddress();
908 #endif
909 #ifdef NS_IMPL_REFCNT_LOGGING
910 if (++gInitCount)
911 nsTraceRefcntImpl::SetActivityIsLegal(true);
912 #endif
914 #ifdef NS_TRACE_MALLOC
915 // XXX we don't have to worry about shutting down trace-malloc; it
916 // handles this itself, through an atexit() callback.
917 if (!NS_TraceMallocHasStarted())
918 NS_TraceMallocStartup(-1); // -1 == no logging
919 #endif
922 EXPORT_XPCOM_API(void)
923 NS_LogTerm()
925 mozilla::LogTerm();
928 namespace mozilla {
929 void
930 LogTerm()
932 NS_ASSERTION(gInitCount > 0,
933 "NS_LogTerm without matching NS_LogInit");
935 if (--gInitCount == 0) {
936 #ifdef DEBUG
937 /* FIXME bug 491977: This is only going to operate on the
938 * BlockingResourceBase which is compiled into
939 * libxul/libxpcom_core.so. Anyone using external linkage will
940 * have their own copy of BlockingResourceBase statics which will
941 * not be freed by this method.
943 * It sounds like what we really want is to be able to register a
944 * callback function to call at XPCOM shutdown. Note that with
945 * this solution, however, we need to guarantee that
946 * BlockingResourceBase::Shutdown() runs after all other shutdown
947 * functions.
949 BlockingResourceBase::Shutdown();
950 #endif
952 if (gInitialized) {
953 nsTraceRefcntImpl::DumpStatistics();
954 nsTraceRefcntImpl::ResetStatistics();
956 nsTraceRefcntImpl::Shutdown();
957 #ifdef NS_IMPL_REFCNT_LOGGING
958 nsTraceRefcntImpl::SetActivityIsLegal(false);
959 gActivityTLS = BAD_TLS_INDEX;
960 #endif
964 } // namespace mozilla
966 EXPORT_XPCOM_API(void)
967 NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt,
968 const char* aClazz, uint32_t classSize)
970 #ifdef NS_IMPL_REFCNT_LOGGING
971 ASSERT_ACTIVITY_IS_LEGAL;
972 if (!gInitialized)
973 InitTraceLog();
974 if (gLogging) {
975 LOCK_TRACELOG();
977 if (gBloatLog) {
978 BloatEntry* entry = GetBloatEntry(aClazz, classSize);
979 if (entry) {
980 entry->AddRef(aRefcnt);
984 // Here's the case where MOZ_COUNT_CTOR was not used,
985 // yet we still want to see creation information:
987 bool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
988 intptr_t serialno = 0;
989 if (gSerialNumbers && loggingThisType) {
990 serialno = GetSerialNumber(aPtr, aRefcnt == 1);
991 NS_ASSERTION(serialno != 0,
992 "Serial number requested for unrecognized pointer! "
993 "Are you memmoving a refcounted object?");
994 int32_t* count = GetRefCount(aPtr);
995 if(count)
996 (*count)++;
1000 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1001 if (aRefcnt == 1 && gAllocLog && loggingThisType && loggingThisObject) {
1002 fprintf(gAllocLog, "\n<%s> 0x%08X %" PRIdPTR " Create\n",
1003 aClazz, NS_PTR_TO_INT32(aPtr), serialno);
1004 nsTraceRefcntImpl::WalkTheStack(gAllocLog);
1007 if (gRefcntsLog && loggingThisType && loggingThisObject) {
1008 if (gLogToLeaky) {
1009 (*leakyLogAddRef)(aPtr, aRefcnt - 1, aRefcnt);
1011 else {
1012 // Can't use PR_LOG(), b/c it truncates the line
1013 fprintf(gRefcntsLog,
1014 "\n<%s> 0x%08X %" PRIdPTR " AddRef %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
1015 nsTraceRefcntImpl::WalkTheStack(gRefcntsLog);
1016 fflush(gRefcntsLog);
1019 UNLOCK_TRACELOG();
1021 #endif
1024 EXPORT_XPCOM_API(void)
1025 NS_LogRelease(void* aPtr, nsrefcnt aRefcnt, const char* aClazz)
1027 #ifdef NS_IMPL_REFCNT_LOGGING
1028 ASSERT_ACTIVITY_IS_LEGAL;
1029 if (!gInitialized)
1030 InitTraceLog();
1031 if (gLogging) {
1032 LOCK_TRACELOG();
1034 if (gBloatLog) {
1035 BloatEntry* entry = GetBloatEntry(aClazz, 0);
1036 if (entry) {
1037 entry->Release(aRefcnt);
1041 bool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
1042 intptr_t serialno = 0;
1043 if (gSerialNumbers && loggingThisType) {
1044 serialno = GetSerialNumber(aPtr, false);
1045 NS_ASSERTION(serialno != 0,
1046 "Serial number requested for unrecognized pointer! "
1047 "Are you memmoving a refcounted object?");
1048 int32_t* count = GetRefCount(aPtr);
1049 if(count)
1050 (*count)--;
1054 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1055 if (gRefcntsLog && loggingThisType && loggingThisObject) {
1056 if (gLogToLeaky) {
1057 (*leakyLogRelease)(aPtr, aRefcnt + 1, aRefcnt);
1059 else {
1060 // Can't use PR_LOG(), b/c it truncates the line
1061 fprintf(gRefcntsLog,
1062 "\n<%s> 0x%08X %" PRIdPTR " Release %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
1063 nsTraceRefcntImpl::WalkTheStack(gRefcntsLog);
1064 fflush(gRefcntsLog);
1068 // Here's the case where MOZ_COUNT_DTOR was not used,
1069 // yet we still want to see deletion information:
1071 if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) {
1072 fprintf(gAllocLog,
1073 "\n<%s> 0x%08X %" PRIdPTR " Destroy\n",
1074 aClazz, NS_PTR_TO_INT32(aPtr), serialno);
1075 nsTraceRefcntImpl::WalkTheStack(gAllocLog);
1078 if (aRefcnt == 0 && gSerialNumbers && loggingThisType) {
1079 RecycleSerialNumberPtr(aPtr);
1082 UNLOCK_TRACELOG();
1084 #endif
1087 EXPORT_XPCOM_API(void)
1088 NS_LogCtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
1090 #ifdef NS_IMPL_REFCNT_LOGGING
1091 ASSERT_ACTIVITY_IS_LEGAL;
1092 if (!gInitialized)
1093 InitTraceLog();
1095 if (gLogging) {
1096 LOCK_TRACELOG();
1098 if (gBloatLog) {
1099 BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
1100 if (entry) {
1101 entry->Ctor();
1105 bool loggingThisType = (!gTypesToLog || LogThisType(aType));
1106 intptr_t serialno = 0;
1107 if (gSerialNumbers && loggingThisType) {
1108 serialno = GetSerialNumber(aPtr, true);
1111 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1112 if (gAllocLog && loggingThisType && loggingThisObject) {
1113 fprintf(gAllocLog, "\n<%s> 0x%08X %" PRIdPTR " Ctor (%d)\n",
1114 aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
1115 nsTraceRefcntImpl::WalkTheStack(gAllocLog);
1118 UNLOCK_TRACELOG();
1120 #endif
1124 EXPORT_XPCOM_API(void)
1125 NS_LogDtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
1127 #ifdef NS_IMPL_REFCNT_LOGGING
1128 ASSERT_ACTIVITY_IS_LEGAL;
1129 if (!gInitialized)
1130 InitTraceLog();
1132 if (gLogging) {
1133 LOCK_TRACELOG();
1135 if (gBloatLog) {
1136 BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
1137 if (entry) {
1138 entry->Dtor();
1142 bool loggingThisType = (!gTypesToLog || LogThisType(aType));
1143 intptr_t serialno = 0;
1144 if (gSerialNumbers && loggingThisType) {
1145 serialno = GetSerialNumber(aPtr, false);
1146 RecycleSerialNumberPtr(aPtr);
1149 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1151 // (If we're on a losing architecture, don't do this because we'll be
1152 // using LogDeleteXPCOM instead to get file and line numbers.)
1153 if (gAllocLog && loggingThisType && loggingThisObject) {
1154 fprintf(gAllocLog, "\n<%s> 0x%08X %" PRIdPTR " Dtor (%d)\n",
1155 aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
1156 nsTraceRefcntImpl::WalkTheStack(gAllocLog);
1159 UNLOCK_TRACELOG();
1161 #endif
1165 EXPORT_XPCOM_API(void)
1166 NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject)
1168 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1169 // Get the most-derived object.
1170 void *object = dynamic_cast<void *>(aObject);
1172 // This is a very indirect way of finding out what the class is
1173 // of the object being logged. If we're logging a specific type,
1174 // then
1175 if (!gTypesToLog || !gSerialNumbers) {
1176 return;
1178 intptr_t serialno = GetSerialNumber(object, false);
1179 if (serialno == 0) {
1180 return;
1183 if (!gInitialized)
1184 InitTraceLog();
1185 if (gLogging) {
1186 LOCK_TRACELOG();
1188 int32_t* count = GetCOMPtrCount(object);
1189 if(count)
1190 (*count)++;
1192 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1194 if (gCOMPtrLog && loggingThisObject) {
1195 fprintf(gCOMPtrLog, "\n<?> 0x%08X %" PRIdPTR " nsCOMPtrAddRef %d 0x%08X\n",
1196 NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
1197 nsTraceRefcntImpl::WalkTheStack(gCOMPtrLog);
1200 UNLOCK_TRACELOG();
1202 #endif
1206 EXPORT_XPCOM_API(void)
1207 NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject)
1209 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1210 // Get the most-derived object.
1211 void *object = dynamic_cast<void *>(aObject);
1213 // This is a very indirect way of finding out what the class is
1214 // of the object being logged. If we're logging a specific type,
1215 // then
1216 if (!gTypesToLog || !gSerialNumbers) {
1217 return;
1219 intptr_t serialno = GetSerialNumber(object, false);
1220 if (serialno == 0) {
1221 return;
1224 if (!gInitialized)
1225 InitTraceLog();
1226 if (gLogging) {
1227 LOCK_TRACELOG();
1229 int32_t* count = GetCOMPtrCount(object);
1230 if(count)
1231 (*count)--;
1233 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1235 if (gCOMPtrLog && loggingThisObject) {
1236 fprintf(gCOMPtrLog, "\n<?> 0x%08X %" PRIdPTR " nsCOMPtrRelease %d 0x%08X\n",
1237 NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
1238 nsTraceRefcntImpl::WalkTheStack(gCOMPtrLog);
1241 UNLOCK_TRACELOG();
1243 #endif
1246 void
1247 nsTraceRefcntImpl::Startup()
1251 static void maybeUnregisterAndCloseFile(FILE *&f) {
1252 if (!f)
1253 return;
1255 MozillaUnRegisterDebugFILE(f);
1256 fclose(f);
1257 f = nullptr;
1260 void
1261 nsTraceRefcntImpl::Shutdown()
1263 #ifdef NS_IMPL_REFCNT_LOGGING
1265 if (gBloatView) {
1266 PL_HashTableDestroy(gBloatView);
1267 gBloatView = nullptr;
1269 if (gTypesToLog) {
1270 PL_HashTableDestroy(gTypesToLog);
1271 gTypesToLog = nullptr;
1273 if (gObjectsToLog) {
1274 PL_HashTableDestroy(gObjectsToLog);
1275 gObjectsToLog = nullptr;
1277 if (gSerialNumbers) {
1278 PL_HashTableDestroy(gSerialNumbers);
1279 gSerialNumbers = nullptr;
1281 maybeUnregisterAndCloseFile(gBloatLog);
1282 maybeUnregisterAndCloseFile(gRefcntsLog);
1283 maybeUnregisterAndCloseFile(gAllocLog);
1284 maybeUnregisterAndCloseFile(gLeakyLog);
1285 maybeUnregisterAndCloseFile(gCOMPtrLog);
1286 #endif
1289 void
1290 nsTraceRefcntImpl::SetActivityIsLegal(bool aLegal)
1292 #ifdef NS_IMPL_REFCNT_LOGGING
1293 if (gActivityTLS == BAD_TLS_INDEX)
1294 PR_NewThreadPrivateIndex(&gActivityTLS, nullptr);
1296 PR_SetThreadPrivate(gActivityTLS, NS_INT32_TO_PTR(!aLegal));
1297 #endif
1300 NS_IMPL_QUERY_INTERFACE1(nsTraceRefcntImpl, nsITraceRefcnt)
1302 NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::AddRef(void)
1304 return 2;
1307 NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::Release(void)
1309 return 1;
1312 NS_IMETHODIMP
1313 nsTraceRefcntImpl::LogAddRef(void *aPtr, nsrefcnt aNewRefcnt,
1314 const char *aTypeName, uint32_t aSize)
1316 NS_LogAddRef(aPtr, aNewRefcnt, aTypeName, aSize);
1317 return NS_OK;
1320 NS_IMETHODIMP
1321 nsTraceRefcntImpl::LogRelease(void *aPtr, nsrefcnt aNewRefcnt,
1322 const char *aTypeName)
1324 NS_LogRelease(aPtr, aNewRefcnt, aTypeName);
1325 return NS_OK;
1328 NS_IMETHODIMP
1329 nsTraceRefcntImpl::LogCtor(void *aPtr, const char *aTypeName, uint32_t aSize)
1331 NS_LogCtor(aPtr, aTypeName, aSize);
1332 return NS_OK;
1335 NS_IMETHODIMP
1336 nsTraceRefcntImpl::LogDtor(void *aPtr, const char *aTypeName, uint32_t aSize)
1338 NS_LogDtor(aPtr, aTypeName, aSize);
1339 return NS_OK;
1342 NS_IMETHODIMP
1343 nsTraceRefcntImpl::LogAddCOMPtr(void *aCOMPtr, nsISupports* aObject)
1345 NS_LogCOMPtrAddRef(aCOMPtr, aObject);
1346 return NS_OK;
1349 NS_IMETHODIMP
1350 nsTraceRefcntImpl::LogReleaseCOMPtr(void *aCOMPtr, nsISupports* aObject)
1352 NS_LogCOMPtrRelease(aCOMPtr, aObject);
1353 return NS_OK;
1356 static const nsTraceRefcntImpl kTraceRefcntImpl;
1358 NS_METHOD
1359 nsTraceRefcntImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
1361 return const_cast<nsTraceRefcntImpl*>(&kTraceRefcntImpl)->
1362 QueryInterface(aIID, aInstancePtr);