1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
---|
2 | /* ***** BEGIN LICENSE BLOCK *****
|
---|
3 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
---|
4 | *
|
---|
5 | * The contents of this file are subject to the Mozilla Public License Version
|
---|
6 | * 1.1 (the "License"); you may not use this file except in compliance with
|
---|
7 | * the License. You may obtain a copy of the License at
|
---|
8 | * http://www.mozilla.org/MPL/
|
---|
9 | *
|
---|
10 | * Software distributed under the License is distributed on an "AS IS" basis,
|
---|
11 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
---|
12 | * for the specific language governing rights and limitations under the
|
---|
13 | * License.
|
---|
14 | *
|
---|
15 | * The Original Code is Mozilla Communicator client code.
|
---|
16 | *
|
---|
17 | * The Initial Developer of the Original Code is
|
---|
18 | * Netscape Communications Corporation.
|
---|
19 | * Portions created by the Initial Developer are Copyright (C) 1998
|
---|
20 | * the Initial Developer. All Rights Reserved.
|
---|
21 | *
|
---|
22 | * Contributor(s):
|
---|
23 | * L. David Baron <[email protected]>
|
---|
24 | *
|
---|
25 | * Alternatively, the contents of this file may be used under the terms of
|
---|
26 | * either of the GNU General Public License Version 2 or later (the "GPL"),
|
---|
27 | * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
---|
28 | * in which case the provisions of the GPL or the LGPL are applicable instead
|
---|
29 | * of those above. If you wish to allow use of your version of this file only
|
---|
30 | * under the terms of either the GPL or the LGPL, and not to allow others to
|
---|
31 | * use your version of this file under the terms of the MPL, indicate your
|
---|
32 | * decision by deleting the provisions above and replace them with the notice
|
---|
33 | * and other provisions required by the GPL or the LGPL. If you do not delete
|
---|
34 | * the provisions above, a recipient may use your version of this file under
|
---|
35 | * the terms of any one of the MPL, the GPL or the LGPL.
|
---|
36 | *
|
---|
37 | * ***** END LICENSE BLOCK ***** */
|
---|
38 |
|
---|
39 | #include "nsTraceRefcntImpl.h"
|
---|
40 | #include "nscore.h"
|
---|
41 | #include "nsISupports.h"
|
---|
42 | #include "nsVoidArray.h"
|
---|
43 | #include "prprf.h"
|
---|
44 | #include "prlog.h"
|
---|
45 | #include "plstr.h"
|
---|
46 | #include <stdlib.h>
|
---|
47 | #include "nsCOMPtr.h"
|
---|
48 | #include "nsCRT.h"
|
---|
49 | #include <math.h>
|
---|
50 |
|
---|
51 | #if defined(_WIN32)
|
---|
52 | #include <windows.h>
|
---|
53 | #elif defined(linux) && !defined(VBOX) && defined(__GLIBC__) && (defined(__i386) || defined(PPC))
|
---|
54 | #include <setjmp.h>
|
---|
55 |
|
---|
56 | //
|
---|
57 | // On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
|
---|
58 | // if __USE_GNU is defined. I suppose its some kind of standards
|
---|
59 | // adherence thing.
|
---|
60 | //
|
---|
61 | #if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
|
---|
62 | #define __USE_GNU
|
---|
63 | #endif
|
---|
64 |
|
---|
65 | #include <dlfcn.h>
|
---|
66 | #endif
|
---|
67 |
|
---|
68 | #ifdef HAVE_LIBDL
|
---|
69 | #include <dlfcn.h>
|
---|
70 | #endif
|
---|
71 |
|
---|
72 | #if defined(XP_MAC) && !TARGET_CARBON
|
---|
73 | #include "macstdlibextras.h"
|
---|
74 | #endif
|
---|
75 |
|
---|
76 | ////////////////////////////////////////////////////////////////////////////////
|
---|
77 |
|
---|
78 | NS_COM void
|
---|
79 | NS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
|
---|
80 | double *meanResult, double *stdDevResult)
|
---|
81 | {
|
---|
82 | double mean = 0.0, var = 0.0, stdDev = 0.0;
|
---|
83 | if (n > 0.0 && sumOfValues >= 0) {
|
---|
84 | mean = sumOfValues / n;
|
---|
85 | double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
|
---|
86 | if (temp < 0.0 || n <= 1)
|
---|
87 | var = 0.0;
|
---|
88 | else
|
---|
89 | var = temp / (n * (n - 1));
|
---|
90 | // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
|
---|
91 | stdDev = var != 0.0 ? sqrt(var) : 0.0;
|
---|
92 | }
|
---|
93 | *meanResult = mean;
|
---|
94 | *stdDevResult = stdDev;
|
---|
95 | }
|
---|
96 |
|
---|
97 | ////////////////////////////////////////////////////////////////////////////////
|
---|
98 |
|
---|
99 | #ifdef NS_BUILD_REFCNT_LOGGING
|
---|
100 | #include "plhash.h"
|
---|
101 | #include "prmem.h"
|
---|
102 |
|
---|
103 | #include "prlock.h"
|
---|
104 |
|
---|
105 | static PRLock* gTraceLock;
|
---|
106 |
|
---|
107 | #define LOCK_TRACELOG() PR_Lock(gTraceLock)
|
---|
108 | #define UNLOCK_TRACELOG() PR_Unlock(gTraceLock)
|
---|
109 |
|
---|
110 | static PLHashTable* gBloatView;
|
---|
111 | static PLHashTable* gTypesToLog;
|
---|
112 | static PLHashTable* gObjectsToLog;
|
---|
113 | static PLHashTable* gSerialNumbers;
|
---|
114 | static PRInt32 gNextSerialNumber;
|
---|
115 |
|
---|
116 | static PRBool gLogging;
|
---|
117 | static PRBool gLogToLeaky;
|
---|
118 | static PRBool gLogLeaksOnly;
|
---|
119 |
|
---|
120 | static void (*leakyLogAddRef)(void* p, int oldrc, int newrc);
|
---|
121 | static void (*leakyLogRelease)(void* p, int oldrc, int newrc);
|
---|
122 |
|
---|
123 | static PRBool gInitialized = PR_FALSE;
|
---|
124 | static FILE *gBloatLog = nsnull;
|
---|
125 | static FILE *gRefcntsLog = nsnull;
|
---|
126 | static FILE *gAllocLog = nsnull;
|
---|
127 | static FILE *gLeakyLog = nsnull;
|
---|
128 | static FILE *gCOMPtrLog = nsnull;
|
---|
129 | static PRBool gActivityIsLegal = PR_FALSE;
|
---|
130 |
|
---|
131 | struct serialNumberRecord {
|
---|
132 | PRInt32 serialNumber;
|
---|
133 | PRInt32 refCount;
|
---|
134 | PRInt32 COMPtrCount;
|
---|
135 | };
|
---|
136 |
|
---|
137 | struct nsTraceRefcntStats {
|
---|
138 | nsrefcnt mAddRefs;
|
---|
139 | nsrefcnt mReleases;
|
---|
140 | nsrefcnt mCreates;
|
---|
141 | nsrefcnt mDestroys;
|
---|
142 | double mRefsOutstandingTotal;
|
---|
143 | double mRefsOutstandingSquared;
|
---|
144 | double mObjsOutstandingTotal;
|
---|
145 | double mObjsOutstandingSquared;
|
---|
146 | };
|
---|
147 |
|
---|
148 | #ifdef DEBUG_dbaron_off
|
---|
149 | // I hope to turn this on for everybody once we hit it a little less.
|
---|
150 | #define ASSERT_ACTIVITY_IS_LEGAL \
|
---|
151 | NS_WARN_IF_FALSE(gActivityIsLegal, \
|
---|
152 | "XPCOM objects created/destroyed from static ctor/dtor")
|
---|
153 | #else
|
---|
154 | #define ASSERT_ACTIVITY_IS_LEGAL
|
---|
155 | #endif
|
---|
156 |
|
---|
157 |
|
---|
158 | // These functions are copied from nsprpub/lib/ds/plhash.c, with changes
|
---|
159 | // to the functions not called Default* to free the serialNumberRecord or
|
---|
160 | // the BloatEntry.
|
---|
161 |
|
---|
162 | static void * PR_CALLBACK
|
---|
163 | DefaultAllocTable(void *pool, PRSize size)
|
---|
164 | {
|
---|
165 | #if defined(XP_MAC)
|
---|
166 | #pragma unused (pool)
|
---|
167 | #endif
|
---|
168 |
|
---|
169 | return PR_MALLOC(size);
|
---|
170 | }
|
---|
171 |
|
---|
172 | static void PR_CALLBACK
|
---|
173 | DefaultFreeTable(void *pool, void *item)
|
---|
174 | {
|
---|
175 | #if defined(XP_MAC)
|
---|
176 | #pragma unused (pool)
|
---|
177 | #endif
|
---|
178 |
|
---|
179 | PR_Free(item);
|
---|
180 | }
|
---|
181 |
|
---|
182 | static PLHashEntry * PR_CALLBACK
|
---|
183 | DefaultAllocEntry(void *pool, const void *key)
|
---|
184 | {
|
---|
185 | #if defined(XP_MAC)
|
---|
186 | #pragma unused (pool,key)
|
---|
187 | #endif
|
---|
188 |
|
---|
189 | return PR_NEW(PLHashEntry);
|
---|
190 | }
|
---|
191 |
|
---|
192 | static void PR_CALLBACK
|
---|
193 | SerialNumberFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
|
---|
194 | {
|
---|
195 | #if defined(XP_MAC)
|
---|
196 | #pragma unused (pool)
|
---|
197 | #endif
|
---|
198 |
|
---|
199 | if (flag == HT_FREE_ENTRY) {
|
---|
200 | PR_Free(NS_REINTERPRET_CAST(serialNumberRecord*,he->value));
|
---|
201 | PR_Free(he);
|
---|
202 | }
|
---|
203 | }
|
---|
204 |
|
---|
205 | static void PR_CALLBACK
|
---|
206 | TypesToLogFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
|
---|
207 | {
|
---|
208 | #if defined(XP_MAC)
|
---|
209 | #pragma unused (pool)
|
---|
210 | #endif
|
---|
211 |
|
---|
212 | if (flag == HT_FREE_ENTRY) {
|
---|
213 | nsCRT::free(NS_CONST_CAST(char*,
|
---|
214 | NS_REINTERPRET_CAST(const char*, he->key)));
|
---|
215 | PR_Free(he);
|
---|
216 | }
|
---|
217 | }
|
---|
218 |
|
---|
219 | static const PLHashAllocOps serialNumberHashAllocOps = {
|
---|
220 | DefaultAllocTable, DefaultFreeTable,
|
---|
221 | DefaultAllocEntry, SerialNumberFreeEntry
|
---|
222 | };
|
---|
223 |
|
---|
224 | static const PLHashAllocOps typesToLogHashAllocOps = {
|
---|
225 | DefaultAllocTable, DefaultFreeTable,
|
---|
226 | DefaultAllocEntry, TypesToLogFreeEntry
|
---|
227 | };
|
---|
228 |
|
---|
229 | ////////////////////////////////////////////////////////////////////////////////
|
---|
230 |
|
---|
231 | class BloatEntry {
|
---|
232 | public:
|
---|
233 | BloatEntry(const char* className, PRUint32 classSize)
|
---|
234 | : mClassSize(classSize) {
|
---|
235 | mClassName = PL_strdup(className);
|
---|
236 | Clear(&mNewStats);
|
---|
237 | Clear(&mAllStats);
|
---|
238 | mTotalLeaked = 0;
|
---|
239 | }
|
---|
240 |
|
---|
241 | ~BloatEntry() {
|
---|
242 | PL_strfree(mClassName);
|
---|
243 | }
|
---|
244 |
|
---|
245 | PRUint32 GetClassSize() { return (PRUint32)mClassSize; }
|
---|
246 | const char* GetClassName() { return mClassName; }
|
---|
247 |
|
---|
248 | static void Clear(nsTraceRefcntStats* stats) {
|
---|
249 | stats->mAddRefs = 0;
|
---|
250 | stats->mReleases = 0;
|
---|
251 | stats->mCreates = 0;
|
---|
252 | stats->mDestroys = 0;
|
---|
253 | stats->mRefsOutstandingTotal = 0;
|
---|
254 | stats->mRefsOutstandingSquared = 0;
|
---|
255 | stats->mObjsOutstandingTotal = 0;
|
---|
256 | stats->mObjsOutstandingSquared = 0;
|
---|
257 | }
|
---|
258 |
|
---|
259 | void Accumulate() {
|
---|
260 | mAllStats.mAddRefs += mNewStats.mAddRefs;
|
---|
261 | mAllStats.mReleases += mNewStats.mReleases;
|
---|
262 | mAllStats.mCreates += mNewStats.mCreates;
|
---|
263 | mAllStats.mDestroys += mNewStats.mDestroys;
|
---|
264 | mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal;
|
---|
265 | mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared;
|
---|
266 | mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal;
|
---|
267 | mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared;
|
---|
268 | Clear(&mNewStats);
|
---|
269 | }
|
---|
270 |
|
---|
271 | void AddRef(nsrefcnt refcnt) {
|
---|
272 | mNewStats.mAddRefs++;
|
---|
273 | if (refcnt == 1) {
|
---|
274 | Ctor();
|
---|
275 | }
|
---|
276 | AccountRefs();
|
---|
277 | }
|
---|
278 |
|
---|
279 | void Release(nsrefcnt refcnt) {
|
---|
280 | mNewStats.mReleases++;
|
---|
281 | if (refcnt == 0) {
|
---|
282 | Dtor();
|
---|
283 | }
|
---|
284 | AccountRefs();
|
---|
285 | }
|
---|
286 |
|
---|
287 | void Ctor() {
|
---|
288 | mNewStats.mCreates++;
|
---|
289 | AccountObjs();
|
---|
290 | }
|
---|
291 |
|
---|
292 | void Dtor() {
|
---|
293 | mNewStats.mDestroys++;
|
---|
294 | AccountObjs();
|
---|
295 | }
|
---|
296 |
|
---|
297 | void AccountRefs() {
|
---|
298 | PRInt32 cnt = (mNewStats.mAddRefs - mNewStats.mReleases);
|
---|
299 | mNewStats.mRefsOutstandingTotal += cnt;
|
---|
300 | mNewStats.mRefsOutstandingSquared += cnt * cnt;
|
---|
301 | }
|
---|
302 |
|
---|
303 | void AccountObjs() {
|
---|
304 | PRInt32 cnt = (mNewStats.mCreates - mNewStats.mDestroys);
|
---|
305 | mNewStats.mObjsOutstandingTotal += cnt;
|
---|
306 | mNewStats.mObjsOutstandingSquared += cnt * cnt;
|
---|
307 | }
|
---|
308 |
|
---|
309 | static PRIntn PR_CALLBACK DumpEntry(PLHashEntry *he, PRIntn i, void *arg) {
|
---|
310 | BloatEntry* entry = (BloatEntry*)he->value;
|
---|
311 | if (entry) {
|
---|
312 | entry->Accumulate();
|
---|
313 | NS_STATIC_CAST(nsVoidArray*, arg)->AppendElement(entry);
|
---|
314 | }
|
---|
315 | return HT_ENUMERATE_NEXT;
|
---|
316 | }
|
---|
317 |
|
---|
318 | static PRIntn PR_CALLBACK TotalEntries(PLHashEntry *he, PRIntn i, void *arg) {
|
---|
319 | BloatEntry* entry = (BloatEntry*)he->value;
|
---|
320 | if (entry && nsCRT::strcmp(entry->mClassName, "TOTAL") != 0) {
|
---|
321 | entry->Total((BloatEntry*)arg);
|
---|
322 | }
|
---|
323 | return HT_ENUMERATE_NEXT;
|
---|
324 | }
|
---|
325 |
|
---|
326 | void Total(BloatEntry* total) {
|
---|
327 | total->mAllStats.mAddRefs += mNewStats.mAddRefs + mAllStats.mAddRefs;
|
---|
328 | total->mAllStats.mReleases += mNewStats.mReleases + mAllStats.mReleases;
|
---|
329 | total->mAllStats.mCreates += mNewStats.mCreates + mAllStats.mCreates;
|
---|
330 | total->mAllStats.mDestroys += mNewStats.mDestroys + mAllStats.mDestroys;
|
---|
331 | total->mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal + mAllStats.mRefsOutstandingTotal;
|
---|
332 | total->mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared + mAllStats.mRefsOutstandingSquared;
|
---|
333 | total->mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal + mAllStats.mObjsOutstandingTotal;
|
---|
334 | total->mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared + mAllStats.mObjsOutstandingSquared;
|
---|
335 | PRInt32 count = (mNewStats.mCreates + mAllStats.mCreates);
|
---|
336 | total->mClassSize += mClassSize * count; // adjust for average in DumpTotal
|
---|
337 | total->mTotalLeaked += (PRInt32)(mClassSize *
|
---|
338 | ((mNewStats.mCreates + mAllStats.mCreates)
|
---|
339 | -(mNewStats.mDestroys + mAllStats.mDestroys)));
|
---|
340 | }
|
---|
341 |
|
---|
342 | nsresult DumpTotal(PRUint32 nClasses, FILE* out) {
|
---|
343 | mClassSize /= mAllStats.mCreates;
|
---|
344 | return Dump(-1, out, nsTraceRefcntImpl::ALL_STATS);
|
---|
345 | }
|
---|
346 |
|
---|
347 | static PRBool HaveLeaks(nsTraceRefcntStats* stats) {
|
---|
348 | return ((stats->mAddRefs != stats->mReleases) ||
|
---|
349 | (stats->mCreates != stats->mDestroys));
|
---|
350 | }
|
---|
351 |
|
---|
352 | static nsresult PrintDumpHeader(FILE* out, const char* msg) {
|
---|
353 | fprintf(out, "\n== BloatView: %s\n\n", msg);
|
---|
354 | fprintf(out,
|
---|
355 | " |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n");
|
---|
356 | fprintf(out,
|
---|
357 | " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
|
---|
358 | return NS_OK;
|
---|
359 | }
|
---|
360 |
|
---|
361 | nsresult Dump(PRIntn i, FILE* out, nsTraceRefcntImpl::StatisticsType type) {
|
---|
362 | nsTraceRefcntStats* stats = (type == nsTraceRefcntImpl::NEW_STATS) ? &mNewStats : &mAllStats;
|
---|
363 | if (gLogLeaksOnly && !HaveLeaks(stats)) {
|
---|
364 | return NS_OK;
|
---|
365 | }
|
---|
366 |
|
---|
367 | double meanRefs, stddevRefs;
|
---|
368 | NS_MeanAndStdDev(stats->mAddRefs + stats->mReleases,
|
---|
369 | stats->mRefsOutstandingTotal,
|
---|
370 | stats->mRefsOutstandingSquared,
|
---|
371 | &meanRefs, &stddevRefs);
|
---|
372 |
|
---|
373 | double meanObjs, stddevObjs;
|
---|
374 | NS_MeanAndStdDev(stats->mCreates + stats->mDestroys,
|
---|
375 | stats->mObjsOutstandingTotal,
|
---|
376 | stats->mObjsOutstandingSquared,
|
---|
377 | &meanObjs, &stddevObjs);
|
---|
378 |
|
---|
379 | if ((stats->mAddRefs - stats->mReleases) != 0 ||
|
---|
380 | stats->mAddRefs != 0 ||
|
---|
381 | meanRefs != 0 ||
|
---|
382 | stddevRefs != 0 ||
|
---|
383 | (stats->mCreates - stats->mDestroys) != 0 ||
|
---|
384 | stats->mCreates != 0 ||
|
---|
385 | meanObjs != 0 ||
|
---|
386 | stddevObjs != 0) {
|
---|
387 | fprintf(out, "%4d %-40.40s %8d %8d %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f)\n",
|
---|
388 | i+1, mClassName,
|
---|
389 | (PRInt32)mClassSize,
|
---|
390 | (nsCRT::strcmp(mClassName, "TOTAL"))
|
---|
391 | ?(PRInt32)((stats->mCreates - stats->mDestroys) * mClassSize)
|
---|
392 | :mTotalLeaked,
|
---|
393 | stats->mCreates,
|
---|
394 | (stats->mCreates - stats->mDestroys),
|
---|
395 | meanObjs,
|
---|
396 | stddevObjs,
|
---|
397 | stats->mAddRefs,
|
---|
398 | (stats->mAddRefs - stats->mReleases),
|
---|
399 | meanRefs,
|
---|
400 | stddevRefs);
|
---|
401 | }
|
---|
402 | return NS_OK;
|
---|
403 | }
|
---|
404 |
|
---|
405 | protected:
|
---|
406 | char* mClassName;
|
---|
407 | double mClassSize; // this is stored as a double because of the way we compute the avg class size for total bloat
|
---|
408 | PRInt32 mTotalLeaked; // used only for TOTAL entry
|
---|
409 | nsTraceRefcntStats mNewStats;
|
---|
410 | nsTraceRefcntStats mAllStats;
|
---|
411 | };
|
---|
412 |
|
---|
413 | static void PR_CALLBACK
|
---|
414 | BloatViewFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
|
---|
415 | {
|
---|
416 | #if defined(XP_MAC)
|
---|
417 | #pragma unused (pool)
|
---|
418 | #endif
|
---|
419 |
|
---|
420 | if (flag == HT_FREE_ENTRY) {
|
---|
421 | BloatEntry* entry = NS_REINTERPRET_CAST(BloatEntry*,he->value);
|
---|
422 | delete entry;
|
---|
423 | PR_Free(he);
|
---|
424 | }
|
---|
425 | }
|
---|
426 |
|
---|
427 | const static PLHashAllocOps bloatViewHashAllocOps = {
|
---|
428 | DefaultAllocTable, DefaultFreeTable,
|
---|
429 | DefaultAllocEntry, BloatViewFreeEntry
|
---|
430 | };
|
---|
431 |
|
---|
432 | static void
|
---|
433 | RecreateBloatView()
|
---|
434 | {
|
---|
435 | gBloatView = PL_NewHashTable(256,
|
---|
436 | PL_HashString,
|
---|
437 | PL_CompareStrings,
|
---|
438 | PL_CompareValues,
|
---|
439 | &bloatViewHashAllocOps, NULL);
|
---|
440 | }
|
---|
441 |
|
---|
442 | static BloatEntry*
|
---|
443 | GetBloatEntry(const char* aTypeName, PRUint32 aInstanceSize)
|
---|
444 | {
|
---|
445 | if (!gBloatView) {
|
---|
446 | RecreateBloatView();
|
---|
447 | }
|
---|
448 | BloatEntry* entry = NULL;
|
---|
449 | if (gBloatView) {
|
---|
450 | entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName);
|
---|
451 | if (entry == NULL && aInstanceSize > 0) {
|
---|
452 |
|
---|
453 | entry = new BloatEntry(aTypeName, aInstanceSize);
|
---|
454 | PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry);
|
---|
455 | if (e == NULL) {
|
---|
456 | delete entry;
|
---|
457 | entry = NULL;
|
---|
458 | }
|
---|
459 | } else {
|
---|
460 | NS_ASSERTION(aInstanceSize == 0 ||
|
---|
461 | entry->GetClassSize() == aInstanceSize,
|
---|
462 | "bad size recorded");
|
---|
463 | }
|
---|
464 | }
|
---|
465 | return entry;
|
---|
466 | }
|
---|
467 |
|
---|
468 | static PRIntn PR_CALLBACK DumpSerialNumbers(PLHashEntry* aHashEntry, PRIntn aIndex, void* aClosure)
|
---|
469 | {
|
---|
470 | serialNumberRecord* record = NS_REINTERPRET_CAST(serialNumberRecord *,aHashEntry->value);
|
---|
471 | #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
|
---|
472 | fprintf((FILE*) aClosure, "%d (%d references; %d from COMPtrs)\n",
|
---|
473 | record->serialNumber,
|
---|
474 | record->refCount,
|
---|
475 | record->COMPtrCount);
|
---|
476 | #else
|
---|
477 | fprintf((FILE*) aClosure, "%d (%d references)\n",
|
---|
478 | record->serialNumber,
|
---|
479 | record->refCount);
|
---|
480 | #endif
|
---|
481 | return HT_ENUMERATE_NEXT;
|
---|
482 | }
|
---|
483 |
|
---|
484 |
|
---|
485 | #endif /* NS_BUILD_REFCNT_LOGGING */
|
---|
486 |
|
---|
487 | nsresult
|
---|
488 | nsTraceRefcntImpl::DumpStatistics(StatisticsType type, FILE* out)
|
---|
489 | {
|
---|
490 | nsresult rv = NS_OK;
|
---|
491 | #ifdef NS_BUILD_REFCNT_LOGGING
|
---|
492 | if (gBloatLog == nsnull || gBloatView == nsnull) {
|
---|
493 | return NS_ERROR_FAILURE;
|
---|
494 | }
|
---|
495 | if (out == nsnull) {
|
---|
496 | out = gBloatLog;
|
---|
497 | }
|
---|
498 |
|
---|
499 | LOCK_TRACELOG();
|
---|
500 |
|
---|
501 | PRBool wasLogging = gLogging;
|
---|
502 | gLogging = PR_FALSE; // turn off logging for this method
|
---|
503 |
|
---|
504 | const char* msg;
|
---|
505 | if (type == NEW_STATS) {
|
---|
506 | if (gLogLeaksOnly)
|
---|
507 | msg = "NEW (incremental) LEAK STATISTICS";
|
---|
508 | else
|
---|
509 | msg = "NEW (incremental) LEAK AND BLOAT STATISTICS";
|
---|
510 | }
|
---|
511 | else {
|
---|
512 | if (gLogLeaksOnly)
|
---|
513 | msg = "ALL (cumulative) LEAK STATISTICS";
|
---|
514 | else
|
---|
515 | msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
|
---|
516 | }
|
---|
517 | rv = BloatEntry::PrintDumpHeader(out, msg);
|
---|
518 | if (NS_FAILED(rv)) goto done;
|
---|
519 |
|
---|
520 | {
|
---|
521 | BloatEntry total("TOTAL", 0);
|
---|
522 | PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
|
---|
523 | total.DumpTotal(gBloatView->nentries, out);
|
---|
524 |
|
---|
525 | nsVoidArray entries;
|
---|
526 | PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries);
|
---|
527 |
|
---|
528 | fprintf(stdout, "nsTraceRefcntImpl::DumpStatistics: %d entries\n",
|
---|
529 | entries.Count());
|
---|
530 |
|
---|
531 | // Sort the entries alphabetically by classname.
|
---|
532 | PRInt32 i, j;
|
---|
533 | for (i = entries.Count() - 1; i >= 1; --i) {
|
---|
534 | for (j = i - 1; j >= 0; --j) {
|
---|
535 | BloatEntry* left = NS_STATIC_CAST(BloatEntry*, entries[i]);
|
---|
536 | BloatEntry* right = NS_STATIC_CAST(BloatEntry*, entries[j]);
|
---|
537 |
|
---|
538 | if (PL_strcmp(left->GetClassName(), right->GetClassName()) < 0) {
|
---|
539 | entries.ReplaceElementAt(right, i);
|
---|
540 | entries.ReplaceElementAt(left, j);
|
---|
541 | }
|
---|
542 | }
|
---|
543 | }
|
---|
544 |
|
---|
545 | // Enumerate from back-to-front, so things come out in alpha order
|
---|
546 | for (i = 0; i < entries.Count(); ++i) {
|
---|
547 | BloatEntry* entry = NS_STATIC_CAST(BloatEntry*, entries[i]);
|
---|
548 | entry->Dump(i, out, type);
|
---|
549 | }
|
---|
550 | }
|
---|
551 |
|
---|
552 | if (gSerialNumbers) {
|
---|
553 | fprintf(out, "\n\nSerial Numbers of Leaked Objects:\n");
|
---|
554 | PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, out);
|
---|
555 | }
|
---|
556 |
|
---|
557 | done:
|
---|
558 | gLogging = wasLogging;
|
---|
559 | UNLOCK_TRACELOG();
|
---|
560 | #endif
|
---|
561 | return rv;
|
---|
562 | }
|
---|
563 |
|
---|
564 | void
|
---|
565 | nsTraceRefcntImpl::ResetStatistics()
|
---|
566 | {
|
---|
567 | #ifdef NS_BUILD_REFCNT_LOGGING
|
---|
568 | LOCK_TRACELOG();
|
---|
569 | if (gBloatView) {
|
---|
570 | PL_HashTableDestroy(gBloatView);
|
---|
571 | gBloatView = nsnull;
|
---|
572 | }
|
---|
573 | UNLOCK_TRACELOG();
|
---|
574 | #endif
|
---|
575 | }
|
---|
576 |
|
---|
577 | #ifdef NS_BUILD_REFCNT_LOGGING
|
---|
578 | static PRBool LogThisType(const char* aTypeName)
|
---|
579 | {
|
---|
580 | void* he = PL_HashTableLookup(gTypesToLog, aTypeName);
|
---|
581 | return nsnull != he;
|
---|
582 | }
|
---|
583 |
|
---|
584 | static PRInt32 GetSerialNumber(void* aPtr, PRBool aCreate)
|
---|
585 | {
|
---|
586 | #ifdef GC_LEAK_DETECTOR
|
---|
587 | // need to disguise this pointer, so the table won't keep the object alive.
|
---|
588 | aPtr = (void*) ~PLHashNumber(aPtr);
|
---|
589 | #endif
|
---|
590 | PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
|
---|
591 | if (hep && *hep) {
|
---|
592 | return PRInt32((NS_REINTERPRET_CAST(serialNumberRecord*,(*hep)->value))->serialNumber);
|
---|
593 | }
|
---|
594 | else if (aCreate) {
|
---|
595 | serialNumberRecord *record = PR_NEW(serialNumberRecord);
|
---|
596 | record->serialNumber = ++gNextSerialNumber;
|
---|
597 | record->refCount = 0;
|
---|
598 | record->COMPtrCount = 0;
|
---|
599 | PL_HashTableRawAdd(gSerialNumbers, hep, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr, NS_REINTERPRET_CAST(void*,record));
|
---|
600 | return gNextSerialNumber;
|
---|
601 | }
|
---|
602 | else {
|
---|
603 | return 0;
|
---|
604 | }
|
---|
605 | }
|
---|
606 |
|
---|
607 | static PRInt32* GetRefCount(void* aPtr)
|
---|
608 | {
|
---|
609 | #ifdef GC_LEAK_DETECTOR
|
---|
610 | // need to disguise this pointer, so the table won't keep the object alive.
|
---|
611 | aPtr = (void*) ~PLHashNumber(aPtr);
|
---|
612 | #endif
|
---|
613 | PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
|
---|
614 | if (hep && *hep) {
|
---|
615 | return &((NS_REINTERPRET_CAST(serialNumberRecord*,(*hep)->value))->refCount);
|
---|
616 | } else {
|
---|
617 | return nsnull;
|
---|
618 | }
|
---|
619 | }
|
---|
620 |
|
---|
621 | static PRInt32* GetCOMPtrCount(void* aPtr)
|
---|
622 | {
|
---|
623 | #ifdef GC_LEAK_DETECTOR
|
---|
624 | // need to disguise this pointer, so the table won't keep the object alive.
|
---|
625 | aPtr = (void*) ~PLHashNumber(aPtr);
|
---|
626 | #endif
|
---|
627 | PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
|
---|
628 | if (hep && *hep) {
|
---|
629 | return &((NS_REINTERPRET_CAST(serialNumberRecord*,(*hep)->value))->COMPtrCount);
|
---|
630 | } else {
|
---|
631 | return nsnull;
|
---|
632 | }
|
---|
633 | }
|
---|
634 |
|
---|
635 | static void RecycleSerialNumberPtr(void* aPtr)
|
---|
636 | {
|
---|
637 | #ifdef GC_LEAK_DETECTOR
|
---|
638 | // need to disguise this pointer, so the table won't keep the object alive.
|
---|
639 | aPtr = (void*) ~PLHashNumber(aPtr);
|
---|
640 | #endif
|
---|
641 | PL_HashTableRemove(gSerialNumbers, aPtr);
|
---|
642 | }
|
---|
643 |
|
---|
644 | static PRBool LogThisObj(PRInt32 aSerialNumber)
|
---|
645 | {
|
---|
646 | return nsnull != PL_HashTableLookup(gObjectsToLog, (const void*)(uintptr_t)(aSerialNumber));
|
---|
647 | }
|
---|
648 |
|
---|
649 | static PRBool InitLog(const char* envVar, const char* msg, FILE* *result)
|
---|
650 | {
|
---|
651 | const char* value = getenv(envVar);
|
---|
652 | if (value) {
|
---|
653 | if (nsCRT::strcmp(value, "1") == 0) {
|
---|
654 | *result = stdout;
|
---|
655 | fprintf(stdout, "### %s defined -- logging %s to stdout\n",
|
---|
656 | envVar, msg);
|
---|
657 | return PR_TRUE;
|
---|
658 | }
|
---|
659 | else if (nsCRT::strcmp(value, "2") == 0) {
|
---|
660 | *result = stderr;
|
---|
661 | fprintf(stdout, "### %s defined -- logging %s to stderr\n",
|
---|
662 | envVar, msg);
|
---|
663 | return PR_TRUE;
|
---|
664 | }
|
---|
665 | else {
|
---|
666 | FILE *stream = ::fopen(value, "w");
|
---|
667 | if (stream != NULL) {
|
---|
668 | *result = stream;
|
---|
669 | fprintf(stdout, "### %s defined -- logging %s to %s\n",
|
---|
670 | envVar, msg, value);
|
---|
671 | return PR_TRUE;
|
---|
672 | }
|
---|
673 | else {
|
---|
674 | fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
|
---|
675 | envVar, msg, value);
|
---|
676 | return PR_FALSE;
|
---|
677 | }
|
---|
678 | }
|
---|
679 | }
|
---|
680 | return PR_FALSE;
|
---|
681 | }
|
---|
682 |
|
---|
683 |
|
---|
684 | static PLHashNumber PR_CALLBACK HashNumber(const void* aKey)
|
---|
685 | {
|
---|
686 | return PLHashNumber(NS_PTR_TO_INT32(aKey));
|
---|
687 | }
|
---|
688 |
|
---|
689 | static void InitTraceLog(void)
|
---|
690 | {
|
---|
691 | if (gInitialized) return;
|
---|
692 | gInitialized = PR_TRUE;
|
---|
693 |
|
---|
694 | #if defined(XP_MAC) && !TARGET_CARBON
|
---|
695 | // this can get called before Toolbox has been initialized.
|
---|
696 | InitializeMacToolbox();
|
---|
697 | #endif
|
---|
698 |
|
---|
699 | PRBool defined;
|
---|
700 | defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
|
---|
701 | if (!defined)
|
---|
702 | gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
|
---|
703 | if (defined || gLogLeaksOnly) {
|
---|
704 | RecreateBloatView();
|
---|
705 | if (!gBloatView) {
|
---|
706 | NS_WARNING("out of memory");
|
---|
707 | gBloatLog = nsnull;
|
---|
708 | gLogLeaksOnly = PR_FALSE;
|
---|
709 | }
|
---|
710 | }
|
---|
711 |
|
---|
712 | (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
|
---|
713 |
|
---|
714 | (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
|
---|
715 |
|
---|
716 | defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog);
|
---|
717 | if (defined) {
|
---|
718 | gLogToLeaky = PR_TRUE;
|
---|
719 | void* p = nsnull;
|
---|
720 | void* q = nsnull;
|
---|
721 | #ifdef HAVE_LIBDL
|
---|
722 | p = dlsym(0, "__log_addref");
|
---|
723 | q = dlsym(0, "__log_release");
|
---|
724 | #endif
|
---|
725 | if (p && q) {
|
---|
726 | leakyLogAddRef = (void (*)(void*,int,int)) p;
|
---|
727 | leakyLogRelease = (void (*)(void*,int,int)) q;
|
---|
728 | }
|
---|
729 | else {
|
---|
730 | gLogToLeaky = PR_FALSE;
|
---|
731 | fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
|
---|
732 | fflush(stdout);
|
---|
733 | }
|
---|
734 | }
|
---|
735 |
|
---|
736 | const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
|
---|
737 |
|
---|
738 | #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
|
---|
739 | if (classes) {
|
---|
740 | (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
|
---|
741 | } else {
|
---|
742 | if (getenv("XPCOM_MEM_COMPTR_LOG")) {
|
---|
743 | fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
|
---|
744 | }
|
---|
745 | }
|
---|
746 | #else
|
---|
747 | const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
|
---|
748 | if (comptr_log) {
|
---|
749 | fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
|
---|
750 | }
|
---|
751 | #endif
|
---|
752 |
|
---|
753 | if (classes) {
|
---|
754 | // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
|
---|
755 | // as a list of class names to track
|
---|
756 | gTypesToLog = PL_NewHashTable(256,
|
---|
757 | PL_HashString,
|
---|
758 | PL_CompareStrings,
|
---|
759 | PL_CompareValues,
|
---|
760 | &typesToLogHashAllocOps, NULL);
|
---|
761 | if (!gTypesToLog) {
|
---|
762 | NS_WARNING("out of memory");
|
---|
763 | fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
|
---|
764 | }
|
---|
765 | else {
|
---|
766 | fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
|
---|
767 | const char* cp = classes;
|
---|
768 | for (;;) {
|
---|
769 | char* cm = (char*) strchr(cp, ',');
|
---|
770 | if (cm) {
|
---|
771 | *cm = '\0';
|
---|
772 | }
|
---|
773 | PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1);
|
---|
774 | fprintf(stdout, "%s ", cp);
|
---|
775 | if (!cm) break;
|
---|
776 | *cm = ',';
|
---|
777 | cp = cm + 1;
|
---|
778 | }
|
---|
779 | fprintf(stdout, "\n");
|
---|
780 | }
|
---|
781 |
|
---|
782 | gSerialNumbers = PL_NewHashTable(256,
|
---|
783 | HashNumber,
|
---|
784 | PL_CompareValues,
|
---|
785 | PL_CompareValues,
|
---|
786 | &serialNumberHashAllocOps, NULL);
|
---|
787 |
|
---|
788 |
|
---|
789 | }
|
---|
790 |
|
---|
791 | const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
|
---|
792 | if (objects) {
|
---|
793 | gObjectsToLog = PL_NewHashTable(256,
|
---|
794 | HashNumber,
|
---|
795 | PL_CompareValues,
|
---|
796 | PL_CompareValues,
|
---|
797 | NULL, NULL);
|
---|
798 |
|
---|
799 | if (!gObjectsToLog) {
|
---|
800 | NS_WARNING("out of memory");
|
---|
801 | fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
|
---|
802 | }
|
---|
803 | else if (! (gRefcntsLog || gAllocLog || gCOMPtrLog)) {
|
---|
804 | fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
|
---|
805 | }
|
---|
806 | else {
|
---|
807 | fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
|
---|
808 | const char* cp = objects;
|
---|
809 | for (;;) {
|
---|
810 | char* cm = (char*) strchr(cp, ',');
|
---|
811 | if (cm) {
|
---|
812 | *cm = '\0';
|
---|
813 | }
|
---|
814 | PRInt32 top = 0;
|
---|
815 | PRInt32 bottom = 0;
|
---|
816 | while (*cp) {
|
---|
817 | if (*cp == '-') {
|
---|
818 | bottom = top;
|
---|
819 | top = 0;
|
---|
820 | ++cp;
|
---|
821 | }
|
---|
822 | top *= 10;
|
---|
823 | top += *cp - '0';
|
---|
824 | ++cp;
|
---|
825 | }
|
---|
826 | if (!bottom) {
|
---|
827 | bottom = top;
|
---|
828 | }
|
---|
829 | for(PRInt32 serialno = bottom; serialno <= top; serialno++) {
|
---|
830 | PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
|
---|
831 | fprintf(stdout, "%d ", serialno);
|
---|
832 | }
|
---|
833 | if (!cm) break;
|
---|
834 | *cm = ',';
|
---|
835 | cp = cm + 1;
|
---|
836 | }
|
---|
837 | fprintf(stdout, "\n");
|
---|
838 | }
|
---|
839 | }
|
---|
840 |
|
---|
841 |
|
---|
842 | if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog || gCOMPtrLog) {
|
---|
843 | gLogging = PR_TRUE;
|
---|
844 | }
|
---|
845 |
|
---|
846 | gTraceLock = PR_NewLock();
|
---|
847 | }
|
---|
848 |
|
---|
849 | #endif
|
---|
850 |
|
---|
851 | #if defined(_WIN32) && defined(_M_IX86) // WIN32 x86 stack walking code
|
---|
852 | #include "nsStackFrameWin.h"
|
---|
853 | void
|
---|
854 | nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
|
---|
855 | {
|
---|
856 | DumpStackToFile(aStream);
|
---|
857 | }
|
---|
858 |
|
---|
859 | // WIN32 x86 stack walking code
|
---|
860 | // i386 or PPC Linux stackwalking code or Solaris
|
---|
861 | #elif (defined(linux) && !defined(VBOX) && defined(__GLIBC__) && (defined(__i386) || defined(PPC))) || (defined(__sun) && (defined(__sparc) || defined(sparc) || defined(__i386) || defined(i386)))
|
---|
862 | #include "nsStackFrameUnix.h"
|
---|
863 | void
|
---|
864 | nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
|
---|
865 | {
|
---|
866 | DumpStackToFile(aStream);
|
---|
867 | }
|
---|
868 |
|
---|
869 | #elif defined(XP_MAC)
|
---|
870 |
|
---|
871 | /**
|
---|
872 | * Stack walking code for the Mac OS.
|
---|
873 | */
|
---|
874 |
|
---|
875 | #include "gc_fragments.h"
|
---|
876 |
|
---|
877 | #include <typeinfo>
|
---|
878 |
|
---|
879 | extern "C" {
|
---|
880 | void MWUnmangle(const char *mangled_name, char *unmangled_name, size_t buffersize);
|
---|
881 | }
|
---|
882 |
|
---|
883 | struct traceback_table {
|
---|
884 | long zero;
|
---|
885 | long magic;
|
---|
886 | long reserved;
|
---|
887 | long codeSize;
|
---|
888 | short nameLength;
|
---|
889 | char name[2];
|
---|
890 | };
|
---|
891 |
|
---|
892 | static char* pc2name(long* pc, char name[], long size)
|
---|
893 | {
|
---|
894 | name[0] = '\0';
|
---|
895 |
|
---|
896 | // make sure pc is instruction aligned (at least).
|
---|
897 | if (UInt32(pc) == (UInt32(pc) & 0xFFFFFFFC)) {
|
---|
898 | long instructionsToLook = 4096;
|
---|
899 | long* instruction = (long*)pc;
|
---|
900 |
|
---|
901 | // look for the traceback table.
|
---|
902 | while (instructionsToLook--) {
|
---|
903 | if (instruction[0] == 0x4E800020 && instruction[1] == 0x00000000) {
|
---|
904 | traceback_table* tb = (traceback_table*)&instruction[1];
|
---|
905 | memcpy(name, tb->name + 1, --nameLength);
|
---|
906 | name[nameLength] = '\0';
|
---|
907 | break;
|
---|
908 | }
|
---|
909 | ++instruction;
|
---|
910 | }
|
---|
911 | }
|
---|
912 |
|
---|
913 | return name;
|
---|
914 | }
|
---|
915 |
|
---|
916 | struct stack_frame {
|
---|
917 | stack_frame* next; // savedSP
|
---|
918 | void* savedCR;
|
---|
919 | void* savedLR;
|
---|
920 | void* reserved0;
|
---|
921 | void* reserved1;
|
---|
922 | void* savedTOC;
|
---|
923 | };
|
---|
924 |
|
---|
925 | static asm stack_frame* getStackFrame()
|
---|
926 | {
|
---|
927 | mr r3, sp
|
---|
928 | blr
|
---|
929 | }
|
---|
930 |
|
---|
931 | NS_COM void
|
---|
932 | nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
|
---|
933 | {
|
---|
934 | stack_frame* currentFrame = getStackFrame(); // WalkTheStack's frame.
|
---|
935 | currentFrame = currentFrame->next; // WalkTheStack's caller's frame.
|
---|
936 | currentFrame = currentFrame->next; // WalkTheStack's caller's caller's frame.
|
---|
937 |
|
---|
938 | while (true) {
|
---|
939 | // LR saved at 8(SP) in each frame. subtract 4 to get address of calling instruction.
|
---|
940 | void* pc = currentFrame->savedLR;
|
---|
941 |
|
---|
942 | // convert PC to name, unmangle it, and generate source location, if possible.
|
---|
943 | static char symbol_name[1024], unmangled_name[1024], file_name[256]; UInt32 file_offset;
|
---|
944 |
|
---|
945 | if (GC_address_to_source((char*)pc, symbol_name, file_name, &file_offset)) {
|
---|
946 | MWUnmangle(symbol_name, unmangled_name, sizeof(unmangled_name));
|
---|
947 | fprintf(aStream, "%s[%s,%ld]\n", unmangled_name, file_name, file_offset);
|
---|
948 | } else {
|
---|
949 | pc2name((long*)pc, symbol_name, sizeof(symbol_name));
|
---|
950 | MWUnmangle(symbol_name, unmangled_name, sizeof(unmangled_name));
|
---|
951 | fprintf(aStream, "%s(0x%08X)\n", unmangled_name, pc);
|
---|
952 | }
|
---|
953 |
|
---|
954 | currentFrame = currentFrame->next;
|
---|
955 | // the bottom-most frame is marked as pointing to NULL, or is ODD if a 68K transition frame.
|
---|
956 | if (currentFrame == NULL || UInt32(currentFrame) & 0x1)
|
---|
957 | break;
|
---|
958 | }
|
---|
959 | }
|
---|
960 |
|
---|
961 | #else // unsupported platform.
|
---|
962 |
|
---|
963 | void
|
---|
964 | nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
|
---|
965 | {
|
---|
966 | fprintf(aStream, "write me, dammit!\n");
|
---|
967 | }
|
---|
968 |
|
---|
969 | #endif
|
---|
970 |
|
---|
971 | //----------------------------------------------------------------------
|
---|
972 |
|
---|
973 | // This thing is exported by libstdc++
|
---|
974 | // Yes, this is a gcc only hack
|
---|
975 | #if defined(MOZ_DEMANGLE_SYMBOLS)
|
---|
976 | #include <cxxabi.h>
|
---|
977 | #include <stdlib.h> // for free()
|
---|
978 | #endif // MOZ_DEMANGLE_SYMBOLS
|
---|
979 |
|
---|
980 | NS_COM void
|
---|
981 | nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol,
|
---|
982 | char * aBuffer,
|
---|
983 | int aBufLen)
|
---|
984 | {
|
---|
985 | NS_ASSERTION(nsnull != aSymbol,"null symbol");
|
---|
986 | NS_ASSERTION(nsnull != aBuffer,"null buffer");
|
---|
987 | NS_ASSERTION(aBufLen >= 32 ,"pulled 32 out of you know where");
|
---|
988 |
|
---|
989 | aBuffer[0] = '\0';
|
---|
990 |
|
---|
991 | #if defined(MOZ_DEMANGLE_SYMBOLS)
|
---|
992 | /* See demangle.h in the gcc source for the voodoo */
|
---|
993 | char * demangled = abi::__cxa_demangle(aSymbol,0,0,0);
|
---|
994 |
|
---|
995 | if (demangled)
|
---|
996 | {
|
---|
997 | strncpy(aBuffer,demangled,aBufLen);
|
---|
998 | free(demangled);
|
---|
999 | }
|
---|
1000 | #endif // MOZ_DEMANGLE_SYMBOLS
|
---|
1001 | }
|
---|
1002 |
|
---|
1003 |
|
---|
1004 | //----------------------------------------------------------------------
|
---|
1005 |
|
---|
1006 | NS_COM void
|
---|
1007 | nsTraceRefcntImpl::LoadLibrarySymbols(const char* aLibraryName,
|
---|
1008 | void* aLibrayHandle)
|
---|
1009 | {
|
---|
1010 | #ifdef NS_BUILD_REFCNT_LOGGING
|
---|
1011 | #if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */
|
---|
1012 | if (!gInitialized)
|
---|
1013 | InitTraceLog();
|
---|
1014 |
|
---|
1015 | if (gAllocLog || gRefcntsLog) {
|
---|
1016 | fprintf(stdout, "### Loading symbols for %s\n", aLibraryName);
|
---|
1017 | fflush(stdout);
|
---|
1018 |
|
---|
1019 | HANDLE myProcess = ::GetCurrentProcess();
|
---|
1020 | BOOL ok = EnsureSymInitialized();
|
---|
1021 | if (ok) {
|
---|
1022 | const char* baseName = aLibraryName;
|
---|
1023 | // just get the base name of the library if a full path was given:
|
---|
1024 | PRInt32 len = strlen(aLibraryName);
|
---|
1025 | for (PRInt32 i = len - 1; i >= 0; i--) {
|
---|
1026 | if (aLibraryName[i] == '\\') {
|
---|
1027 | baseName = &aLibraryName[i + 1];
|
---|
1028 | break;
|
---|
1029 | }
|
---|
1030 | }
|
---|
1031 | DWORD baseAddr = _SymLoadModule(myProcess,
|
---|
1032 | NULL,
|
---|
1033 | (char*)baseName,
|
---|
1034 | (char*)baseName,
|
---|
1035 | 0,
|
---|
1036 | 0);
|
---|
1037 | ok = (baseAddr != nsnull);
|
---|
1038 | }
|
---|
1039 | if (!ok) {
|
---|
1040 | LPVOID lpMsgBuf;
|
---|
1041 | FormatMessage(
|
---|
1042 | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
---|
1043 | FORMAT_MESSAGE_FROM_SYSTEM |
|
---|
1044 | FORMAT_MESSAGE_IGNORE_INSERTS,
|
---|
1045 | NULL,
|
---|
1046 | GetLastError(),
|
---|
1047 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
|
---|
1048 | (LPTSTR) &lpMsgBuf,
|
---|
1049 | 0,
|
---|
1050 | NULL
|
---|
1051 | );
|
---|
1052 | fprintf(stdout, "### ERROR: LoadLibrarySymbols for %s: %s\n",
|
---|
1053 | aLibraryName, lpMsgBuf);
|
---|
1054 | fflush(stdout);
|
---|
1055 | LocalFree( lpMsgBuf );
|
---|
1056 | }
|
---|
1057 | }
|
---|
1058 | #endif
|
---|
1059 | #endif
|
---|
1060 | }
|
---|
1061 |
|
---|
1062 | //----------------------------------------------------------------------
|
---|
1063 |
|
---|
1064 |
|
---|
1065 |
|
---|
1066 |
|
---|
1067 |
|
---|
1068 |
|
---|
1069 | // don't use the logging ones. :-)
|
---|
1070 | NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::AddRef(void)
|
---|
1071 | {
|
---|
1072 | NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");
|
---|
1073 | ++mRefCnt;
|
---|
1074 | return mRefCnt;
|
---|
1075 | }
|
---|
1076 |
|
---|
1077 | NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::Release(void)
|
---|
1078 | {
|
---|
1079 | NS_PRECONDITION(0 != mRefCnt, "dup release");
|
---|
1080 | --mRefCnt;
|
---|
1081 | if (mRefCnt == 0) {
|
---|
1082 | mRefCnt = 1; /* stabilize */
|
---|
1083 | delete this;
|
---|
1084 | return 0;
|
---|
1085 | }
|
---|
1086 | return mRefCnt;
|
---|
1087 | }
|
---|
1088 |
|
---|
1089 | NS_IMPL_QUERY_INTERFACE1(nsTraceRefcntImpl, nsITraceRefcnt)
|
---|
1090 |
|
---|
1091 | nsTraceRefcntImpl::nsTraceRefcntImpl()
|
---|
1092 | {
|
---|
1093 | /* member initializers and constructor code */
|
---|
1094 | }
|
---|
1095 |
|
---|
1096 | NS_IMETHODIMP
|
---|
1097 | nsTraceRefcntImpl::LogAddRef(void* aPtr,
|
---|
1098 | nsrefcnt aRefcnt,
|
---|
1099 | const char* aClazz,
|
---|
1100 | PRUint32 classSize)
|
---|
1101 | {
|
---|
1102 | #ifdef NS_BUILD_REFCNT_LOGGING
|
---|
1103 | ASSERT_ACTIVITY_IS_LEGAL;
|
---|
1104 | if (!gInitialized)
|
---|
1105 | InitTraceLog();
|
---|
1106 | if (gLogging) {
|
---|
1107 | LOCK_TRACELOG();
|
---|
1108 |
|
---|
1109 | if (gBloatLog) {
|
---|
1110 | BloatEntry* entry = GetBloatEntry(aClazz, classSize);
|
---|
1111 | if (entry) {
|
---|
1112 | entry->AddRef(aRefcnt);
|
---|
1113 | }
|
---|
1114 | }
|
---|
1115 |
|
---|
1116 | // Here's the case where neither NS_NEWXPCOM nor MOZ_COUNT_CTOR were used,
|
---|
1117 | // yet we still want to see creation information:
|
---|
1118 |
|
---|
1119 | PRBool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
|
---|
1120 | PRInt32 serialno = 0;
|
---|
1121 | if (gSerialNumbers && loggingThisType) {
|
---|
1122 | serialno = GetSerialNumber(aPtr, aRefcnt == 1);
|
---|
1123 | PRInt32* count = GetRefCount(aPtr);
|
---|
1124 | if(count)
|
---|
1125 | (*count)++;
|
---|
1126 |
|
---|
1127 | }
|
---|
1128 |
|
---|
1129 | PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
---|
1130 | if (aRefcnt == 1 && gAllocLog && loggingThisType && loggingThisObject) {
|
---|
1131 | fprintf(gAllocLog, "\n<%s> 0x%08X %d Create\n",
|
---|
1132 | aClazz, NS_PTR_TO_INT32(aPtr), serialno);
|
---|
1133 | WalkTheStack(gAllocLog);
|
---|
1134 | }
|
---|
1135 |
|
---|
1136 | if (gRefcntsLog && loggingThisType && loggingThisObject) {
|
---|
1137 | if (gLogToLeaky) {
|
---|
1138 | (*leakyLogAddRef)(aPtr, aRefcnt - 1, aRefcnt);
|
---|
1139 | }
|
---|
1140 | else {
|
---|
1141 | // Can't use PR_LOG(), b/c it truncates the line
|
---|
1142 | fprintf(gRefcntsLog,
|
---|
1143 | "\n<%s> 0x%08X %d AddRef %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
|
---|
1144 | WalkTheStack(gRefcntsLog);
|
---|
1145 | fflush(gRefcntsLog);
|
---|
1146 | }
|
---|
1147 | }
|
---|
1148 | UNLOCK_TRACELOG();
|
---|
1149 | }
|
---|
1150 | #endif
|
---|
1151 | return NS_OK;
|
---|
1152 | }
|
---|
1153 |
|
---|
1154 | NS_IMETHODIMP
|
---|
1155 | nsTraceRefcntImpl::LogRelease(void* aPtr,
|
---|
1156 | nsrefcnt aRefcnt,
|
---|
1157 | const char* aClazz)
|
---|
1158 | {
|
---|
1159 | #ifdef NS_BUILD_REFCNT_LOGGING
|
---|
1160 | ASSERT_ACTIVITY_IS_LEGAL;
|
---|
1161 | if (!gInitialized)
|
---|
1162 | InitTraceLog();
|
---|
1163 | if (gLogging) {
|
---|
1164 | LOCK_TRACELOG();
|
---|
1165 |
|
---|
1166 | if (gBloatLog) {
|
---|
1167 | BloatEntry* entry = GetBloatEntry(aClazz, 0);
|
---|
1168 | if (entry) {
|
---|
1169 | entry->Release(aRefcnt);
|
---|
1170 | }
|
---|
1171 | }
|
---|
1172 |
|
---|
1173 | PRBool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
|
---|
1174 | PRInt32 serialno = 0;
|
---|
1175 | if (gSerialNumbers && loggingThisType) {
|
---|
1176 | serialno = GetSerialNumber(aPtr, PR_FALSE);
|
---|
1177 | PRInt32* count = GetRefCount(aPtr);
|
---|
1178 | if(count)
|
---|
1179 | (*count)--;
|
---|
1180 |
|
---|
1181 | }
|
---|
1182 |
|
---|
1183 | PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
---|
1184 | if (gRefcntsLog && loggingThisType && loggingThisObject) {
|
---|
1185 | if (gLogToLeaky) {
|
---|
1186 | (*leakyLogRelease)(aPtr, aRefcnt + 1, aRefcnt);
|
---|
1187 | }
|
---|
1188 | else {
|
---|
1189 | // Can't use PR_LOG(), b/c it truncates the line
|
---|
1190 | fprintf(gRefcntsLog,
|
---|
1191 | "\n<%s> 0x%08X %d Release %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
|
---|
1192 | WalkTheStack(gRefcntsLog);
|
---|
1193 | fflush(gRefcntsLog);
|
---|
1194 | }
|
---|
1195 | }
|
---|
1196 |
|
---|
1197 | // Here's the case where neither NS_DELETEXPCOM nor MOZ_COUNT_DTOR were used,
|
---|
1198 | // yet we still want to see deletion information:
|
---|
1199 |
|
---|
1200 | if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) {
|
---|
1201 | fprintf(gAllocLog,
|
---|
1202 | "\n<%s> 0x%08X %d Destroy\n",
|
---|
1203 | aClazz, NS_PTR_TO_INT32(aPtr), serialno);
|
---|
1204 | WalkTheStack(gAllocLog);
|
---|
1205 | }
|
---|
1206 |
|
---|
1207 | if (aRefcnt == 0 && gSerialNumbers && loggingThisType) {
|
---|
1208 | RecycleSerialNumberPtr(aPtr);
|
---|
1209 | }
|
---|
1210 |
|
---|
1211 | UNLOCK_TRACELOG();
|
---|
1212 | }
|
---|
1213 | #endif
|
---|
1214 | return NS_OK;
|
---|
1215 | }
|
---|
1216 |
|
---|
1217 | NS_IMETHODIMP
|
---|
1218 | nsTraceRefcntImpl::LogCtor(void* aPtr,
|
---|
1219 | const char* aType,
|
---|
1220 | PRUint32 aInstanceSize)
|
---|
1221 | {
|
---|
1222 | #ifdef NS_BUILD_REFCNT_LOGGING
|
---|
1223 | ASSERT_ACTIVITY_IS_LEGAL;
|
---|
1224 | if (!gInitialized)
|
---|
1225 | InitTraceLog();
|
---|
1226 |
|
---|
1227 | if (gLogging) {
|
---|
1228 | LOCK_TRACELOG();
|
---|
1229 |
|
---|
1230 | if (gBloatLog) {
|
---|
1231 | BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
|
---|
1232 | if (entry) {
|
---|
1233 | entry->Ctor();
|
---|
1234 | }
|
---|
1235 | }
|
---|
1236 |
|
---|
1237 | PRBool loggingThisType = (!gTypesToLog || LogThisType(aType));
|
---|
1238 | PRInt32 serialno = 0;
|
---|
1239 | if (gSerialNumbers && loggingThisType) {
|
---|
1240 | serialno = GetSerialNumber(aPtr, PR_TRUE);
|
---|
1241 | }
|
---|
1242 |
|
---|
1243 | PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
---|
1244 | if (gAllocLog && loggingThisType && loggingThisObject) {
|
---|
1245 | fprintf(gAllocLog, "\n<%s> 0x%08X %d Ctor (%d)\n",
|
---|
1246 | aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
|
---|
1247 | WalkTheStack(gAllocLog);
|
---|
1248 | }
|
---|
1249 |
|
---|
1250 | UNLOCK_TRACELOG();
|
---|
1251 | }
|
---|
1252 | #endif
|
---|
1253 | return NS_OK;
|
---|
1254 | }
|
---|
1255 |
|
---|
1256 |
|
---|
1257 | NS_IMETHODIMP
|
---|
1258 | nsTraceRefcntImpl::LogDtor(void* aPtr,
|
---|
1259 | const char* aType,
|
---|
1260 | PRUint32 aInstanceSize)
|
---|
1261 | {
|
---|
1262 | #ifdef NS_BUILD_REFCNT_LOGGING
|
---|
1263 | ASSERT_ACTIVITY_IS_LEGAL;
|
---|
1264 | if (!gInitialized)
|
---|
1265 | InitTraceLog();
|
---|
1266 |
|
---|
1267 | if (gLogging) {
|
---|
1268 | LOCK_TRACELOG();
|
---|
1269 |
|
---|
1270 | if (gBloatLog) {
|
---|
1271 | BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
|
---|
1272 | if (entry) {
|
---|
1273 | entry->Dtor();
|
---|
1274 | }
|
---|
1275 | }
|
---|
1276 |
|
---|
1277 | PRBool loggingThisType = (!gTypesToLog || LogThisType(aType));
|
---|
1278 | PRInt32 serialno = 0;
|
---|
1279 | if (gSerialNumbers && loggingThisType) {
|
---|
1280 | serialno = GetSerialNumber(aPtr, PR_FALSE);
|
---|
1281 | RecycleSerialNumberPtr(aPtr);
|
---|
1282 | }
|
---|
1283 |
|
---|
1284 | PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
---|
1285 |
|
---|
1286 | // (If we're on a losing architecture, don't do this because we'll be
|
---|
1287 | // using LogDeleteXPCOM instead to get file and line numbers.)
|
---|
1288 | if (gAllocLog && loggingThisType && loggingThisObject) {
|
---|
1289 | fprintf(gAllocLog, "\n<%s> 0x%08X %d Dtor (%d)\n",
|
---|
1290 | aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
|
---|
1291 | WalkTheStack(gAllocLog);
|
---|
1292 | }
|
---|
1293 |
|
---|
1294 | UNLOCK_TRACELOG();
|
---|
1295 | }
|
---|
1296 | #endif
|
---|
1297 | return NS_OK;
|
---|
1298 | }
|
---|
1299 |
|
---|
1300 |
|
---|
1301 | NS_IMETHODIMP
|
---|
1302 | nsTraceRefcntImpl::LogAddCOMPtr(void* aCOMPtr,
|
---|
1303 | nsISupports* aObject)
|
---|
1304 | {
|
---|
1305 | #if defined(NS_BUILD_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
|
---|
1306 | // Get the most-derived object.
|
---|
1307 | void *object = dynamic_cast<void *>(aObject);
|
---|
1308 |
|
---|
1309 | // This is a very indirect way of finding out what the class is
|
---|
1310 | // of the object being logged. If we're logging a specific type,
|
---|
1311 | // then
|
---|
1312 | if (!gTypesToLog || !gSerialNumbers) {
|
---|
1313 | return NS_OK;
|
---|
1314 | }
|
---|
1315 | PRInt32 serialno = GetSerialNumber(object, PR_FALSE);
|
---|
1316 | if (serialno == 0) {
|
---|
1317 | return NS_OK;
|
---|
1318 | }
|
---|
1319 |
|
---|
1320 | if (!gInitialized)
|
---|
1321 | InitTraceLog();
|
---|
1322 | if (gLogging) {
|
---|
1323 | LOCK_TRACELOG();
|
---|
1324 |
|
---|
1325 | PRInt32* count = GetCOMPtrCount(object);
|
---|
1326 | if(count)
|
---|
1327 | (*count)++;
|
---|
1328 |
|
---|
1329 | PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
---|
1330 |
|
---|
1331 | if (gCOMPtrLog && loggingThisObject) {
|
---|
1332 | fprintf(gCOMPtrLog, "\n<?> 0x%08X %d nsCOMPtrAddRef %d 0x%08X\n",
|
---|
1333 | NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
|
---|
1334 | WalkTheStack(gCOMPtrLog);
|
---|
1335 | }
|
---|
1336 |
|
---|
1337 | UNLOCK_TRACELOG();
|
---|
1338 | }
|
---|
1339 | #endif
|
---|
1340 | return NS_OK;
|
---|
1341 | }
|
---|
1342 |
|
---|
1343 |
|
---|
1344 | NS_IMETHODIMP
|
---|
1345 | nsTraceRefcntImpl::LogReleaseCOMPtr(void* aCOMPtr,
|
---|
1346 | nsISupports* aObject)
|
---|
1347 | {
|
---|
1348 | #if defined(NS_BUILD_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
|
---|
1349 | // Get the most-derived object.
|
---|
1350 | void *object = dynamic_cast<void *>(aObject);
|
---|
1351 |
|
---|
1352 | // This is a very indirect way of finding out what the class is
|
---|
1353 | // of the object being logged. If we're logging a specific type,
|
---|
1354 | // then
|
---|
1355 | if (!gTypesToLog || !gSerialNumbers) {
|
---|
1356 | return NS_OK;
|
---|
1357 | }
|
---|
1358 | PRInt32 serialno = GetSerialNumber(object, PR_FALSE);
|
---|
1359 | if (serialno == 0) {
|
---|
1360 | return NS_OK;
|
---|
1361 | }
|
---|
1362 |
|
---|
1363 | if (!gInitialized)
|
---|
1364 | InitTraceLog();
|
---|
1365 | if (gLogging) {
|
---|
1366 | LOCK_TRACELOG();
|
---|
1367 |
|
---|
1368 | PRInt32* count = GetCOMPtrCount(object);
|
---|
1369 | if(count)
|
---|
1370 | (*count)--;
|
---|
1371 |
|
---|
1372 | PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
|
---|
1373 |
|
---|
1374 | if (gCOMPtrLog && loggingThisObject) {
|
---|
1375 | fprintf(gCOMPtrLog, "\n<?> 0x%08X %d nsCOMPtrRelease %d 0x%08X\n",
|
---|
1376 | NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
|
---|
1377 | WalkTheStack(gCOMPtrLog);
|
---|
1378 | }
|
---|
1379 |
|
---|
1380 | UNLOCK_TRACELOG();
|
---|
1381 | }
|
---|
1382 | #endif
|
---|
1383 | return NS_OK;
|
---|
1384 | }
|
---|
1385 |
|
---|
1386 | NS_COM void
|
---|
1387 | nsTraceRefcntImpl::Startup()
|
---|
1388 | {
|
---|
1389 | #ifdef NS_BUILD_REFCNT_LOGGING
|
---|
1390 | SetActivityIsLegal(PR_TRUE);
|
---|
1391 | #endif
|
---|
1392 | }
|
---|
1393 |
|
---|
1394 | NS_COM void
|
---|
1395 | nsTraceRefcntImpl::Shutdown()
|
---|
1396 | {
|
---|
1397 | #ifdef NS_BUILD_REFCNT_LOGGING
|
---|
1398 |
|
---|
1399 | if (gBloatView) {
|
---|
1400 | PL_HashTableDestroy(gBloatView);
|
---|
1401 | gBloatView = nsnull;
|
---|
1402 | }
|
---|
1403 | if (gTypesToLog) {
|
---|
1404 | PL_HashTableDestroy(gTypesToLog);
|
---|
1405 | gTypesToLog = nsnull;
|
---|
1406 | }
|
---|
1407 | if (gObjectsToLog) {
|
---|
1408 | PL_HashTableDestroy(gObjectsToLog);
|
---|
1409 | gObjectsToLog = nsnull;
|
---|
1410 | }
|
---|
1411 | if (gSerialNumbers) {
|
---|
1412 | PL_HashTableDestroy(gSerialNumbers);
|
---|
1413 | gSerialNumbers = nsnull;
|
---|
1414 | }
|
---|
1415 |
|
---|
1416 | SetActivityIsLegal(PR_FALSE);
|
---|
1417 |
|
---|
1418 | #endif
|
---|
1419 | }
|
---|
1420 |
|
---|
1421 | NS_COM void
|
---|
1422 | nsTraceRefcntImpl::SetActivityIsLegal(PRBool aLegal)
|
---|
1423 | {
|
---|
1424 | #ifdef NS_BUILD_REFCNT_LOGGING
|
---|
1425 | gActivityIsLegal = aLegal;
|
---|
1426 | #endif
|
---|
1427 | }
|
---|
1428 |
|
---|
1429 |
|
---|
1430 | NS_METHOD
|
---|
1431 | nsTraceRefcntImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
|
---|
1432 | {
|
---|
1433 | *aInstancePtr = nsnull;
|
---|
1434 | nsITraceRefcnt* tracer = new nsTraceRefcntImpl();
|
---|
1435 | if (!tracer)
|
---|
1436 | return NS_ERROR_OUT_OF_MEMORY;
|
---|
1437 |
|
---|
1438 | nsresult rv = tracer->QueryInterface(aIID, aInstancePtr);
|
---|
1439 | if (NS_FAILED(rv)) {
|
---|
1440 | delete tracer;
|
---|
1441 | }
|
---|
1442 |
|
---|
1443 | return rv;
|
---|
1444 | }
|
---|