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