|
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/. */ |
|
5 |
|
6 #include "nsTraceRefcnt.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" |
|
20 |
|
21 #include "nsXULAppAPI.h" |
|
22 #ifdef XP_WIN |
|
23 #include <process.h> |
|
24 #define getpid _getpid |
|
25 #else |
|
26 #include <unistd.h> |
|
27 #endif |
|
28 |
|
29 #ifdef NS_TRACE_MALLOC |
|
30 #include "nsTraceMalloc.h" |
|
31 #endif |
|
32 |
|
33 #include "mozilla/BlockingResourceBase.h" |
|
34 #include "mozilla/PoisonIOInterposer.h" |
|
35 |
|
36 #ifdef HAVE_DLOPEN |
|
37 #include <dlfcn.h> |
|
38 #endif |
|
39 |
|
40 //////////////////////////////////////////////////////////////////////////////// |
|
41 |
|
42 void |
|
43 NS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues, |
|
44 double *meanResult, double *stdDevResult) |
|
45 { |
|
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; |
|
56 } |
|
57 *meanResult = mean; |
|
58 *stdDevResult = stdDev; |
|
59 } |
|
60 |
|
61 //////////////////////////////////////////////////////////////////////////////// |
|
62 |
|
63 #if !defined(XP_WIN) || (!defined(MOZ_OPTIMIZE) || defined(MOZ_PROFILING) || defined(DEBUG)) |
|
64 #define STACKWALKING_AVAILABLE |
|
65 #endif |
|
66 |
|
67 #define NS_IMPL_REFCNT_LOGGING |
|
68 |
|
69 #ifdef NS_IMPL_REFCNT_LOGGING |
|
70 #include "plhash.h" |
|
71 #include "prmem.h" |
|
72 |
|
73 #include "prlock.h" |
|
74 |
|
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; |
|
78 |
|
79 #define LOCK_TRACELOG() PR_Lock(gTraceLock) |
|
80 #define UNLOCK_TRACELOG() PR_Unlock(gTraceLock) |
|
81 |
|
82 static PLHashTable* gBloatView; |
|
83 static PLHashTable* gTypesToLog; |
|
84 static PLHashTable* gObjectsToLog; |
|
85 static PLHashTable* gSerialNumbers; |
|
86 static intptr_t gNextSerialNumber; |
|
87 |
|
88 static bool gLogging; |
|
89 static bool gLogToLeaky; |
|
90 static bool gLogLeaksOnly; |
|
91 |
|
92 static void (*leakyLogAddRef)(void* p, int oldrc, int newrc); |
|
93 static void (*leakyLogRelease)(void* p, int oldrc, int newrc); |
|
94 |
|
95 #define BAD_TLS_INDEX ((unsigned) -1) |
|
96 |
|
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; |
|
102 |
|
103 static bool gInitialized; |
|
104 static nsrefcnt gInitCount; |
|
105 |
|
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; |
|
111 |
|
112 struct serialNumberRecord { |
|
113 intptr_t serialNumber; |
|
114 int32_t refCount; |
|
115 int32_t COMPtrCount; |
|
116 }; |
|
117 |
|
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; |
|
127 }; |
|
128 |
|
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"; |
|
133 |
|
134 static void |
|
135 AssertActivityIsLegal() |
|
136 { |
|
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); |
|
143 } |
|
144 } |
|
145 } |
|
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 |
|
153 |
|
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. |
|
157 |
|
158 static void * |
|
159 DefaultAllocTable(void *pool, size_t size) |
|
160 { |
|
161 return PR_MALLOC(size); |
|
162 } |
|
163 |
|
164 static void |
|
165 DefaultFreeTable(void *pool, void *item) |
|
166 { |
|
167 PR_Free(item); |
|
168 } |
|
169 |
|
170 static PLHashEntry * |
|
171 DefaultAllocEntry(void *pool, const void *key) |
|
172 { |
|
173 return PR_NEW(PLHashEntry); |
|
174 } |
|
175 |
|
176 static void |
|
177 SerialNumberFreeEntry(void *pool, PLHashEntry *he, unsigned flag) |
|
178 { |
|
179 if (flag == HT_FREE_ENTRY) { |
|
180 PR_Free(reinterpret_cast<serialNumberRecord*>(he->value)); |
|
181 PR_Free(he); |
|
182 } |
|
183 } |
|
184 |
|
185 static void |
|
186 TypesToLogFreeEntry(void *pool, PLHashEntry *he, unsigned flag) |
|
187 { |
|
188 if (flag == HT_FREE_ENTRY) { |
|
189 free(const_cast<char*>(reinterpret_cast<const char*>(he->key))); |
|
190 PR_Free(he); |
|
191 } |
|
192 } |
|
193 |
|
194 static const PLHashAllocOps serialNumberHashAllocOps = { |
|
195 DefaultAllocTable, DefaultFreeTable, |
|
196 DefaultAllocEntry, SerialNumberFreeEntry |
|
197 }; |
|
198 |
|
199 static const PLHashAllocOps typesToLogHashAllocOps = { |
|
200 DefaultAllocTable, DefaultFreeTable, |
|
201 DefaultAllocEntry, TypesToLogFreeEntry |
|
202 }; |
|
203 |
|
204 //////////////////////////////////////////////////////////////////////////////// |
|
205 |
|
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; |
|
214 } |
|
215 |
|
216 ~BloatEntry() { |
|
217 PL_strfree(mClassName); |
|
218 } |
|
219 |
|
220 uint32_t GetClassSize() { return (uint32_t)mClassSize; } |
|
221 const char* GetClassName() { return mClassName; } |
|
222 |
|
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; |
|
232 } |
|
233 |
|
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); |
|
244 } |
|
245 |
|
246 void AddRef(nsrefcnt refcnt) { |
|
247 mNewStats.mAddRefs++; |
|
248 if (refcnt == 1) { |
|
249 Ctor(); |
|
250 } |
|
251 AccountRefs(); |
|
252 } |
|
253 |
|
254 void Release(nsrefcnt refcnt) { |
|
255 mNewStats.mReleases++; |
|
256 if (refcnt == 0) { |
|
257 Dtor(); |
|
258 } |
|
259 AccountRefs(); |
|
260 } |
|
261 |
|
262 void Ctor() { |
|
263 mNewStats.mCreates++; |
|
264 AccountObjs(); |
|
265 } |
|
266 |
|
267 void Dtor() { |
|
268 mNewStats.mDestroys++; |
|
269 AccountObjs(); |
|
270 } |
|
271 |
|
272 void AccountRefs() { |
|
273 uint64_t cnt = (mNewStats.mAddRefs - mNewStats.mReleases); |
|
274 mNewStats.mRefsOutstandingTotal += cnt; |
|
275 mNewStats.mRefsOutstandingSquared += cnt * cnt; |
|
276 } |
|
277 |
|
278 void AccountObjs() { |
|
279 uint64_t cnt = (mNewStats.mCreates - mNewStats.mDestroys); |
|
280 mNewStats.mObjsOutstandingTotal += cnt; |
|
281 mNewStats.mObjsOutstandingSquared += cnt * cnt; |
|
282 } |
|
283 |
|
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); |
|
289 } |
|
290 return HT_ENUMERATE_NEXT; |
|
291 } |
|
292 |
|
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); |
|
297 } |
|
298 return HT_ENUMERATE_NEXT; |
|
299 } |
|
300 |
|
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))); |
|
315 } |
|
316 |
|
317 void DumpTotal(FILE* out) { |
|
318 mClassSize /= mAllStats.mCreates; |
|
319 Dump(-1, out, nsTraceRefcnt::ALL_STATS); |
|
320 } |
|
321 |
|
322 static bool HaveLeaks(nsTraceRefcntStats* stats) { |
|
323 return ((stats->mAddRefs != stats->mReleases) || |
|
324 (stats->mCreates != stats->mDestroys)); |
|
325 } |
|
326 |
|
327 bool PrintDumpHeader(FILE* out, const char* msg, nsTraceRefcnt::StatisticsType type) { |
|
328 fprintf(out, "\n== BloatView: %s, %s process %d\n", msg, |
|
329 XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid()); |
|
330 nsTraceRefcntStats& stats = |
|
331 (type == nsTraceRefcnt::NEW_STATS) ? mNewStats : mAllStats; |
|
332 if (gLogLeaksOnly && !HaveLeaks(&stats)) |
|
333 return false; |
|
334 |
|
335 fprintf(out, |
|
336 "\n" \ |
|
337 " |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n" \ |
|
338 " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n"); |
|
339 |
|
340 this->DumpTotal(out); |
|
341 |
|
342 return true; |
|
343 } |
|
344 |
|
345 void Dump(int i, FILE* out, nsTraceRefcnt::StatisticsType type) { |
|
346 nsTraceRefcntStats* stats = (type == nsTraceRefcnt::NEW_STATS) ? &mNewStats : &mAllStats; |
|
347 if (gLogLeaksOnly && !HaveLeaks(stats)) { |
|
348 return; |
|
349 } |
|
350 |
|
351 double meanRefs, stddevRefs; |
|
352 NS_MeanAndStdDev(stats->mAddRefs + stats->mReleases, |
|
353 stats->mRefsOutstandingTotal, |
|
354 stats->mRefsOutstandingSquared, |
|
355 &meanRefs, &stddevRefs); |
|
356 |
|
357 double meanObjs, stddevObjs; |
|
358 NS_MeanAndStdDev(stats->mCreates + stats->mDestroys, |
|
359 stats->mObjsOutstandingTotal, |
|
360 stats->mObjsOutstandingSquared, |
|
361 &meanObjs, &stddevObjs); |
|
362 |
|
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); |
|
385 } |
|
386 } |
|
387 |
|
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; |
|
394 }; |
|
395 |
|
396 static void |
|
397 BloatViewFreeEntry(void *pool, PLHashEntry *he, unsigned flag) |
|
398 { |
|
399 if (flag == HT_FREE_ENTRY) { |
|
400 BloatEntry* entry = reinterpret_cast<BloatEntry*>(he->value); |
|
401 delete entry; |
|
402 PR_Free(he); |
|
403 } |
|
404 } |
|
405 |
|
406 const static PLHashAllocOps bloatViewHashAllocOps = { |
|
407 DefaultAllocTable, DefaultFreeTable, |
|
408 DefaultAllocEntry, BloatViewFreeEntry |
|
409 }; |
|
410 |
|
411 static void |
|
412 RecreateBloatView() |
|
413 { |
|
414 gBloatView = PL_NewHashTable(256, |
|
415 PL_HashString, |
|
416 PL_CompareStrings, |
|
417 PL_CompareValues, |
|
418 &bloatViewHashAllocOps, nullptr); |
|
419 } |
|
420 |
|
421 static BloatEntry* |
|
422 GetBloatEntry(const char* aTypeName, uint32_t aInstanceSize) |
|
423 { |
|
424 if (!gBloatView) { |
|
425 RecreateBloatView(); |
|
426 } |
|
427 BloatEntry* entry = nullptr; |
|
428 if (gBloatView) { |
|
429 entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName); |
|
430 if (entry == nullptr && aInstanceSize > 0) { |
|
431 |
|
432 entry = new BloatEntry(aTypeName, aInstanceSize); |
|
433 PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry); |
|
434 if (e == nullptr) { |
|
435 delete entry; |
|
436 entry = nullptr; |
|
437 } |
|
438 } else { |
|
439 NS_ASSERTION(aInstanceSize == 0 || |
|
440 entry->GetClassSize() == aInstanceSize, |
|
441 "bad size recorded"); |
|
442 } |
|
443 } |
|
444 return entry; |
|
445 } |
|
446 |
|
447 static int DumpSerialNumbers(PLHashEntry* aHashEntry, int aIndex, void* aClosure) |
|
448 { |
|
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; |
|
465 } |
|
466 |
|
467 |
|
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; |
|
473 } |
|
474 bool LessThan(BloatEntry* const& aA, BloatEntry* const& aB) const { |
|
475 return PL_strcmp(aA->GetClassName(), aB->GetClassName()) < 0; |
|
476 } |
|
477 }; |
|
478 |
|
479 #endif /* NS_IMPL_REFCNT_LOGGING */ |
|
480 |
|
481 nsresult |
|
482 nsTraceRefcnt::DumpStatistics(StatisticsType type, FILE* out) |
|
483 { |
|
484 #ifdef NS_IMPL_REFCNT_LOGGING |
|
485 if (gBloatLog == nullptr || gBloatView == nullptr) { |
|
486 return NS_ERROR_FAILURE; |
|
487 } |
|
488 if (out == nullptr) { |
|
489 out = gBloatLog; |
|
490 } |
|
491 |
|
492 LOCK_TRACELOG(); |
|
493 |
|
494 bool wasLogging = gLogging; |
|
495 gLogging = false; // turn off logging for this method |
|
496 |
|
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"; |
|
505 } |
|
506 else { |
|
507 if (gLogLeaksOnly) |
|
508 msg = "ALL (cumulative) LEAK STATISTICS"; |
|
509 else |
|
510 msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS"; |
|
511 } |
|
512 const bool leaked = total.PrintDumpHeader(out, msg, type); |
|
513 |
|
514 nsTArray<BloatEntry*> entries; |
|
515 PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries); |
|
516 const uint32_t count = entries.Length(); |
|
517 |
|
518 if (!gLogLeaksOnly || leaked) { |
|
519 // Sort the entries alphabetically by classname. |
|
520 entries.Sort(); |
|
521 |
|
522 for (uint32_t i = 0; i < count; ++i) { |
|
523 BloatEntry* entry = entries[i]; |
|
524 entry->Dump(i, out, type); |
|
525 } |
|
526 |
|
527 fprintf(out, "\n"); |
|
528 } |
|
529 |
|
530 fprintf(out, "nsTraceRefcnt::DumpStatistics: %d entries\n", count); |
|
531 |
|
532 if (gSerialNumbers) { |
|
533 fprintf(out, "\nSerial Numbers of Leaked Objects:\n"); |
|
534 PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, out); |
|
535 } |
|
536 |
|
537 gLogging = wasLogging; |
|
538 UNLOCK_TRACELOG(); |
|
539 #endif |
|
540 |
|
541 return NS_OK; |
|
542 } |
|
543 |
|
544 void |
|
545 nsTraceRefcnt::ResetStatistics() |
|
546 { |
|
547 #ifdef NS_IMPL_REFCNT_LOGGING |
|
548 LOCK_TRACELOG(); |
|
549 if (gBloatView) { |
|
550 PL_HashTableDestroy(gBloatView); |
|
551 gBloatView = nullptr; |
|
552 } |
|
553 UNLOCK_TRACELOG(); |
|
554 #endif |
|
555 } |
|
556 |
|
557 #ifdef NS_IMPL_REFCNT_LOGGING |
|
558 static bool LogThisType(const char* aTypeName) |
|
559 { |
|
560 void* he = PL_HashTableLookup(gTypesToLog, aTypeName); |
|
561 return nullptr != he; |
|
562 } |
|
563 |
|
564 static intptr_t GetSerialNumber(void* aPtr, bool aCreate) |
|
565 { |
|
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; |
|
569 } |
|
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; |
|
577 } |
|
578 else { |
|
579 return 0; |
|
580 } |
|
581 } |
|
582 |
|
583 static int32_t* GetRefCount(void* aPtr) |
|
584 { |
|
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; |
|
590 } |
|
591 } |
|
592 |
|
593 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR) |
|
594 static int32_t* GetCOMPtrCount(void* aPtr) |
|
595 { |
|
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; |
|
601 } |
|
602 } |
|
603 #endif |
|
604 |
|
605 static void RecycleSerialNumberPtr(void* aPtr) |
|
606 { |
|
607 PL_HashTableRemove(gSerialNumbers, aPtr); |
|
608 } |
|
609 |
|
610 static bool LogThisObj(intptr_t aSerialNumber) |
|
611 { |
|
612 return nullptr != PL_HashTableLookup(gObjectsToLog, (const void*)aSerialNumber); |
|
613 } |
|
614 |
|
615 #ifdef XP_WIN |
|
616 #define FOPEN_NO_INHERIT "N" |
|
617 #else |
|
618 #define FOPEN_NO_INHERIT |
|
619 #endif |
|
620 |
|
621 static bool InitLog(const char* envVar, const char* msg, FILE* *result) |
|
622 { |
|
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; |
|
630 } |
|
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; |
|
636 } |
|
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"); |
|
651 } |
|
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()); |
|
658 } |
|
659 else { |
|
660 fprintf(stdout, "### %s defined -- unable to log %s to %s\n", |
|
661 envVar, msg, fname.get()); |
|
662 } |
|
663 return stream != nullptr; |
|
664 } |
|
665 } |
|
666 return false; |
|
667 } |
|
668 |
|
669 |
|
670 static PLHashNumber HashNumber(const void* aKey) |
|
671 { |
|
672 return PLHashNumber(NS_PTR_TO_INT32(aKey)); |
|
673 } |
|
674 |
|
675 static void InitTraceLog(void) |
|
676 { |
|
677 if (gInitialized) return; |
|
678 gInitialized = true; |
|
679 |
|
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; |
|
690 } |
|
691 } |
|
692 |
|
693 (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog); |
|
694 |
|
695 (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog); |
|
696 |
|
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 |
|
702 { |
|
703 PRLibrary *lib = nullptr; |
|
704 p = PR_FindFunctionSymbolAndLibrary("__log_addref", &lib); |
|
705 if (lib) { |
|
706 PR_UnloadLibrary(lib); |
|
707 lib = nullptr; |
|
708 } |
|
709 q = PR_FindFunctionSymbolAndLibrary("__log_release", &lib); |
|
710 if (lib) { |
|
711 PR_UnloadLibrary(lib); |
|
712 } |
|
713 } |
|
714 #endif |
|
715 if (p && q) { |
|
716 leakyLogAddRef = (void (*)(void*,int,int)) p; |
|
717 leakyLogRelease = (void (*)(void*,int,int)) q; |
|
718 } |
|
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); |
|
723 } |
|
724 } |
|
725 |
|
726 const char* classes = getenv("XPCOM_MEM_LOG_CLASSES"); |
|
727 |
|
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"); |
|
734 } |
|
735 } |
|
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"); |
|
740 } |
|
741 #endif |
|
742 |
|
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"); |
|
754 } |
|
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'; |
|
762 } |
|
763 PL_HashTableAdd(gTypesToLog, strdup(cp), (void*)1); |
|
764 fprintf(stdout, "%s ", cp); |
|
765 if (!cm) break; |
|
766 *cm = ','; |
|
767 cp = cm + 1; |
|
768 } |
|
769 fprintf(stdout, "\n"); |
|
770 } |
|
771 |
|
772 gSerialNumbers = PL_NewHashTable(256, |
|
773 HashNumber, |
|
774 PL_CompareValues, |
|
775 PL_CompareValues, |
|
776 &serialNumberHashAllocOps, nullptr); |
|
777 |
|
778 |
|
779 } |
|
780 |
|
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); |
|
788 |
|
789 if (!gObjectsToLog) { |
|
790 NS_WARNING("out of memory"); |
|
791 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n"); |
|
792 } |
|
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"); |
|
795 } |
|
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'; |
|
803 } |
|
804 intptr_t top = 0; |
|
805 intptr_t bottom = 0; |
|
806 while (*cp) { |
|
807 if (*cp == '-') { |
|
808 bottom = top; |
|
809 top = 0; |
|
810 ++cp; |
|
811 } |
|
812 top *= 10; |
|
813 top += *cp - '0'; |
|
814 ++cp; |
|
815 } |
|
816 if (!bottom) { |
|
817 bottom = top; |
|
818 } |
|
819 for (intptr_t serialno = bottom; serialno <= top; serialno++) { |
|
820 PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1); |
|
821 fprintf(stdout, "%" PRIdPTR " ", serialno); |
|
822 } |
|
823 if (!cm) break; |
|
824 *cm = ','; |
|
825 cp = cm + 1; |
|
826 } |
|
827 fprintf(stdout, "\n"); |
|
828 } |
|
829 } |
|
830 |
|
831 |
|
832 if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog || gCOMPtrLog) { |
|
833 gLogging = true; |
|
834 } |
|
835 |
|
836 gTraceLock = PR_NewLock(); |
|
837 } |
|
838 |
|
839 #endif |
|
840 |
|
841 extern "C" { |
|
842 |
|
843 #ifdef STACKWALKING_AVAILABLE |
|
844 static void PrintStackFrame(void *aPC, void *aSP, void *aClosure) |
|
845 { |
|
846 FILE *stream = (FILE*)aClosure; |
|
847 nsCodeAddressDetails details; |
|
848 char buf[1024]; |
|
849 |
|
850 NS_DescribeCodeAddress(aPC, &details); |
|
851 NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf)); |
|
852 fputs(buf, stream); |
|
853 } |
|
854 #endif |
|
855 |
|
856 } |
|
857 |
|
858 void |
|
859 nsTraceRefcnt::WalkTheStack(FILE* aStream) |
|
860 { |
|
861 #ifdef STACKWALKING_AVAILABLE |
|
862 NS_StackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, aStream, |
|
863 0, nullptr); |
|
864 #endif |
|
865 } |
|
866 |
|
867 //---------------------------------------------------------------------- |
|
868 |
|
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 |
|
875 |
|
876 void |
|
877 nsTraceRefcnt::DemangleSymbol(const char * aSymbol, |
|
878 char * aBuffer, |
|
879 int aBufLen) |
|
880 { |
|
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"); |
|
884 |
|
885 aBuffer[0] = '\0'; |
|
886 |
|
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); |
|
890 |
|
891 if (demangled) |
|
892 { |
|
893 strncpy(aBuffer,demangled,aBufLen); |
|
894 free(demangled); |
|
895 } |
|
896 #endif // MOZ_DEMANGLE_SYMBOLS |
|
897 } |
|
898 |
|
899 |
|
900 //---------------------------------------------------------------------- |
|
901 |
|
902 EXPORT_XPCOM_API(void) |
|
903 NS_LogInit() |
|
904 { |
|
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 nsTraceRefcnt::SetActivityIsLegal(true); |
|
912 #endif |
|
913 |
|
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 |
|
920 } |
|
921 |
|
922 EXPORT_XPCOM_API(void) |
|
923 NS_LogTerm() |
|
924 { |
|
925 mozilla::LogTerm(); |
|
926 } |
|
927 |
|
928 namespace mozilla { |
|
929 void |
|
930 LogTerm() |
|
931 { |
|
932 NS_ASSERTION(gInitCount > 0, |
|
933 "NS_LogTerm without matching NS_LogInit"); |
|
934 |
|
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. |
|
942 * |
|
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. |
|
948 */ |
|
949 BlockingResourceBase::Shutdown(); |
|
950 #endif |
|
951 |
|
952 if (gInitialized) { |
|
953 nsTraceRefcnt::DumpStatistics(); |
|
954 nsTraceRefcnt::ResetStatistics(); |
|
955 } |
|
956 nsTraceRefcnt::Shutdown(); |
|
957 #ifdef NS_IMPL_REFCNT_LOGGING |
|
958 nsTraceRefcnt::SetActivityIsLegal(false); |
|
959 gActivityTLS = BAD_TLS_INDEX; |
|
960 #endif |
|
961 } |
|
962 } |
|
963 |
|
964 } // namespace mozilla |
|
965 |
|
966 EXPORT_XPCOM_API(void) |
|
967 NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt, |
|
968 const char* aClazz, uint32_t classSize) |
|
969 { |
|
970 #ifdef NS_IMPL_REFCNT_LOGGING |
|
971 ASSERT_ACTIVITY_IS_LEGAL; |
|
972 if (!gInitialized) |
|
973 InitTraceLog(); |
|
974 if (gLogging) { |
|
975 LOCK_TRACELOG(); |
|
976 |
|
977 if (gBloatLog) { |
|
978 BloatEntry* entry = GetBloatEntry(aClazz, classSize); |
|
979 if (entry) { |
|
980 entry->AddRef(aRefcnt); |
|
981 } |
|
982 } |
|
983 |
|
984 // Here's the case where MOZ_COUNT_CTOR was not used, |
|
985 // yet we still want to see creation information: |
|
986 |
|
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)++; |
|
997 |
|
998 } |
|
999 |
|
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 nsTraceRefcnt::WalkTheStack(gAllocLog); |
|
1005 } |
|
1006 |
|
1007 if (gRefcntsLog && loggingThisType && loggingThisObject) { |
|
1008 if (gLogToLeaky) { |
|
1009 (*leakyLogAddRef)(aPtr, aRefcnt - 1, aRefcnt); |
|
1010 } |
|
1011 else { |
|
1012 // Can't use PR_LOG(), b/c it truncates the line |
|
1013 fprintf(gRefcntsLog, |
|
1014 "\n<%s> 0x%08X %" PRIuPTR " AddRef %" PRIuPTR "\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt); |
|
1015 nsTraceRefcnt::WalkTheStack(gRefcntsLog); |
|
1016 fflush(gRefcntsLog); |
|
1017 } |
|
1018 } |
|
1019 UNLOCK_TRACELOG(); |
|
1020 } |
|
1021 #endif |
|
1022 } |
|
1023 |
|
1024 EXPORT_XPCOM_API(void) |
|
1025 NS_LogRelease(void* aPtr, nsrefcnt aRefcnt, const char* aClazz) |
|
1026 { |
|
1027 #ifdef NS_IMPL_REFCNT_LOGGING |
|
1028 ASSERT_ACTIVITY_IS_LEGAL; |
|
1029 if (!gInitialized) |
|
1030 InitTraceLog(); |
|
1031 if (gLogging) { |
|
1032 LOCK_TRACELOG(); |
|
1033 |
|
1034 if (gBloatLog) { |
|
1035 BloatEntry* entry = GetBloatEntry(aClazz, 0); |
|
1036 if (entry) { |
|
1037 entry->Release(aRefcnt); |
|
1038 } |
|
1039 } |
|
1040 |
|
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)--; |
|
1051 |
|
1052 } |
|
1053 |
|
1054 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); |
|
1055 if (gRefcntsLog && loggingThisType && loggingThisObject) { |
|
1056 if (gLogToLeaky) { |
|
1057 (*leakyLogRelease)(aPtr, aRefcnt + 1, aRefcnt); |
|
1058 } |
|
1059 else { |
|
1060 // Can't use PR_LOG(), b/c it truncates the line |
|
1061 fprintf(gRefcntsLog, |
|
1062 "\n<%s> 0x%08X %" PRIuPTR " Release %" PRIuPTR "\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt); |
|
1063 nsTraceRefcnt::WalkTheStack(gRefcntsLog); |
|
1064 fflush(gRefcntsLog); |
|
1065 } |
|
1066 } |
|
1067 |
|
1068 // Here's the case where MOZ_COUNT_DTOR was not used, |
|
1069 // yet we still want to see deletion information: |
|
1070 |
|
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 nsTraceRefcnt::WalkTheStack(gAllocLog); |
|
1076 } |
|
1077 |
|
1078 if (aRefcnt == 0 && gSerialNumbers && loggingThisType) { |
|
1079 RecycleSerialNumberPtr(aPtr); |
|
1080 } |
|
1081 |
|
1082 UNLOCK_TRACELOG(); |
|
1083 } |
|
1084 #endif |
|
1085 } |
|
1086 |
|
1087 EXPORT_XPCOM_API(void) |
|
1088 NS_LogCtor(void* aPtr, const char* aType, uint32_t aInstanceSize) |
|
1089 { |
|
1090 #ifdef NS_IMPL_REFCNT_LOGGING |
|
1091 ASSERT_ACTIVITY_IS_LEGAL; |
|
1092 if (!gInitialized) |
|
1093 InitTraceLog(); |
|
1094 |
|
1095 if (gLogging) { |
|
1096 LOCK_TRACELOG(); |
|
1097 |
|
1098 if (gBloatLog) { |
|
1099 BloatEntry* entry = GetBloatEntry(aType, aInstanceSize); |
|
1100 if (entry) { |
|
1101 entry->Ctor(); |
|
1102 } |
|
1103 } |
|
1104 |
|
1105 bool loggingThisType = (!gTypesToLog || LogThisType(aType)); |
|
1106 intptr_t serialno = 0; |
|
1107 if (gSerialNumbers && loggingThisType) { |
|
1108 serialno = GetSerialNumber(aPtr, true); |
|
1109 } |
|
1110 |
|
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 nsTraceRefcnt::WalkTheStack(gAllocLog); |
|
1116 } |
|
1117 |
|
1118 UNLOCK_TRACELOG(); |
|
1119 } |
|
1120 #endif |
|
1121 } |
|
1122 |
|
1123 |
|
1124 EXPORT_XPCOM_API(void) |
|
1125 NS_LogDtor(void* aPtr, const char* aType, uint32_t aInstanceSize) |
|
1126 { |
|
1127 #ifdef NS_IMPL_REFCNT_LOGGING |
|
1128 ASSERT_ACTIVITY_IS_LEGAL; |
|
1129 if (!gInitialized) |
|
1130 InitTraceLog(); |
|
1131 |
|
1132 if (gLogging) { |
|
1133 LOCK_TRACELOG(); |
|
1134 |
|
1135 if (gBloatLog) { |
|
1136 BloatEntry* entry = GetBloatEntry(aType, aInstanceSize); |
|
1137 if (entry) { |
|
1138 entry->Dtor(); |
|
1139 } |
|
1140 } |
|
1141 |
|
1142 bool loggingThisType = (!gTypesToLog || LogThisType(aType)); |
|
1143 intptr_t serialno = 0; |
|
1144 if (gSerialNumbers && loggingThisType) { |
|
1145 serialno = GetSerialNumber(aPtr, false); |
|
1146 RecycleSerialNumberPtr(aPtr); |
|
1147 } |
|
1148 |
|
1149 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); |
|
1150 |
|
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 nsTraceRefcnt::WalkTheStack(gAllocLog); |
|
1157 } |
|
1158 |
|
1159 UNLOCK_TRACELOG(); |
|
1160 } |
|
1161 #endif |
|
1162 } |
|
1163 |
|
1164 |
|
1165 EXPORT_XPCOM_API(void) |
|
1166 NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject) |
|
1167 { |
|
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); |
|
1171 |
|
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; |
|
1177 } |
|
1178 intptr_t serialno = GetSerialNumber(object, false); |
|
1179 if (serialno == 0) { |
|
1180 return; |
|
1181 } |
|
1182 |
|
1183 if (!gInitialized) |
|
1184 InitTraceLog(); |
|
1185 if (gLogging) { |
|
1186 LOCK_TRACELOG(); |
|
1187 |
|
1188 int32_t* count = GetCOMPtrCount(object); |
|
1189 if(count) |
|
1190 (*count)++; |
|
1191 |
|
1192 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); |
|
1193 |
|
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 nsTraceRefcnt::WalkTheStack(gCOMPtrLog); |
|
1198 } |
|
1199 |
|
1200 UNLOCK_TRACELOG(); |
|
1201 } |
|
1202 #endif |
|
1203 } |
|
1204 |
|
1205 |
|
1206 EXPORT_XPCOM_API(void) |
|
1207 NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject) |
|
1208 { |
|
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); |
|
1212 |
|
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; |
|
1218 } |
|
1219 intptr_t serialno = GetSerialNumber(object, false); |
|
1220 if (serialno == 0) { |
|
1221 return; |
|
1222 } |
|
1223 |
|
1224 if (!gInitialized) |
|
1225 InitTraceLog(); |
|
1226 if (gLogging) { |
|
1227 LOCK_TRACELOG(); |
|
1228 |
|
1229 int32_t* count = GetCOMPtrCount(object); |
|
1230 if(count) |
|
1231 (*count)--; |
|
1232 |
|
1233 bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); |
|
1234 |
|
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 nsTraceRefcnt::WalkTheStack(gCOMPtrLog); |
|
1239 } |
|
1240 |
|
1241 UNLOCK_TRACELOG(); |
|
1242 } |
|
1243 #endif |
|
1244 } |
|
1245 |
|
1246 void |
|
1247 nsTraceRefcnt::Startup() |
|
1248 { |
|
1249 } |
|
1250 |
|
1251 static void maybeUnregisterAndCloseFile(FILE *&f) { |
|
1252 if (!f) |
|
1253 return; |
|
1254 |
|
1255 MozillaUnRegisterDebugFILE(f); |
|
1256 fclose(f); |
|
1257 f = nullptr; |
|
1258 } |
|
1259 |
|
1260 void |
|
1261 nsTraceRefcnt::Shutdown() |
|
1262 { |
|
1263 #ifdef NS_IMPL_REFCNT_LOGGING |
|
1264 |
|
1265 if (gBloatView) { |
|
1266 PL_HashTableDestroy(gBloatView); |
|
1267 gBloatView = nullptr; |
|
1268 } |
|
1269 if (gTypesToLog) { |
|
1270 PL_HashTableDestroy(gTypesToLog); |
|
1271 gTypesToLog = nullptr; |
|
1272 } |
|
1273 if (gObjectsToLog) { |
|
1274 PL_HashTableDestroy(gObjectsToLog); |
|
1275 gObjectsToLog = nullptr; |
|
1276 } |
|
1277 if (gSerialNumbers) { |
|
1278 PL_HashTableDestroy(gSerialNumbers); |
|
1279 gSerialNumbers = nullptr; |
|
1280 } |
|
1281 maybeUnregisterAndCloseFile(gBloatLog); |
|
1282 maybeUnregisterAndCloseFile(gRefcntsLog); |
|
1283 maybeUnregisterAndCloseFile(gAllocLog); |
|
1284 maybeUnregisterAndCloseFile(gLeakyLog); |
|
1285 maybeUnregisterAndCloseFile(gCOMPtrLog); |
|
1286 #endif |
|
1287 } |
|
1288 |
|
1289 void |
|
1290 nsTraceRefcnt::SetActivityIsLegal(bool aLegal) |
|
1291 { |
|
1292 #ifdef NS_IMPL_REFCNT_LOGGING |
|
1293 if (gActivityTLS == BAD_TLS_INDEX) |
|
1294 PR_NewThreadPrivateIndex(&gActivityTLS, nullptr); |
|
1295 |
|
1296 PR_SetThreadPrivate(gActivityTLS, NS_INT32_TO_PTR(!aLegal)); |
|
1297 #endif |
|
1298 } |