VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/base/nsTraceRefcntImpl.cpp@ 101854

Last change on this file since 101854 was 101828, checked in by vboxsync, 16 months ago

libs/xpcom: Get rid of unused nsStackFrameUnix.{cpp,h} (disabled using the VBOX pre-processor define), bugref:10545

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.9 KB
Line 
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
78NS_COM void
79NS_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
105static PRLock* gTraceLock;
106
107#define LOCK_TRACELOG() PR_Lock(gTraceLock)
108#define UNLOCK_TRACELOG() PR_Unlock(gTraceLock)
109
110static PLHashTable* gBloatView;
111static PLHashTable* gTypesToLog;
112static PLHashTable* gObjectsToLog;
113static PLHashTable* gSerialNumbers;
114static PRInt32 gNextSerialNumber;
115
116static PRBool gLogging;
117static PRBool gLogToLeaky;
118static PRBool gLogLeaksOnly;
119
120static void (*leakyLogAddRef)(void* p, int oldrc, int newrc);
121static void (*leakyLogRelease)(void* p, int oldrc, int newrc);
122
123static PRBool gInitialized = PR_FALSE;
124static FILE *gBloatLog = nsnull;
125static FILE *gRefcntsLog = nsnull;
126static FILE *gAllocLog = nsnull;
127static FILE *gLeakyLog = nsnull;
128static FILE *gCOMPtrLog = nsnull;
129static PRBool gActivityIsLegal = PR_FALSE;
130
131struct serialNumberRecord {
132 PRInt32 serialNumber;
133 PRInt32 refCount;
134 PRInt32 COMPtrCount;
135};
136
137struct 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
162static void * PR_CALLBACK
163DefaultAllocTable(void *pool, PRSize size)
164{
165#if defined(XP_MAC)
166#pragma unused (pool)
167#endif
168
169 return PR_MALLOC(size);
170}
171
172static void PR_CALLBACK
173DefaultFreeTable(void *pool, void *item)
174{
175#if defined(XP_MAC)
176#pragma unused (pool)
177#endif
178
179 PR_Free(item);
180}
181
182static PLHashEntry * PR_CALLBACK
183DefaultAllocEntry(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
192static void PR_CALLBACK
193SerialNumberFreeEntry(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
205static void PR_CALLBACK
206TypesToLogFreeEntry(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
219static const PLHashAllocOps serialNumberHashAllocOps = {
220 DefaultAllocTable, DefaultFreeTable,
221 DefaultAllocEntry, SerialNumberFreeEntry
222};
223
224static const PLHashAllocOps typesToLogHashAllocOps = {
225 DefaultAllocTable, DefaultFreeTable,
226 DefaultAllocEntry, TypesToLogFreeEntry
227};
228
229////////////////////////////////////////////////////////////////////////////////
230
231class BloatEntry {
232public:
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
405protected:
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
413static void PR_CALLBACK
414BloatViewFreeEntry(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
427const static PLHashAllocOps bloatViewHashAllocOps = {
428 DefaultAllocTable, DefaultFreeTable,
429 DefaultAllocEntry, BloatViewFreeEntry
430};
431
432static void
433RecreateBloatView()
434{
435 gBloatView = PL_NewHashTable(256,
436 PL_HashString,
437 PL_CompareStrings,
438 PL_CompareValues,
439 &bloatViewHashAllocOps, NULL);
440}
441
442static BloatEntry*
443GetBloatEntry(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
468static 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
487nsresult
488nsTraceRefcntImpl::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
557done:
558 gLogging = wasLogging;
559 UNLOCK_TRACELOG();
560#endif
561 return rv;
562}
563
564void
565nsTraceRefcntImpl::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
578static PRBool LogThisType(const char* aTypeName)
579{
580 void* he = PL_HashTableLookup(gTypesToLog, aTypeName);
581 return nsnull != he;
582}
583
584static PRInt32 GetSerialNumber(void* aPtr, PRBool aCreate)
585{
586 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
587 if (hep && *hep) {
588 return PRInt32((NS_REINTERPRET_CAST(serialNumberRecord*,(*hep)->value))->serialNumber);
589 }
590 else if (aCreate) {
591 serialNumberRecord *record = PR_NEW(serialNumberRecord);
592 record->serialNumber = ++gNextSerialNumber;
593 record->refCount = 0;
594 record->COMPtrCount = 0;
595 PL_HashTableRawAdd(gSerialNumbers, hep, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr, NS_REINTERPRET_CAST(void*,record));
596 return gNextSerialNumber;
597 }
598 else {
599 return 0;
600 }
601}
602
603static PRInt32* GetRefCount(void* aPtr)
604{
605 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
606 if (hep && *hep) {
607 return &((NS_REINTERPRET_CAST(serialNumberRecord*,(*hep)->value))->refCount);
608 } else {
609 return nsnull;
610 }
611}
612
613static PRInt32* GetCOMPtrCount(void* aPtr)
614{
615 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
616 if (hep && *hep) {
617 return &((NS_REINTERPRET_CAST(serialNumberRecord*,(*hep)->value))->COMPtrCount);
618 } else {
619 return nsnull;
620 }
621}
622
623static void RecycleSerialNumberPtr(void* aPtr)
624{
625 PL_HashTableRemove(gSerialNumbers, aPtr);
626}
627
628static PRBool LogThisObj(PRInt32 aSerialNumber)
629{
630 return nsnull != PL_HashTableLookup(gObjectsToLog, (const void*)(uintptr_t)(aSerialNumber));
631}
632
633static PRBool InitLog(const char* envVar, const char* msg, FILE* *result)
634{
635 const char* value = getenv(envVar);
636 if (value) {
637 if (nsCRT::strcmp(value, "1") == 0) {
638 *result = stdout;
639 fprintf(stdout, "### %s defined -- logging %s to stdout\n",
640 envVar, msg);
641 return PR_TRUE;
642 }
643 else if (nsCRT::strcmp(value, "2") == 0) {
644 *result = stderr;
645 fprintf(stdout, "### %s defined -- logging %s to stderr\n",
646 envVar, msg);
647 return PR_TRUE;
648 }
649 else {
650 FILE *stream = ::fopen(value, "w");
651 if (stream != NULL) {
652 *result = stream;
653 fprintf(stdout, "### %s defined -- logging %s to %s\n",
654 envVar, msg, value);
655 return PR_TRUE;
656 }
657 else {
658 fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
659 envVar, msg, value);
660 return PR_FALSE;
661 }
662 }
663 }
664 return PR_FALSE;
665}
666
667
668static PLHashNumber PR_CALLBACK HashNumber(const void* aKey)
669{
670 return PLHashNumber(NS_PTR_TO_INT32(aKey));
671}
672
673static void InitTraceLog(void)
674{
675 if (gInitialized) return;
676 gInitialized = PR_TRUE;
677
678#if defined(XP_MAC) && !TARGET_CARBON
679 // this can get called before Toolbox has been initialized.
680 InitializeMacToolbox();
681#endif
682
683 PRBool defined;
684 defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
685 if (!defined)
686 gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
687 if (defined || gLogLeaksOnly) {
688 RecreateBloatView();
689 if (!gBloatView) {
690 NS_WARNING("out of memory");
691 gBloatLog = nsnull;
692 gLogLeaksOnly = PR_FALSE;
693 }
694 }
695
696 (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
697
698 (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
699
700 defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog);
701 if (defined) {
702 gLogToLeaky = PR_TRUE;
703 void* p = nsnull;
704 void* q = nsnull;
705#ifdef HAVE_LIBDL
706 p = dlsym(0, "__log_addref");
707 q = dlsym(0, "__log_release");
708#endif
709 if (p && q) {
710 leakyLogAddRef = (void (*)(void*,int,int)) p;
711 leakyLogRelease = (void (*)(void*,int,int)) q;
712 }
713 else {
714 gLogToLeaky = PR_FALSE;
715 fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
716 fflush(stdout);
717 }
718 }
719
720 const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
721
722#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
723 if (classes) {
724 (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
725 } else {
726 if (getenv("XPCOM_MEM_COMPTR_LOG")) {
727 fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
728 }
729 }
730#else
731 const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
732 if (comptr_log) {
733 fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
734 }
735#endif
736
737 if (classes) {
738 // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
739 // as a list of class names to track
740 gTypesToLog = PL_NewHashTable(256,
741 PL_HashString,
742 PL_CompareStrings,
743 PL_CompareValues,
744 &typesToLogHashAllocOps, NULL);
745 if (!gTypesToLog) {
746 NS_WARNING("out of memory");
747 fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
748 }
749 else {
750 fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
751 const char* cp = classes;
752 for (;;) {
753 char* cm = (char*) strchr(cp, ',');
754 if (cm) {
755 *cm = '\0';
756 }
757 PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1);
758 fprintf(stdout, "%s ", cp);
759 if (!cm) break;
760 *cm = ',';
761 cp = cm + 1;
762 }
763 fprintf(stdout, "\n");
764 }
765
766 gSerialNumbers = PL_NewHashTable(256,
767 HashNumber,
768 PL_CompareValues,
769 PL_CompareValues,
770 &serialNumberHashAllocOps, NULL);
771
772
773 }
774
775 const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
776 if (objects) {
777 gObjectsToLog = PL_NewHashTable(256,
778 HashNumber,
779 PL_CompareValues,
780 PL_CompareValues,
781 NULL, NULL);
782
783 if (!gObjectsToLog) {
784 NS_WARNING("out of memory");
785 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
786 }
787 else if (! (gRefcntsLog || gAllocLog || gCOMPtrLog)) {
788 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
789 }
790 else {
791 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
792 const char* cp = objects;
793 for (;;) {
794 char* cm = (char*) strchr(cp, ',');
795 if (cm) {
796 *cm = '\0';
797 }
798 PRInt32 top = 0;
799 PRInt32 bottom = 0;
800 while (*cp) {
801 if (*cp == '-') {
802 bottom = top;
803 top = 0;
804 ++cp;
805 }
806 top *= 10;
807 top += *cp - '0';
808 ++cp;
809 }
810 if (!bottom) {
811 bottom = top;
812 }
813 for(PRInt32 serialno = bottom; serialno <= top; serialno++) {
814 PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
815 fprintf(stdout, "%d ", serialno);
816 }
817 if (!cm) break;
818 *cm = ',';
819 cp = cm + 1;
820 }
821 fprintf(stdout, "\n");
822 }
823 }
824
825
826 if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog || gCOMPtrLog) {
827 gLogging = PR_TRUE;
828 }
829
830 gTraceLock = PR_NewLock();
831}
832
833#endif
834
835void
836nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
837{
838 fprintf(aStream, "write me, dammit!\n");
839}
840
841//----------------------------------------------------------------------
842
843// This thing is exported by libstdc++
844// Yes, this is a gcc only hack
845#if defined(MOZ_DEMANGLE_SYMBOLS)
846#include <cxxabi.h>
847#include <stdlib.h> // for free()
848#endif // MOZ_DEMANGLE_SYMBOLS
849
850NS_COM void
851nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol,
852 char * aBuffer,
853 int aBufLen)
854{
855 NS_ASSERTION(nsnull != aSymbol,"null symbol");
856 NS_ASSERTION(nsnull != aBuffer,"null buffer");
857 NS_ASSERTION(aBufLen >= 32 ,"pulled 32 out of you know where");
858
859 aBuffer[0] = '\0';
860
861#if defined(MOZ_DEMANGLE_SYMBOLS)
862 /* See demangle.h in the gcc source for the voodoo */
863 char * demangled = abi::__cxa_demangle(aSymbol,0,0,0);
864
865 if (demangled)
866 {
867 strncpy(aBuffer,demangled,aBufLen);
868 free(demangled);
869 }
870#endif // MOZ_DEMANGLE_SYMBOLS
871}
872
873
874//----------------------------------------------------------------------
875
876NS_COM void
877nsTraceRefcntImpl::LoadLibrarySymbols(const char* aLibraryName,
878 void* aLibrayHandle)
879{
880#ifdef NS_BUILD_REFCNT_LOGGING
881#if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */
882 if (!gInitialized)
883 InitTraceLog();
884
885 if (gAllocLog || gRefcntsLog) {
886 fprintf(stdout, "### Loading symbols for %s\n", aLibraryName);
887 fflush(stdout);
888
889 HANDLE myProcess = ::GetCurrentProcess();
890 BOOL ok = EnsureSymInitialized();
891 if (ok) {
892 const char* baseName = aLibraryName;
893 // just get the base name of the library if a full path was given:
894 PRInt32 len = strlen(aLibraryName);
895 for (PRInt32 i = len - 1; i >= 0; i--) {
896 if (aLibraryName[i] == '\\') {
897 baseName = &aLibraryName[i + 1];
898 break;
899 }
900 }
901 DWORD baseAddr = _SymLoadModule(myProcess,
902 NULL,
903 (char*)baseName,
904 (char*)baseName,
905 0,
906 0);
907 ok = (baseAddr != nsnull);
908 }
909 if (!ok) {
910 LPVOID lpMsgBuf;
911 FormatMessage(
912 FORMAT_MESSAGE_ALLOCATE_BUFFER |
913 FORMAT_MESSAGE_FROM_SYSTEM |
914 FORMAT_MESSAGE_IGNORE_INSERTS,
915 NULL,
916 GetLastError(),
917 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
918 (LPTSTR) &lpMsgBuf,
919 0,
920 NULL
921 );
922 fprintf(stdout, "### ERROR: LoadLibrarySymbols for %s: %s\n",
923 aLibraryName, lpMsgBuf);
924 fflush(stdout);
925 LocalFree( lpMsgBuf );
926 }
927 }
928#endif
929#endif
930}
931
932//----------------------------------------------------------------------
933
934
935
936
937
938
939// don't use the logging ones. :-)
940NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::AddRef(void)
941{
942 NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");
943 ++mRefCnt;
944 return mRefCnt;
945}
946
947NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::Release(void)
948{
949 NS_PRECONDITION(0 != mRefCnt, "dup release");
950 --mRefCnt;
951 if (mRefCnt == 0) {
952 mRefCnt = 1; /* stabilize */
953 delete this;
954 return 0;
955 }
956 return mRefCnt;
957}
958
959NS_IMPL_QUERY_INTERFACE1(nsTraceRefcntImpl, nsITraceRefcnt)
960
961nsTraceRefcntImpl::nsTraceRefcntImpl()
962{
963 /* member initializers and constructor code */
964}
965
966NS_IMETHODIMP
967nsTraceRefcntImpl::LogAddRef(void* aPtr,
968 nsrefcnt aRefcnt,
969 const char* aClazz,
970 PRUint32 classSize)
971{
972#ifdef NS_BUILD_REFCNT_LOGGING
973 ASSERT_ACTIVITY_IS_LEGAL;
974 if (!gInitialized)
975 InitTraceLog();
976 if (gLogging) {
977 LOCK_TRACELOG();
978
979 if (gBloatLog) {
980 BloatEntry* entry = GetBloatEntry(aClazz, classSize);
981 if (entry) {
982 entry->AddRef(aRefcnt);
983 }
984 }
985
986 // Here's the case where neither NS_NEWXPCOM nor MOZ_COUNT_CTOR were used,
987 // yet we still want to see creation information:
988
989 PRBool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
990 PRInt32 serialno = 0;
991 if (gSerialNumbers && loggingThisType) {
992 serialno = GetSerialNumber(aPtr, aRefcnt == 1);
993 PRInt32* count = GetRefCount(aPtr);
994 if(count)
995 (*count)++;
996
997 }
998
999 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1000 if (aRefcnt == 1 && gAllocLog && loggingThisType && loggingThisObject) {
1001 fprintf(gAllocLog, "\n<%s> 0x%08X %d Create\n",
1002 aClazz, NS_PTR_TO_INT32(aPtr), serialno);
1003 WalkTheStack(gAllocLog);
1004 }
1005
1006 if (gRefcntsLog && loggingThisType && loggingThisObject) {
1007 if (gLogToLeaky) {
1008 (*leakyLogAddRef)(aPtr, aRefcnt - 1, aRefcnt);
1009 }
1010 else {
1011 // Can't use PR_LOG(), b/c it truncates the line
1012 fprintf(gRefcntsLog,
1013 "\n<%s> 0x%08X %d AddRef %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
1014 WalkTheStack(gRefcntsLog);
1015 fflush(gRefcntsLog);
1016 }
1017 }
1018 UNLOCK_TRACELOG();
1019 }
1020#endif
1021 return NS_OK;
1022}
1023
1024NS_IMETHODIMP
1025nsTraceRefcntImpl::LogRelease(void* aPtr,
1026 nsrefcnt aRefcnt,
1027 const char* aClazz)
1028{
1029#ifdef NS_BUILD_REFCNT_LOGGING
1030 ASSERT_ACTIVITY_IS_LEGAL;
1031 if (!gInitialized)
1032 InitTraceLog();
1033 if (gLogging) {
1034 LOCK_TRACELOG();
1035
1036 if (gBloatLog) {
1037 BloatEntry* entry = GetBloatEntry(aClazz, 0);
1038 if (entry) {
1039 entry->Release(aRefcnt);
1040 }
1041 }
1042
1043 PRBool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
1044 PRInt32 serialno = 0;
1045 if (gSerialNumbers && loggingThisType) {
1046 serialno = GetSerialNumber(aPtr, PR_FALSE);
1047 PRInt32* count = GetRefCount(aPtr);
1048 if(count)
1049 (*count)--;
1050
1051 }
1052
1053 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1054 if (gRefcntsLog && loggingThisType && loggingThisObject) {
1055 if (gLogToLeaky) {
1056 (*leakyLogRelease)(aPtr, aRefcnt + 1, aRefcnt);
1057 }
1058 else {
1059 // Can't use PR_LOG(), b/c it truncates the line
1060 fprintf(gRefcntsLog,
1061 "\n<%s> 0x%08X %d Release %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
1062 WalkTheStack(gRefcntsLog);
1063 fflush(gRefcntsLog);
1064 }
1065 }
1066
1067 // Here's the case where neither NS_DELETEXPCOM nor MOZ_COUNT_DTOR were used,
1068 // yet we still want to see deletion information:
1069
1070 if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) {
1071 fprintf(gAllocLog,
1072 "\n<%s> 0x%08X %d Destroy\n",
1073 aClazz, NS_PTR_TO_INT32(aPtr), serialno);
1074 WalkTheStack(gAllocLog);
1075 }
1076
1077 if (aRefcnt == 0 && gSerialNumbers && loggingThisType) {
1078 RecycleSerialNumberPtr(aPtr);
1079 }
1080
1081 UNLOCK_TRACELOG();
1082 }
1083#endif
1084 return NS_OK;
1085}
1086
1087NS_IMETHODIMP
1088nsTraceRefcntImpl::LogCtor(void* aPtr,
1089 const char* aType,
1090 PRUint32 aInstanceSize)
1091{
1092#ifdef NS_BUILD_REFCNT_LOGGING
1093 ASSERT_ACTIVITY_IS_LEGAL;
1094 if (!gInitialized)
1095 InitTraceLog();
1096
1097 if (gLogging) {
1098 LOCK_TRACELOG();
1099
1100 if (gBloatLog) {
1101 BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
1102 if (entry) {
1103 entry->Ctor();
1104 }
1105 }
1106
1107 PRBool loggingThisType = (!gTypesToLog || LogThisType(aType));
1108 PRInt32 serialno = 0;
1109 if (gSerialNumbers && loggingThisType) {
1110 serialno = GetSerialNumber(aPtr, PR_TRUE);
1111 }
1112
1113 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1114 if (gAllocLog && loggingThisType && loggingThisObject) {
1115 fprintf(gAllocLog, "\n<%s> 0x%08X %d Ctor (%d)\n",
1116 aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
1117 WalkTheStack(gAllocLog);
1118 }
1119
1120 UNLOCK_TRACELOG();
1121 }
1122#endif
1123 return NS_OK;
1124}
1125
1126
1127NS_IMETHODIMP
1128nsTraceRefcntImpl::LogDtor(void* aPtr,
1129 const char* aType,
1130 PRUint32 aInstanceSize)
1131{
1132#ifdef NS_BUILD_REFCNT_LOGGING
1133 ASSERT_ACTIVITY_IS_LEGAL;
1134 if (!gInitialized)
1135 InitTraceLog();
1136
1137 if (gLogging) {
1138 LOCK_TRACELOG();
1139
1140 if (gBloatLog) {
1141 BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
1142 if (entry) {
1143 entry->Dtor();
1144 }
1145 }
1146
1147 PRBool loggingThisType = (!gTypesToLog || LogThisType(aType));
1148 PRInt32 serialno = 0;
1149 if (gSerialNumbers && loggingThisType) {
1150 serialno = GetSerialNumber(aPtr, PR_FALSE);
1151 RecycleSerialNumberPtr(aPtr);
1152 }
1153
1154 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1155
1156 // (If we're on a losing architecture, don't do this because we'll be
1157 // using LogDeleteXPCOM instead to get file and line numbers.)
1158 if (gAllocLog && loggingThisType && loggingThisObject) {
1159 fprintf(gAllocLog, "\n<%s> 0x%08X %d Dtor (%d)\n",
1160 aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
1161 WalkTheStack(gAllocLog);
1162 }
1163
1164 UNLOCK_TRACELOG();
1165 }
1166#endif
1167 return NS_OK;
1168}
1169
1170
1171NS_IMETHODIMP
1172nsTraceRefcntImpl::LogAddCOMPtr(void* aCOMPtr,
1173 nsISupports* aObject)
1174{
1175#if defined(NS_BUILD_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1176 // Get the most-derived object.
1177 void *object = dynamic_cast<void *>(aObject);
1178
1179 // This is a very indirect way of finding out what the class is
1180 // of the object being logged. If we're logging a specific type,
1181 // then
1182 if (!gTypesToLog || !gSerialNumbers) {
1183 return NS_OK;
1184 }
1185 PRInt32 serialno = GetSerialNumber(object, PR_FALSE);
1186 if (serialno == 0) {
1187 return NS_OK;
1188 }
1189
1190 if (!gInitialized)
1191 InitTraceLog();
1192 if (gLogging) {
1193 LOCK_TRACELOG();
1194
1195 PRInt32* count = GetCOMPtrCount(object);
1196 if(count)
1197 (*count)++;
1198
1199 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1200
1201 if (gCOMPtrLog && loggingThisObject) {
1202 fprintf(gCOMPtrLog, "\n<?> 0x%08X %d nsCOMPtrAddRef %d 0x%08X\n",
1203 NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
1204 WalkTheStack(gCOMPtrLog);
1205 }
1206
1207 UNLOCK_TRACELOG();
1208 }
1209#endif
1210 return NS_OK;
1211}
1212
1213
1214NS_IMETHODIMP
1215nsTraceRefcntImpl::LogReleaseCOMPtr(void* aCOMPtr,
1216 nsISupports* aObject)
1217{
1218#if defined(NS_BUILD_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1219 // Get the most-derived object.
1220 void *object = dynamic_cast<void *>(aObject);
1221
1222 // This is a very indirect way of finding out what the class is
1223 // of the object being logged. If we're logging a specific type,
1224 // then
1225 if (!gTypesToLog || !gSerialNumbers) {
1226 return NS_OK;
1227 }
1228 PRInt32 serialno = GetSerialNumber(object, PR_FALSE);
1229 if (serialno == 0) {
1230 return NS_OK;
1231 }
1232
1233 if (!gInitialized)
1234 InitTraceLog();
1235 if (gLogging) {
1236 LOCK_TRACELOG();
1237
1238 PRInt32* count = GetCOMPtrCount(object);
1239 if(count)
1240 (*count)--;
1241
1242 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1243
1244 if (gCOMPtrLog && loggingThisObject) {
1245 fprintf(gCOMPtrLog, "\n<?> 0x%08X %d nsCOMPtrRelease %d 0x%08X\n",
1246 NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
1247 WalkTheStack(gCOMPtrLog);
1248 }
1249
1250 UNLOCK_TRACELOG();
1251 }
1252#endif
1253 return NS_OK;
1254}
1255
1256NS_COM void
1257nsTraceRefcntImpl::Startup()
1258{
1259#ifdef NS_BUILD_REFCNT_LOGGING
1260 SetActivityIsLegal(PR_TRUE);
1261#endif
1262}
1263
1264NS_COM void
1265nsTraceRefcntImpl::Shutdown()
1266{
1267#ifdef NS_BUILD_REFCNT_LOGGING
1268
1269 if (gBloatView) {
1270 PL_HashTableDestroy(gBloatView);
1271 gBloatView = nsnull;
1272 }
1273 if (gTypesToLog) {
1274 PL_HashTableDestroy(gTypesToLog);
1275 gTypesToLog = nsnull;
1276 }
1277 if (gObjectsToLog) {
1278 PL_HashTableDestroy(gObjectsToLog);
1279 gObjectsToLog = nsnull;
1280 }
1281 if (gSerialNumbers) {
1282 PL_HashTableDestroy(gSerialNumbers);
1283 gSerialNumbers = nsnull;
1284 }
1285
1286 SetActivityIsLegal(PR_FALSE);
1287
1288#endif
1289}
1290
1291NS_COM void
1292nsTraceRefcntImpl::SetActivityIsLegal(PRBool aLegal)
1293{
1294#ifdef NS_BUILD_REFCNT_LOGGING
1295 gActivityIsLegal = aLegal;
1296#endif
1297}
1298
1299
1300NS_METHOD
1301nsTraceRefcntImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
1302{
1303 *aInstancePtr = nsnull;
1304 nsITraceRefcnt* tracer = new nsTraceRefcntImpl();
1305 if (!tracer)
1306 return NS_ERROR_OUT_OF_MEMORY;
1307
1308 nsresult rv = tracer->QueryInterface(aIID, aInstancePtr);
1309 if (NS_FAILED(rv)) {
1310 delete tracer;
1311 }
1312
1313 return rv;
1314}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette