VirtualBox

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

Last change on this file since 103140 was 102458, checked in by vboxsync, 11 months ago

libs/xpcom: Get rid of PL_strcasecmp/PL_strncasecmp and replace with IPRT equivalents, bugref:10545

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