1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set cindent tabstop=4 expandtab shiftwidth=4: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * The Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2006
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : //
41 : // This file implements a garbage-cycle collector based on the paper
42 : //
43 : // Concurrent Cycle Collection in Reference Counted Systems
44 : // Bacon & Rajan (2001), ECOOP 2001 / Springer LNCS vol 2072
45 : //
46 : // We are not using the concurrent or acyclic cases of that paper; so
47 : // the green, red and orange colors are not used.
48 : //
49 : // The collector is based on tracking pointers of four colors:
50 : //
51 : // Black nodes are definitely live. If we ever determine a node is
52 : // black, it's ok to forget about, drop from our records.
53 : //
54 : // White nodes are definitely garbage cycles. Once we finish with our
55 : // scanning, we unlink all the white nodes and expect that by
56 : // unlinking them they will self-destruct (since a garbage cycle is
57 : // only keeping itself alive with internal links, by definition).
58 : //
59 : // Grey nodes are being scanned. Nodes that turn grey will turn
60 : // either black if we determine that they're live, or white if we
61 : // determine that they're a garbage cycle. After the main collection
62 : // algorithm there should be no grey nodes.
63 : //
64 : // Purple nodes are *candidates* for being scanned. They are nodes we
65 : // haven't begun scanning yet because they're not old enough, or we're
66 : // still partway through the algorithm.
67 : //
68 : // XPCOM objects participating in garbage-cycle collection are obliged
69 : // to inform us when they ought to turn purple; that is, when their
70 : // refcount transitions from N+1 -> N, for nonzero N. Furthermore we
71 : // require that *after* an XPCOM object has informed us of turning
72 : // purple, they will tell us when they either transition back to being
73 : // black (incremented refcount) or are ultimately deleted.
74 :
75 :
76 : // Safety:
77 : //
78 : // An XPCOM object is either scan-safe or scan-unsafe, purple-safe or
79 : // purple-unsafe.
80 : //
81 : // An object is scan-safe if:
82 : //
83 : // - It can be QI'ed to |nsXPCOMCycleCollectionParticipant|, though this
84 : // operation loses ISupports identity (like nsIClassInfo).
85 : // - The operation |traverse| on the resulting
86 : // nsXPCOMCycleCollectionParticipant does not cause *any* refcount
87 : // adjustment to occur (no AddRef / Release calls).
88 : //
89 : // An object is purple-safe if it satisfies the following properties:
90 : //
91 : // - The object is scan-safe.
92 : // - If the object calls |nsCycleCollector::suspect(this)|,
93 : // it will eventually call |nsCycleCollector::forget(this)|,
94 : // exactly once per call to |suspect|, before being destroyed.
95 : //
96 : // When we receive a pointer |ptr| via
97 : // |nsCycleCollector::suspect(ptr)|, we assume it is purple-safe. We
98 : // can check the scan-safety, but have no way to ensure the
99 : // purple-safety; objects must obey, or else the entire system falls
100 : // apart. Don't involve an object in this scheme if you can't
101 : // guarantee its purple-safety.
102 : //
103 : // When we have a scannable set of purple nodes ready, we begin
104 : // our walks. During the walks, the nodes we |traverse| should only
105 : // feed us more scan-safe nodes, and should not adjust the refcounts
106 : // of those nodes.
107 : //
108 : // We do not |AddRef| or |Release| any objects during scanning. We
109 : // rely on purple-safety of the roots that call |suspect| and
110 : // |forget| to hold, such that we will forget about a purple pointer
111 : // before it is destroyed. The pointers that are merely scan-safe,
112 : // we hold only for the duration of scanning, and there should be no
113 : // objects released from the scan-safe set during the scan (there
114 : // should be no threads involved).
115 : //
116 : // We *do* call |AddRef| and |Release| on every white object, on
117 : // either side of the calls to |Unlink|. This keeps the set of white
118 : // objects alive during the unlinking.
119 : //
120 :
121 : #if !defined(__MINGW32__)
122 : #ifdef WIN32
123 : #include <crtdbg.h>
124 : #include <errno.h>
125 : #endif
126 : #endif
127 :
128 : #include "base/process_util.h"
129 :
130 : /* This must occur *after* base/process_util.h to avoid typedefs conflicts. */
131 : #include "mozilla/Util.h"
132 :
133 : #include "nsCycleCollectionParticipant.h"
134 : #include "nsCycleCollectorUtils.h"
135 : #include "nsIProgrammingLanguage.h"
136 : #include "nsBaseHashtable.h"
137 : #include "nsHashKeys.h"
138 : #include "nsDeque.h"
139 : #include "nsCycleCollector.h"
140 : #include "nsThreadUtils.h"
141 : #include "prenv.h"
142 : #include "prprf.h"
143 : #include "plstr.h"
144 : #include "nsPrintfCString.h"
145 : #include "nsTArray.h"
146 : #include "nsIObserverService.h"
147 : #include "nsIConsoleService.h"
148 : #include "nsServiceManagerUtils.h"
149 : #include "nsThreadUtils.h"
150 : #include "nsTArray.h"
151 : #include "mozilla/Services.h"
152 : #include "nsICycleCollectorListener.h"
153 : #include "nsIXPConnect.h"
154 : #include "nsIJSRuntimeService.h"
155 : #include "nsIMemoryReporter.h"
156 : #include "xpcpublic.h"
157 : #include "nsXPCOMPrivate.h"
158 : #include "sampler.h"
159 : #include <stdio.h>
160 : #include <string.h>
161 : #ifdef WIN32
162 : #include <io.h>
163 : #include <process.h>
164 : #endif
165 :
166 : #ifdef XP_WIN
167 : #include <windows.h>
168 : #endif
169 :
170 : #include "mozilla/Mutex.h"
171 : #include "mozilla/CondVar.h"
172 : #include "mozilla/Telemetry.h"
173 :
174 : using namespace mozilla;
175 :
176 : //#define COLLECT_TIME_DEBUG
177 :
178 : #ifdef DEBUG_CC
179 : #define IF_DEBUG_CC_PARAM(_p) , _p
180 : #define IF_DEBUG_CC_ONLY_PARAM(_p) _p
181 : #else
182 : #define IF_DEBUG_CC_PARAM(_p)
183 : #define IF_DEBUG_CC_ONLY_PARAM(_p)
184 : #endif
185 :
186 : #define DEFAULT_SHUTDOWN_COLLECTIONS 5
187 : #ifdef DEBUG_CC
188 : #define SHUTDOWN_COLLECTIONS(params) params.mShutdownCollections
189 : #else
190 : #define SHUTDOWN_COLLECTIONS(params) DEFAULT_SHUTDOWN_COLLECTIONS
191 : #endif
192 :
193 : #if defined(XP_WIN)
194 : // Defined in nsThreadManager.cpp.
195 : extern DWORD gTLSThreadIDIndex;
196 : #elif defined(NS_TLS)
197 : // Defined in nsThreadManager.cpp.
198 : extern NS_TLS mozilla::threads::ID gTLSThreadID;
199 : #else
200 : PRThread* gCycleCollectorThread = nsnull;
201 : #endif
202 :
203 : // If true, always log cycle collector graphs.
204 : const bool gAlwaysLogCCGraphs = false;
205 :
206 : MOZ_NEVER_INLINE void
207 4356099 : CC_AbortIfNull(void *ptr)
208 : {
209 4356099 : if (!ptr)
210 0 : MOZ_Assert("ptr was null", __FILE__, __LINE__);
211 4356099 : }
212 :
213 : // Various parameters of this collector can be tuned using environment
214 : // variables.
215 :
216 : struct nsCycleCollectorParams
217 : {
218 : bool mDoNothing;
219 : bool mLogGraphs;
220 : #ifdef DEBUG_CC
221 : bool mReportStats;
222 : bool mHookMalloc;
223 : bool mFaultIsFatal;
224 : bool mLogPointers;
225 : PRUint32 mShutdownCollections;
226 : #endif
227 :
228 1419 : nsCycleCollectorParams() :
229 : #ifdef DEBUG_CC
230 : mDoNothing (PR_GetEnv("XPCOM_CC_DO_NOTHING") != NULL),
231 : mLogGraphs (gAlwaysLogCCGraphs ||
232 : PR_GetEnv("XPCOM_CC_DRAW_GRAPHS") != NULL),
233 : mReportStats (PR_GetEnv("XPCOM_CC_REPORT_STATS") != NULL),
234 : mHookMalloc (PR_GetEnv("XPCOM_CC_HOOK_MALLOC") != NULL),
235 : mFaultIsFatal (PR_GetEnv("XPCOM_CC_FAULT_IS_FATAL") != NULL),
236 : mLogPointers (PR_GetEnv("XPCOM_CC_LOG_POINTERS") != NULL),
237 :
238 : mShutdownCollections(DEFAULT_SHUTDOWN_COLLECTIONS)
239 : #else
240 : mDoNothing (false),
241 1419 : mLogGraphs (gAlwaysLogCCGraphs)
242 : #endif
243 : {
244 : #ifdef DEBUG_CC
245 : char *s = PR_GetEnv("XPCOM_CC_SHUTDOWN_COLLECTIONS");
246 : if (s)
247 : PR_sscanf(s, "%d", &mShutdownCollections);
248 : #endif
249 1419 : }
250 : };
251 :
252 : #ifdef DEBUG_CC
253 : // Various operations involving the collector are recorded in a
254 : // statistics table. These are for diagnostics.
255 :
256 : struct nsCycleCollectorStats
257 : {
258 : PRUint32 mFailedQI;
259 : PRUint32 mSuccessfulQI;
260 :
261 : PRUint32 mVisitedNode;
262 : PRUint32 mWalkedGraph;
263 : PRUint32 mCollectedBytes;
264 : PRUint32 mFreeCalls;
265 : PRUint32 mFreedBytes;
266 :
267 : PRUint32 mSetColorGrey;
268 : PRUint32 mSetColorBlack;
269 : PRUint32 mSetColorWhite;
270 :
271 : PRUint32 mFailedUnlink;
272 : PRUint32 mCollectedNode;
273 :
274 : PRUint32 mSuspectNode;
275 : PRUint32 mForgetNode;
276 : PRUint32 mFreedWhilePurple;
277 :
278 : PRUint32 mCollection;
279 :
280 : nsCycleCollectorStats()
281 : {
282 : memset(this, 0, sizeof(nsCycleCollectorStats));
283 : }
284 :
285 : void Dump()
286 : {
287 : fprintf(stderr, "\f\n");
288 : #define DUMP(entry) fprintf(stderr, "%30.30s: %-20.20d\n", #entry, entry)
289 : DUMP(mFailedQI);
290 : DUMP(mSuccessfulQI);
291 :
292 : DUMP(mVisitedNode);
293 : DUMP(mWalkedGraph);
294 : DUMP(mCollectedBytes);
295 : DUMP(mFreeCalls);
296 : DUMP(mFreedBytes);
297 :
298 : DUMP(mSetColorGrey);
299 : DUMP(mSetColorBlack);
300 : DUMP(mSetColorWhite);
301 :
302 : DUMP(mFailedUnlink);
303 : DUMP(mCollectedNode);
304 :
305 : DUMP(mSuspectNode);
306 : DUMP(mForgetNode);
307 : DUMP(mFreedWhilePurple);
308 :
309 : DUMP(mCollection);
310 : #undef DUMP
311 : }
312 : };
313 : #endif
314 :
315 : #ifdef DEBUG_CC
316 : static bool nsCycleCollector_shouldSuppress(nsISupports *s);
317 : static void InitMemHook(void);
318 : #endif
319 :
320 : #ifdef COLLECT_TIME_DEBUG
321 : class TimeLog
322 : {
323 : public:
324 : TimeLog() : mLastCheckpoint(TimeStamp::Now()) {}
325 :
326 : void
327 : Checkpoint(const char* aEvent)
328 : {
329 : TimeStamp now = TimeStamp::Now();
330 : PRUint32 dur = (PRUint32) ((now - mLastCheckpoint).ToMilliseconds());
331 : if (dur > 0) {
332 : printf("cc: %s took %dms\n", aEvent, dur);
333 : }
334 : mLastCheckpoint = now;
335 : }
336 :
337 : private:
338 : TimeStamp mLastCheckpoint;
339 : };
340 : #else
341 : class TimeLog
342 : {
343 : public:
344 9382 : TimeLog() {}
345 26647 : void Checkpoint(const char* aEvent) {}
346 : };
347 : #endif
348 :
349 :
350 : ////////////////////////////////////////////////////////////////////////
351 : // Base types
352 : ////////////////////////////////////////////////////////////////////////
353 :
354 : struct PtrInfo;
355 :
356 : class EdgePool
357 : {
358 : public:
359 : // EdgePool allocates arrays of void*, primarily to hold PtrInfo*.
360 : // However, at the end of a block, the last two pointers are a null
361 : // and then a void** pointing to the next block. This allows
362 : // EdgePool::Iterators to be a single word but still capable of crossing
363 : // block boundaries.
364 :
365 1419 : EdgePool()
366 : {
367 1419 : mSentinelAndBlocks[0].block = nsnull;
368 1419 : mSentinelAndBlocks[1].block = nsnull;
369 1419 : mNumBlocks = 0;
370 1419 : }
371 :
372 1419 : ~EdgePool()
373 : {
374 1419 : NS_ASSERTION(!mSentinelAndBlocks[0].block &&
375 : !mSentinelAndBlocks[1].block,
376 : "Didn't call Clear()?");
377 1419 : }
378 :
379 1925 : void Clear()
380 : {
381 1925 : Block *b = Blocks();
382 5784 : while (b) {
383 1934 : Block *next = b->Next();
384 : delete b;
385 1934 : NS_ASSERTION(mNumBlocks > 0,
386 : "Expected EdgePool mNumBlocks to be positive.");
387 1934 : mNumBlocks--;
388 1934 : b = next;
389 : }
390 :
391 1925 : mSentinelAndBlocks[0].block = nsnull;
392 1925 : mSentinelAndBlocks[1].block = nsnull;
393 1925 : }
394 :
395 : private:
396 : struct Block;
397 : union PtrInfoOrBlock {
398 : // Use a union to avoid reinterpret_cast and the ensuing
399 : // potential aliasing bugs.
400 : PtrInfo *ptrInfo;
401 : Block *block;
402 : };
403 : struct Block {
404 : enum { BlockSize = 16 * 1024 };
405 :
406 : PtrInfoOrBlock mPointers[BlockSize];
407 1934 : Block() {
408 1934 : mPointers[BlockSize - 2].block = nsnull; // sentinel
409 1934 : mPointers[BlockSize - 1].block = nsnull; // next block pointer
410 1934 : }
411 3868 : Block*& Next()
412 3868 : { return mPointers[BlockSize - 1].block; }
413 1934 : PtrInfoOrBlock* Start()
414 1934 : { return &mPointers[0]; }
415 1934 : PtrInfoOrBlock* End()
416 1934 : { return &mPointers[BlockSize - 2]; }
417 : };
418 :
419 : // Store the null sentinel so that we can have valid iterators
420 : // before adding any edges and without adding any blocks.
421 : PtrInfoOrBlock mSentinelAndBlocks[2];
422 : PRUint32 mNumBlocks;
423 :
424 3850 : Block*& Blocks() { return mSentinelAndBlocks[1].block; }
425 :
426 : public:
427 : class Iterator
428 : {
429 : public:
430 601885 : Iterator() : mPointer(nsnull) {}
431 603812 : Iterator(PtrInfoOrBlock *aPointer) : mPointer(aPointer) {}
432 1230322 : Iterator(const Iterator& aOther) : mPointer(aOther.mPointer) {}
433 :
434 1739228 : Iterator& operator++()
435 : {
436 1739228 : if (mPointer->ptrInfo == nsnull) {
437 : // Null pointer is a sentinel for link to the next block.
438 3844 : mPointer = (mPointer + 1)->block->mPointers;
439 : }
440 1739228 : ++mPointer;
441 1739228 : return *this;
442 : }
443 :
444 3478456 : PtrInfo* operator*() const
445 : {
446 3478456 : if (mPointer->ptrInfo == nsnull) {
447 : // Null pointer is a sentinel for link to the next block.
448 7688 : return (mPointer + 1)->block->mPointers->ptrInfo;
449 : }
450 3470768 : return mPointer->ptrInfo;
451 : }
452 : bool operator==(const Iterator& aOther) const
453 : { return mPointer == aOther.mPointer; }
454 2354389 : bool operator!=(const Iterator& aOther) const
455 2354389 : { return mPointer != aOther.mPointer; }
456 :
457 : private:
458 : PtrInfoOrBlock *mPointer;
459 : };
460 :
461 : class Builder;
462 : friend class Builder;
463 : class Builder {
464 : public:
465 1925 : Builder(EdgePool &aPool)
466 : : mCurrent(&aPool.mSentinelAndBlocks[0]),
467 : mBlockEnd(&aPool.mSentinelAndBlocks[0]),
468 1925 : mNextBlockPtr(&aPool.Blocks()),
469 3850 : mNumBlocks(aPool.mNumBlocks)
470 : {
471 1925 : }
472 :
473 603812 : Iterator Mark() { return Iterator(mCurrent); }
474 :
475 1725048 : void Add(PtrInfo* aEdge) {
476 1725048 : if (mCurrent == mBlockEnd) {
477 1934 : Block *b = new Block();
478 1934 : if (!b) {
479 : // This means we just won't collect (some) cycles.
480 0 : NS_NOTREACHED("out of memory, ignoring edges");
481 0 : return;
482 : }
483 1934 : *mNextBlockPtr = b;
484 1934 : mCurrent = b->Start();
485 1934 : mBlockEnd = b->End();
486 1934 : mNextBlockPtr = &b->Next();
487 1934 : mNumBlocks++;
488 : }
489 1725048 : (mCurrent++)->ptrInfo = aEdge;
490 : }
491 : private:
492 : // mBlockEnd points to space for null sentinel
493 : PtrInfoOrBlock *mCurrent, *mBlockEnd;
494 : Block **mNextBlockPtr;
495 : PRUint32 &mNumBlocks;
496 : };
497 :
498 0 : size_t BlocksSize() const {
499 0 : return sizeof(Block) * mNumBlocks;
500 : }
501 :
502 : };
503 :
504 : #ifdef DEBUG_CC
505 :
506 : struct ReversedEdge {
507 : PtrInfo *mTarget;
508 : nsCString *mEdgeName;
509 : ReversedEdge *mNext;
510 : };
511 :
512 : #endif
513 :
514 :
515 : enum NodeColor { black, white, grey };
516 :
517 : // This structure should be kept as small as possible; we may expect
518 : // hundreds of thousands of them to be allocated and touched
519 : // repeatedly during each cycle collection.
520 :
521 : struct PtrInfo
522 : {
523 : void *mPointer;
524 : nsCycleCollectionParticipant *mParticipant;
525 : PRUint32 mColor : 2;
526 : PRUint32 mInternalRefs : 30;
527 : PRUint32 mRefCount;
528 : private:
529 : EdgePool::Iterator mFirstChild;
530 :
531 : public:
532 : #ifdef DEBUG_CC
533 : size_t mBytes;
534 : char *mName;
535 : PRUint32 mLangID;
536 :
537 : // For finding roots in ExplainLiveExpectedGarbage (when there are
538 : // missing calls to suspect or failures to unlink).
539 : PRUint32 mSCCIndex; // strongly connected component
540 :
541 : // For finding roots in ExplainLiveExpectedGarbage (when nodes
542 : // expected to be garbage are black).
543 : ReversedEdge* mReversedEdges; // linked list
544 : PtrInfo* mShortestPathToExpectedGarbage;
545 : nsCString* mShortestPathToExpectedGarbageEdgeName;
546 :
547 : nsTArray<nsCString> mEdgeNames;
548 : #endif
549 :
550 601885 : PtrInfo(void *aPointer, nsCycleCollectionParticipant *aParticipant
551 : IF_DEBUG_CC_PARAM(PRUint32 aLangID)
552 : )
553 : : mPointer(aPointer),
554 : mParticipant(aParticipant),
555 : mColor(grey),
556 : mInternalRefs(0),
557 : mRefCount(0),
558 601885 : mFirstChild()
559 : #ifdef DEBUG_CC
560 : , mBytes(0),
561 : mName(nsnull),
562 : mLangID(aLangID),
563 : mSCCIndex(0),
564 : mReversedEdges(nsnull),
565 : mShortestPathToExpectedGarbage(nsnull),
566 : mShortestPathToExpectedGarbageEdgeName(nsnull)
567 : #endif
568 : {
569 601885 : }
570 :
571 : #ifdef DEBUG_CC
572 : void Destroy() {
573 : PL_strfree(mName);
574 : mEdgeNames.~nsTArray<nsCString>();
575 : }
576 : #endif
577 :
578 : // Allow NodePool::Block's constructor to compile.
579 : PtrInfo() {
580 : NS_NOTREACHED("should never be called");
581 : }
582 :
583 615161 : EdgePool::Iterator FirstChild()
584 : {
585 615161 : return mFirstChild;
586 : }
587 :
588 : // this PtrInfo must be part of a NodePool
589 615161 : EdgePool::Iterator LastChild()
590 : {
591 615161 : return (this + 1)->mFirstChild;
592 : }
593 :
594 601885 : void SetFirstChild(EdgePool::Iterator aFirstChild)
595 : {
596 601885 : mFirstChild = aFirstChild;
597 601885 : }
598 :
599 : // this PtrInfo must be part of a NodePool
600 1927 : void SetLastChild(EdgePool::Iterator aLastChild)
601 : {
602 1927 : (this + 1)->mFirstChild = aLastChild;
603 1927 : }
604 : };
605 :
606 : /**
607 : * A structure designed to be used like a linked list of PtrInfo, except
608 : * that allocates the PtrInfo 32K-at-a-time.
609 : */
610 : class NodePool
611 : {
612 : private:
613 : enum { BlockSize = 8 * 1024 }; // could be int template parameter
614 :
615 : struct Block {
616 : // We create and destroy Block using NS_Alloc/NS_Free rather
617 : // than new and delete to avoid calling its constructor and
618 : // destructor.
619 : Block() { NS_NOTREACHED("should never be called"); }
620 : ~Block() { NS_NOTREACHED("should never be called"); }
621 :
622 : Block* mNext;
623 : PtrInfo mEntries[BlockSize + 1]; // +1 to store last child of last node
624 : };
625 :
626 : public:
627 1419 : NodePool()
628 : : mBlocks(nsnull),
629 : mLast(nsnull),
630 1419 : mNumBlocks(0)
631 : {
632 1419 : }
633 :
634 1419 : ~NodePool()
635 : {
636 1419 : NS_ASSERTION(!mBlocks, "Didn't call Clear()?");
637 1419 : }
638 :
639 1925 : void Clear()
640 : {
641 : #ifdef DEBUG_CC
642 : {
643 : Enumerator queue(*this);
644 : while (!queue.IsDone()) {
645 : queue.GetNext()->Destroy();
646 : }
647 : }
648 : #endif
649 1925 : Block *b = mBlocks;
650 5777 : while (b) {
651 1927 : Block *n = b->mNext;
652 1927 : NS_Free(b);
653 1927 : NS_ASSERTION(mNumBlocks > 0,
654 : "Expected NodePool mNumBlocks to be positive.");
655 1927 : mNumBlocks--;
656 1927 : b = n;
657 : }
658 :
659 1925 : mBlocks = nsnull;
660 1925 : mLast = nsnull;
661 1925 : }
662 :
663 : class Builder;
664 : friend class Builder;
665 : class Builder {
666 : public:
667 1925 : Builder(NodePool& aPool)
668 : : mNextBlock(&aPool.mBlocks),
669 : mNext(aPool.mLast),
670 : mBlockEnd(nsnull),
671 1925 : mNumBlocks(aPool.mNumBlocks)
672 : {
673 1925 : NS_ASSERTION(aPool.mBlocks == nsnull && aPool.mLast == nsnull,
674 : "pool not empty");
675 1925 : }
676 601885 : PtrInfo *Add(void *aPointer, nsCycleCollectionParticipant *aParticipant
677 : IF_DEBUG_CC_PARAM(PRUint32 aLangID)
678 : )
679 : {
680 601885 : if (mNext == mBlockEnd) {
681 : Block *block;
682 3854 : if (!(*mNextBlock = block =
683 3854 : static_cast<Block*>(NS_Alloc(sizeof(Block)))))
684 0 : return nsnull;
685 1927 : mNext = block->mEntries;
686 1927 : mBlockEnd = block->mEntries + BlockSize;
687 1927 : block->mNext = nsnull;
688 1927 : mNextBlock = &block->mNext;
689 1927 : mNumBlocks++;
690 : }
691 : return new (mNext++) PtrInfo(aPointer, aParticipant
692 : IF_DEBUG_CC_PARAM(aLangID)
693 601885 : );
694 : }
695 : private:
696 : Block **mNextBlock;
697 : PtrInfo *&mNext;
698 : PtrInfo *mBlockEnd;
699 : PRUint32 &mNumBlocks;
700 : };
701 :
702 : class Enumerator;
703 : friend class Enumerator;
704 : class Enumerator {
705 : public:
706 5745 : Enumerator(NodePool& aPool)
707 : : mFirstBlock(aPool.mBlocks),
708 : mCurBlock(nsnull),
709 : mNext(nsnull),
710 : mBlockEnd(nsnull),
711 5745 : mLast(aPool.mLast)
712 : {
713 5745 : }
714 :
715 2536471 : bool IsDone() const
716 : {
717 2536471 : return mNext == mLast;
718 : }
719 :
720 601885 : bool AtBlockEnd() const
721 : {
722 601885 : return mNext == mBlockEnd;
723 : }
724 :
725 1328866 : PtrInfo* GetNext()
726 : {
727 1328866 : NS_ASSERTION(!IsDone(), "calling GetNext when done");
728 1328866 : if (mNext == mBlockEnd) {
729 5766 : Block *nextBlock = mCurBlock ? mCurBlock->mNext : mFirstBlock;
730 5766 : mNext = nextBlock->mEntries;
731 5766 : mBlockEnd = mNext + BlockSize;
732 5766 : mCurBlock = nextBlock;
733 : }
734 1328866 : return mNext++;
735 : }
736 : private:
737 : Block *mFirstBlock, *mCurBlock;
738 : // mNext is the next value we want to return, unless mNext == mBlockEnd
739 : // NB: mLast is a reference to allow enumerating while building!
740 : PtrInfo *mNext, *mBlockEnd, *&mLast;
741 : };
742 :
743 0 : size_t BlocksSize() const {
744 0 : return sizeof(Block) * mNumBlocks;
745 : }
746 :
747 : private:
748 : Block *mBlocks;
749 : PtrInfo *mLast;
750 : PRUint32 mNumBlocks;
751 : };
752 :
753 :
754 : struct WeakMapping
755 0 : {
756 : // map and key will be null if the corresponding objects are GC marked
757 : PtrInfo *mMap;
758 : PtrInfo *mKey;
759 : PtrInfo *mVal;
760 : };
761 :
762 : class GCGraphBuilder;
763 :
764 : struct GCGraph
765 : {
766 : NodePool mNodes;
767 : EdgePool mEdges;
768 : nsTArray<WeakMapping> mWeakMaps;
769 : PRUint32 mRootCount;
770 : #ifdef DEBUG_CC
771 : ReversedEdge *mReversedEdges;
772 : #endif
773 :
774 1419 : GCGraph() : mRootCount(0) {
775 1419 : }
776 1419 : ~GCGraph() {
777 1419 : }
778 :
779 0 : size_t BlocksSize() const {
780 0 : return mNodes.BlocksSize() + mEdges.BlocksSize();
781 : }
782 :
783 : };
784 :
785 : // XXX Would be nice to have an nsHashSet<KeyType> API that has
786 : // Add/Remove/Has rather than PutEntry/RemoveEntry/GetEntry.
787 : typedef nsTHashtable<nsVoidPtrHashKey> PointerSet;
788 :
789 : static inline void
790 : ToParticipant(nsISupports *s, nsXPCOMCycleCollectionParticipant **cp);
791 :
792 : struct nsPurpleBuffer
793 : {
794 : private:
795 : struct Block {
796 : Block *mNext;
797 : nsPurpleBufferEntry mEntries[255];
798 :
799 2187 : Block() : mNext(nsnull) {}
800 : };
801 : public:
802 : // This class wraps a linked list of the elements in the purple
803 : // buffer.
804 :
805 : nsCycleCollectorParams &mParams;
806 : PRUint32 mNumBlocksAlloced;
807 : PRUint32 mCount;
808 : Block mFirstBlock;
809 : nsPurpleBufferEntry *mFreeList;
810 :
811 : // For objects compiled against Gecko 1.9 and 1.9.1.
812 : PointerSet mCompatObjects;
813 : #ifdef DEBUG_CC
814 : PointerSet mNormalObjects; // duplicates our blocks
815 : nsCycleCollectorStats &mStats;
816 : #endif
817 :
818 : #ifdef DEBUG_CC
819 : nsPurpleBuffer(nsCycleCollectorParams ¶ms,
820 : nsCycleCollectorStats &stats)
821 : : mParams(params),
822 : mStats(stats)
823 : {
824 : InitBlocks();
825 : mNormalObjects.Init();
826 : mCompatObjects.Init();
827 : }
828 : #else
829 1419 : nsPurpleBuffer(nsCycleCollectorParams ¶ms)
830 1419 : : mParams(params)
831 : {
832 1419 : InitBlocks();
833 1419 : mCompatObjects.Init();
834 1419 : }
835 : #endif
836 :
837 1419 : ~nsPurpleBuffer()
838 1419 : {
839 1419 : FreeBlocks();
840 1419 : }
841 :
842 3344 : void InitBlocks()
843 : {
844 3344 : mNumBlocksAlloced = 0;
845 3344 : mCount = 0;
846 3344 : mFreeList = nsnull;
847 3344 : StartBlock(&mFirstBlock);
848 3344 : }
849 :
850 4112 : void StartBlock(Block *aBlock)
851 : {
852 4112 : NS_ABORT_IF_FALSE(!mFreeList, "should not have free list");
853 :
854 : // Put all the entries in the block on the free list.
855 4112 : nsPurpleBufferEntry *entries = aBlock->mEntries;
856 4112 : mFreeList = entries;
857 1048560 : for (PRUint32 i = 1; i < ArrayLength(aBlock->mEntries); ++i) {
858 1044448 : entries[i - 1].mNextInFreeList =
859 1044448 : (nsPurpleBufferEntry*)(PRUword(entries + i) | 1);
860 : }
861 4112 : entries[ArrayLength(aBlock->mEntries) - 1].mNextInFreeList =
862 4112 : (nsPurpleBufferEntry*)1;
863 4112 : }
864 :
865 3344 : void FreeBlocks()
866 : {
867 3344 : if (mCount > 0)
868 0 : UnmarkRemainingPurple(&mFirstBlock);
869 3344 : Block *b = mFirstBlock.mNext;
870 7456 : while (b) {
871 768 : if (mCount > 0)
872 0 : UnmarkRemainingPurple(b);
873 768 : Block *next = b->mNext;
874 : delete b;
875 768 : b = next;
876 768 : NS_ASSERTION(mNumBlocksAlloced > 0,
877 : "Expected positive mNumBlocksAlloced.");
878 768 : mNumBlocksAlloced--;
879 : }
880 3344 : mFirstBlock.mNext = nsnull;
881 3344 : }
882 :
883 0 : void UnmarkRemainingPurple(Block *b)
884 : {
885 0 : for (nsPurpleBufferEntry *e = b->mEntries,
886 0 : *eEnd = ArrayEnd(b->mEntries);
887 : e != eEnd; ++e) {
888 0 : if (!(PRUword(e->mObject) & PRUword(1))) {
889 : // This is a real entry (rather than something on the
890 : // free list).
891 0 : if (e->mObject) {
892 : nsXPCOMCycleCollectionParticipant *cp;
893 0 : ToParticipant(e->mObject, &cp);
894 :
895 0 : cp->UnmarkIfPurple(e->mObject);
896 : }
897 :
898 0 : if (--mCount == 0)
899 0 : break;
900 : }
901 : }
902 0 : }
903 :
904 : void SelectPointers(GCGraphBuilder &builder);
905 :
906 : // RemoveSkippable removes entries from the purple buffer if
907 : // nsPurpleBufferEntry::mObject is null or if the object's
908 : // nsXPCOMCycleCollectionParticipant::CanSkip() returns true.
909 : // If removeChildlessNodes is true, then any nodes in the purple buffer
910 : // that will have no children in the cycle collector graph will also be
911 : // removed. CanSkip() may be run on these children.
912 : void RemoveSkippable(bool removeChildlessNodes);
913 :
914 : #ifdef DEBUG_CC
915 : void NoteAll(GCGraphBuilder &builder);
916 :
917 : bool Exists(void *p) const
918 : {
919 : return mNormalObjects.GetEntry(p) || mCompatObjects.GetEntry(p);
920 : }
921 : #endif
922 :
923 1489498 : nsPurpleBufferEntry* NewEntry()
924 : {
925 1489498 : if (!mFreeList) {
926 768 : Block *b = new Block;
927 768 : if (!b) {
928 0 : return nsnull;
929 : }
930 768 : mNumBlocksAlloced++;
931 768 : StartBlock(b);
932 :
933 : // Add the new block as the second block in the list.
934 768 : b->mNext = mFirstBlock.mNext;
935 768 : mFirstBlock.mNext = b;
936 : }
937 :
938 1489498 : nsPurpleBufferEntry *e = mFreeList;
939 : mFreeList = (nsPurpleBufferEntry*)
940 1489498 : (PRUword(mFreeList->mNextInFreeList) & ~PRUword(1));
941 1489498 : return e;
942 : }
943 :
944 1489498 : nsPurpleBufferEntry* Put(nsISupports *p)
945 : {
946 1489498 : nsPurpleBufferEntry *e = NewEntry();
947 1489498 : if (!e) {
948 0 : return nsnull;
949 : }
950 :
951 1489498 : ++mCount;
952 :
953 1489498 : e->mObject = p;
954 :
955 : #ifdef DEBUG_CC
956 : mNormalObjects.PutEntry(p);
957 : #endif
958 :
959 : // Caller is responsible for filling in result's mRefCnt.
960 1489498 : return e;
961 : }
962 :
963 1489498 : void Remove(nsPurpleBufferEntry *e)
964 : {
965 1489498 : NS_ASSERTION(mCount != 0, "must have entries");
966 :
967 : #ifdef DEBUG_CC
968 : mNormalObjects.RemoveEntry(e->mObject);
969 : #endif
970 :
971 : e->mNextInFreeList =
972 1489498 : (nsPurpleBufferEntry*)(PRUword(mFreeList) | PRUword(1));
973 1489498 : mFreeList = e;
974 :
975 1489498 : --mCount;
976 1489498 : }
977 :
978 0 : bool PutCompatObject(nsISupports *p)
979 : {
980 0 : ++mCount;
981 0 : return !!mCompatObjects.PutEntry(p);
982 : }
983 :
984 0 : void RemoveCompatObject(nsISupports *p)
985 : {
986 0 : --mCount;
987 0 : mCompatObjects.RemoveEntry(p);
988 0 : }
989 :
990 23251 : PRUint32 Count() const
991 : {
992 23251 : return mCount;
993 : }
994 :
995 0 : size_t BlocksSize() const
996 : {
997 0 : return sizeof(Block) * mNumBlocksAlloced;
998 : }
999 :
1000 : };
1001 :
1002 : struct CallbackClosure
1003 : {
1004 0 : CallbackClosure(nsPurpleBuffer *aPurpleBuffer, GCGraphBuilder &aBuilder)
1005 : : mPurpleBuffer(aPurpleBuffer),
1006 0 : mBuilder(aBuilder)
1007 : {
1008 0 : }
1009 : nsPurpleBuffer *mPurpleBuffer;
1010 : GCGraphBuilder &mBuilder;
1011 : };
1012 :
1013 : static bool
1014 : AddPurpleRoot(GCGraphBuilder &builder, nsISupports *root);
1015 :
1016 : static PLDHashOperator
1017 0 : selectionCallback(nsVoidPtrHashKey* key, void* userArg)
1018 : {
1019 0 : CallbackClosure *closure = static_cast<CallbackClosure*>(userArg);
1020 0 : if (AddPurpleRoot(closure->mBuilder,
1021 : static_cast<nsISupports *>(
1022 0 : const_cast<void*>(key->GetKey()))))
1023 0 : return PL_DHASH_REMOVE;
1024 :
1025 0 : return PL_DHASH_NEXT;
1026 : }
1027 :
1028 : void
1029 1925 : nsPurpleBuffer::SelectPointers(GCGraphBuilder &aBuilder)
1030 : {
1031 : #ifdef DEBUG_CC
1032 : // Can't use mCount here, since it may include null entries.
1033 : PRUint32 realCount = 0;
1034 : for (Block *b = &mFirstBlock; b; b = b->mNext) {
1035 : for (nsPurpleBufferEntry *e = b->mEntries,
1036 : *eEnd = ArrayEnd(b->mEntries);
1037 : e != eEnd; ++e) {
1038 : if (!(PRUword(e->mObject) & PRUword(1))) {
1039 : if (e->mObject) {
1040 : ++realCount;
1041 : }
1042 : }
1043 : }
1044 : }
1045 :
1046 : NS_ABORT_IF_FALSE(mCompatObjects.Count() + mNormalObjects.Count() ==
1047 : realCount,
1048 : "count out of sync");
1049 : #endif
1050 :
1051 1925 : if (mCompatObjects.Count()) {
1052 0 : mCount -= mCompatObjects.Count();
1053 0 : CallbackClosure closure(this, aBuilder);
1054 0 : mCompatObjects.EnumerateEntries(selectionCallback, &closure);
1055 0 : mCount += mCompatObjects.Count(); // in case of allocation failure
1056 : }
1057 :
1058 : // Walk through all the blocks.
1059 4618 : for (Block *b = &mFirstBlock; b; b = b->mNext) {
1060 692101 : for (nsPurpleBufferEntry *e = b->mEntries,
1061 2693 : *eEnd = ArrayEnd(b->mEntries);
1062 : e != eEnd; ++e) {
1063 686715 : if (!(PRUword(e->mObject) & PRUword(1))) {
1064 : // This is a real entry (rather than something on the
1065 : // free list).
1066 115052 : if (!e->mObject || AddPurpleRoot(aBuilder, e->mObject)) {
1067 115052 : Remove(e);
1068 : }
1069 : }
1070 : }
1071 : }
1072 :
1073 1925 : NS_WARN_IF_FALSE(mCount == 0, "AddPurpleRoot failed");
1074 1925 : if (mCount == 0) {
1075 1925 : FreeBlocks();
1076 1925 : InitBlocks();
1077 : }
1078 1925 : }
1079 :
1080 :
1081 :
1082 : ////////////////////////////////////////////////////////////////////////
1083 : // Implement the LanguageRuntime interface for C++/XPCOM
1084 : ////////////////////////////////////////////////////////////////////////
1085 :
1086 :
1087 : struct nsCycleCollectionXPCOMRuntime :
1088 : public nsCycleCollectionLanguageRuntime
1089 1419 : {
1090 1925 : nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
1091 : bool explainLiveExpectedGarbage)
1092 : {
1093 1925 : return NS_OK;
1094 : }
1095 :
1096 1910 : nsresult FinishTraverse()
1097 : {
1098 1910 : return NS_OK;
1099 : }
1100 :
1101 1925 : nsresult FinishCycleCollection()
1102 : {
1103 1925 : return NS_OK;
1104 : }
1105 :
1106 : inline nsCycleCollectionParticipant *ToParticipant(void *p);
1107 :
1108 : #ifdef DEBUG_CC
1109 : virtual void PrintAllReferencesTo(void *p) {}
1110 : #endif
1111 : };
1112 :
1113 : struct nsCycleCollector
1114 : {
1115 : bool mCollectionInProgress;
1116 : bool mScanInProgress;
1117 : bool mFollowupCollection;
1118 : nsCycleCollectorResults *mResults;
1119 : TimeStamp mCollectionStart;
1120 :
1121 : nsCycleCollectionLanguageRuntime *mRuntimes[nsIProgrammingLanguage::MAX+1];
1122 : nsCycleCollectionXPCOMRuntime mXPCOMRuntime;
1123 :
1124 : GCGraph mGraph;
1125 :
1126 : nsCycleCollectorParams mParams;
1127 :
1128 : nsTArray<PtrInfo*> *mWhiteNodes;
1129 : PRUint32 mWhiteNodeCount;
1130 :
1131 : // mVisitedRefCounted and mVisitedGCed are only used for telemetry
1132 : PRUint32 mVisitedRefCounted;
1133 : PRUint32 mVisitedGCed;
1134 :
1135 : nsPurpleBuffer mPurpleBuf;
1136 :
1137 : CC_BeforeUnlinkCallback mBeforeUnlinkCB;
1138 : CC_ForgetSkippableCallback mForgetSkippableCB;
1139 :
1140 : void RegisterRuntime(PRUint32 langID,
1141 : nsCycleCollectionLanguageRuntime *rt);
1142 : nsCycleCollectionLanguageRuntime * GetRuntime(PRUint32 langID);
1143 : void ForgetRuntime(PRUint32 langID);
1144 :
1145 : void SelectPurple(GCGraphBuilder &builder);
1146 : void MarkRoots(GCGraphBuilder &builder);
1147 : void ScanRoots();
1148 : void ScanWeakMaps();
1149 :
1150 : void ForgetSkippable(bool removeChildlessNodes);
1151 :
1152 : // returns whether anything was collected
1153 : bool CollectWhite(nsICycleCollectorListener *aListener);
1154 :
1155 : nsCycleCollector();
1156 : ~nsCycleCollector();
1157 :
1158 : // The first pair of Suspect and Forget functions are only used by
1159 : // old XPCOM binary components.
1160 : bool Suspect(nsISupports *n);
1161 : bool Forget(nsISupports *n);
1162 : nsPurpleBufferEntry* Suspect2(nsISupports *n);
1163 : bool Forget2(nsPurpleBufferEntry *e);
1164 :
1165 : void Collect(nsCycleCollectorResults *aResults,
1166 : PRUint32 aTryCollections,
1167 : nsICycleCollectorListener *aListener);
1168 :
1169 : // Prepare for and cleanup after one or more collection(s).
1170 : bool PrepareForCollection(nsCycleCollectorResults *aResults,
1171 : nsTArray<PtrInfo*> *aWhiteNodes);
1172 : void GCIfNeeded(bool aForceGC);
1173 : void CleanupAfterCollection();
1174 :
1175 : // Start and finish an individual collection.
1176 : bool BeginCollection(nsICycleCollectorListener *aListener);
1177 : bool FinishCollection(nsICycleCollectorListener *aListener);
1178 :
1179 : PRUint32 SuspectedCount();
1180 : void Shutdown();
1181 :
1182 1925 : void ClearGraph()
1183 : {
1184 1925 : mGraph.mNodes.Clear();
1185 1925 : mGraph.mEdges.Clear();
1186 1925 : mGraph.mWeakMaps.Clear();
1187 1925 : mGraph.mRootCount = 0;
1188 1925 : }
1189 :
1190 : #ifdef DEBUG_CC
1191 : nsCycleCollectorStats mStats;
1192 :
1193 : FILE *mPtrLog;
1194 :
1195 : void Allocated(void *n, size_t sz);
1196 : void Freed(void *n);
1197 :
1198 : void LogPurpleRemoval(void* aObject);
1199 :
1200 : void ExplainLiveExpectedGarbage();
1201 : bool CreateReversedEdges();
1202 : void DestroyReversedEdges();
1203 : void ShouldBeFreed(nsISupports *n);
1204 : void WasFreed(nsISupports *n);
1205 : PointerSet mExpectedGarbage;
1206 : #endif
1207 : };
1208 :
1209 :
1210 : /**
1211 : * GraphWalker is templatized over a Visitor class that must provide
1212 : * the following two methods:
1213 : *
1214 : * bool ShouldVisitNode(PtrInfo const *pi);
1215 : * void VisitNode(PtrInfo *pi);
1216 : */
1217 : template <class Visitor>
1218 : class GraphWalker
1219 : {
1220 : private:
1221 : Visitor mVisitor;
1222 :
1223 : void DoWalk(nsDeque &aQueue);
1224 :
1225 : public:
1226 : void Walk(PtrInfo *s0);
1227 : void WalkFromRoots(GCGraph &aGraph);
1228 : // copy-constructing the visitor should be cheap, and less
1229 : // indirection than using a reference
1230 14693 : GraphWalker(const Visitor aVisitor) : mVisitor(aVisitor) {}
1231 : };
1232 :
1233 :
1234 : ////////////////////////////////////////////////////////////////////////
1235 : // The static collector object
1236 : ////////////////////////////////////////////////////////////////////////
1237 :
1238 :
1239 : static nsCycleCollector *sCollector = nsnull;
1240 :
1241 :
1242 : ////////////////////////////////////////////////////////////////////////
1243 : // Utility functions
1244 : ////////////////////////////////////////////////////////////////////////
1245 :
1246 0 : class CCRunnableFaultReport : public nsRunnable {
1247 : public:
1248 0 : CCRunnableFaultReport(const nsCString& report)
1249 0 : {
1250 0 : CopyUTF8toUTF16(report, mReport);
1251 0 : }
1252 :
1253 0 : NS_IMETHOD Run() {
1254 0 : nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1255 0 : if (obs) {
1256 0 : obs->NotifyObservers(nsnull, "cycle-collector-fault",
1257 0 : mReport.get());
1258 : }
1259 :
1260 : nsCOMPtr<nsIConsoleService> cons =
1261 0 : do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1262 0 : if (cons) {
1263 0 : cons->LogStringMessage(mReport.get());
1264 : }
1265 0 : return NS_OK;
1266 : }
1267 :
1268 : private:
1269 : nsString mReport;
1270 : };
1271 :
1272 : static void
1273 0 : Fault(const char *msg, const void *ptr=nsnull)
1274 : {
1275 : #ifdef DEBUG_CC
1276 : // This should be nearly impossible, but just in case.
1277 : if (!sCollector)
1278 : return;
1279 :
1280 : if (sCollector->mParams.mFaultIsFatal) {
1281 :
1282 : if (ptr)
1283 : printf("Fatal fault in cycle collector: %s (ptr: %p)\n", msg, ptr);
1284 : else
1285 : printf("Fatal fault in cycle collector: %s\n", msg);
1286 :
1287 : exit(1);
1288 : }
1289 : #endif
1290 :
1291 : nsPrintfCString str(256, "Fault in cycle collector: %s (ptr: %p)\n",
1292 0 : msg, ptr);
1293 0 : NS_NOTREACHED(str.get());
1294 :
1295 : // When faults are not fatal, we assume we're running in a
1296 : // production environment and we therefore want to disable the
1297 : // collector on a fault. This will unfortunately cause the browser
1298 : // to leak pretty fast wherever creates cyclical garbage, but it's
1299 : // probably a better user experience than crashing. Besides, we
1300 : // *should* never hit a fault.
1301 :
1302 0 : sCollector->mParams.mDoNothing = true;
1303 :
1304 : // Report to observers off an event so we don't run JS under GC
1305 : // (which is where we might be right now).
1306 0 : nsCOMPtr<nsIRunnable> ev = new CCRunnableFaultReport(str);
1307 0 : NS_DispatchToMainThread(ev);
1308 0 : }
1309 :
1310 : #ifdef DEBUG_CC
1311 : static void
1312 : Fault(const char *msg, PtrInfo *pi)
1313 : {
1314 : printf("Fault in cycle collector: %s\n"
1315 : " while operating on pointer %p %s\n",
1316 : msg, pi->mPointer, pi->mName);
1317 : if (pi->mInternalRefs) {
1318 : printf(" which has internal references from:\n");
1319 : NodePool::Enumerator queue(sCollector->mGraph.mNodes);
1320 : while (!queue.IsDone()) {
1321 : PtrInfo *ppi = queue.GetNext();
1322 : for (EdgePool::Iterator e = ppi->FirstChild(),
1323 : e_end = ppi->LastChild();
1324 : e != e_end; ++e) {
1325 : if (*e == pi) {
1326 : printf(" %p %s\n", ppi->mPointer, ppi->mName);
1327 : }
1328 : }
1329 : }
1330 : }
1331 :
1332 : Fault(msg, pi->mPointer);
1333 : }
1334 : #else
1335 : inline void
1336 0 : Fault(const char *msg, PtrInfo *pi)
1337 : {
1338 0 : Fault(msg, pi->mPointer);
1339 0 : }
1340 : #endif
1341 :
1342 : static inline void
1343 2839968 : AbortIfOffMainThreadIfCheckFast()
1344 : {
1345 : #if defined(XP_WIN) || defined(NS_TLS)
1346 2839968 : if (!NS_IsMainThread() && !NS_IsCycleCollectorThread()) {
1347 0 : NS_RUNTIMEABORT("Main-thread-only object used off the main thread");
1348 : }
1349 : #endif
1350 2839968 : }
1351 :
1352 : static nsISupports *
1353 878289 : canonicalize(nsISupports *in)
1354 : {
1355 : nsISupports* child;
1356 : in->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
1357 878289 : reinterpret_cast<void**>(&child));
1358 878289 : return child;
1359 : }
1360 :
1361 : static inline void
1362 2360843 : ToParticipant(nsISupports *s, nsXPCOMCycleCollectionParticipant **cp)
1363 : {
1364 : // We use QI to move from an nsISupports to an
1365 : // nsXPCOMCycleCollectionParticipant, which is a per-class singleton helper
1366 : // object that implements traversal and unlinking logic for the nsISupports
1367 : // in question.
1368 2360843 : CallQueryInterface(s, cp);
1369 : #ifdef DEBUG_CC
1370 : if (cp)
1371 : ++sCollector->mStats.mSuccessfulQI;
1372 : else
1373 : ++sCollector->mStats.mFailedQI;
1374 : #endif
1375 2360843 : }
1376 :
1377 : nsCycleCollectionParticipant *
1378 0 : nsCycleCollectionXPCOMRuntime::ToParticipant(void *p)
1379 : {
1380 : nsXPCOMCycleCollectionParticipant *cp;
1381 0 : ::ToParticipant(static_cast<nsISupports*>(p), &cp);
1382 0 : return cp;
1383 : }
1384 :
1385 :
1386 : template <class Visitor>
1387 : MOZ_NEVER_INLINE void
1388 12783 : GraphWalker<Visitor>::Walk(PtrInfo *s0)
1389 : {
1390 25566 : nsDeque queue;
1391 12783 : CC_AbortIfNull(s0);
1392 12783 : queue.Push(s0);
1393 12783 : DoWalk(queue);
1394 12783 : }
1395 :
1396 : template <class Visitor>
1397 : MOZ_NEVER_INLINE void
1398 1910 : GraphWalker<Visitor>::WalkFromRoots(GCGraph& aGraph)
1399 : {
1400 3820 : nsDeque queue;
1401 1910 : NodePool::Enumerator etor(aGraph.mNodes);
1402 127006 : for (PRUint32 i = 0; i < aGraph.mRootCount; ++i) {
1403 125096 : PtrInfo *pi = etor.GetNext();
1404 125096 : CC_AbortIfNull(pi);
1405 125096 : queue.Push(pi);
1406 : }
1407 1910 : DoWalk(queue);
1408 1910 : }
1409 :
1410 : template <class Visitor>
1411 : MOZ_NEVER_INLINE void
1412 14693 : GraphWalker<Visitor>::DoWalk(nsDeque &aQueue)
1413 : {
1414 : // Use a aQueue to match the breadth-first traversal used when we
1415 : // built the graph, for hopefully-better locality.
1416 1906493 : while (aQueue.GetSize() > 0) {
1417 1877107 : PtrInfo *pi = static_cast<PtrInfo*>(aQueue.PopFront());
1418 1877107 : CC_AbortIfNull(pi);
1419 :
1420 1877107 : if (mVisitor.ShouldVisitNode(pi)) {
1421 615161 : mVisitor.VisitNode(pi);
1422 2969550 : for (EdgePool::Iterator child = pi->FirstChild(),
1423 615161 : child_end = pi->LastChild();
1424 : child != child_end; ++child) {
1425 1739228 : CC_AbortIfNull(*child);
1426 1739228 : aQueue.Push(*child);
1427 : }
1428 : }
1429 : };
1430 :
1431 : #ifdef DEBUG_CC
1432 : sCollector->mStats.mWalkedGraph++;
1433 : #endif
1434 14693 : }
1435 :
1436 : struct CCGraphDescriber
1437 0 : {
1438 0 : CCGraphDescriber()
1439 0 : : mAddress("0x"), mToAddress("0x"), mCnt(0), mType(eUnknown) {}
1440 :
1441 : enum Type
1442 : {
1443 : eRefCountedObject,
1444 : eGCedObject,
1445 : eGCMarkedObject,
1446 : eEdge,
1447 : eRoot,
1448 : eGarbage,
1449 : eUnknown
1450 : };
1451 :
1452 : nsCString mAddress;
1453 : nsCString mToAddress;
1454 : nsCString mName;
1455 : PRUint32 mCnt;
1456 : Type mType;
1457 : };
1458 :
1459 : class nsCycleCollectorLogger : public nsICycleCollectorListener
1460 : {
1461 : public:
1462 0 : nsCycleCollectorLogger() :
1463 : mStream(nsnull), mWantAllTraces(false),
1464 : mDisableLog(false), mWantAfterProcessing(false),
1465 0 : mNextIndex(0)
1466 : {
1467 0 : }
1468 0 : ~nsCycleCollectorLogger()
1469 0 : {
1470 0 : if (mStream) {
1471 0 : fclose(mStream);
1472 : }
1473 0 : }
1474 : NS_DECL_ISUPPORTS
1475 :
1476 0 : NS_IMETHOD AllTraces(nsICycleCollectorListener** aListener)
1477 : {
1478 0 : mWantAllTraces = true;
1479 0 : NS_ADDREF(*aListener = this);
1480 0 : return NS_OK;
1481 : }
1482 :
1483 0 : NS_IMETHOD GetWantAllTraces(bool* aAllTraces)
1484 : {
1485 0 : *aAllTraces = mWantAllTraces;
1486 0 : return NS_OK;
1487 : }
1488 :
1489 0 : NS_IMETHOD GetDisableLog(bool* aDisableLog)
1490 : {
1491 0 : *aDisableLog = mDisableLog;
1492 0 : return NS_OK;
1493 : }
1494 :
1495 0 : NS_IMETHOD SetDisableLog(bool aDisableLog)
1496 : {
1497 0 : mDisableLog = aDisableLog;
1498 0 : return NS_OK;
1499 : }
1500 :
1501 0 : NS_IMETHOD GetWantAfterProcessing(bool* aWantAfterProcessing)
1502 : {
1503 0 : *aWantAfterProcessing = mWantAfterProcessing;
1504 0 : return NS_OK;
1505 : }
1506 :
1507 0 : NS_IMETHOD SetWantAfterProcessing(bool aWantAfterProcessing)
1508 : {
1509 0 : mWantAfterProcessing = aWantAfterProcessing;
1510 0 : return NS_OK;
1511 : }
1512 :
1513 0 : NS_IMETHOD Begin()
1514 : {
1515 0 : mCurrentAddress.AssignLiteral("0x");
1516 0 : mDescribers.Clear();
1517 0 : mNextIndex = 0;
1518 0 : if (mDisableLog) {
1519 0 : return NS_OK;
1520 : }
1521 0 : char basename[MAXPATHLEN] = {'\0'};
1522 0 : char ccname[MAXPATHLEN] = {'\0'};
1523 : #ifdef XP_WIN
1524 : // On Windows, tmpnam returns useless stuff, such as "\\s164.".
1525 : // Therefore we need to call the APIs directly.
1526 : GetTempPathA(mozilla::ArrayLength(basename), basename);
1527 : #else
1528 0 : tmpnam(basename);
1529 0 : char *lastSlash = strrchr(basename, XPCOM_FILE_PATH_SEPARATOR[0]);
1530 0 : if (lastSlash) {
1531 0 : *lastSlash = '\0';
1532 : }
1533 : #endif
1534 :
1535 0 : ++gLogCounter;
1536 :
1537 : #ifdef DEBUG
1538 : // Dump the JS heap.
1539 0 : char gcname[MAXPATHLEN] = {'\0'};
1540 : sprintf(gcname, "%s%sgc-edges-%d.%d.log", basename,
1541 : XPCOM_FILE_PATH_SEPARATOR,
1542 0 : gLogCounter, base::GetCurrentProcId());
1543 :
1544 0 : FILE* gcDumpFile = fopen(gcname, "w");
1545 0 : if (!gcDumpFile)
1546 0 : return NS_ERROR_FAILURE;
1547 0 : xpc::DumpJSHeap(gcDumpFile);
1548 0 : fclose(gcDumpFile);
1549 : #endif
1550 :
1551 : // Open a file for dumping the CC graph.
1552 : sprintf(ccname, "%s%scc-edges-%d.%d.log", basename,
1553 : XPCOM_FILE_PATH_SEPARATOR,
1554 0 : gLogCounter, base::GetCurrentProcId());
1555 0 : mStream = fopen(ccname, "w");
1556 0 : if (!mStream)
1557 0 : return NS_ERROR_FAILURE;
1558 :
1559 : nsCOMPtr<nsIConsoleService> cs =
1560 0 : do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1561 0 : if (cs) {
1562 0 : cs->LogStringMessage(NS_ConvertUTF8toUTF16(ccname).get());
1563 : #ifdef DEBUG
1564 0 : cs->LogStringMessage(NS_ConvertUTF8toUTF16(gcname).get());
1565 : #endif
1566 : }
1567 :
1568 0 : return NS_OK;
1569 : }
1570 0 : NS_IMETHOD NoteRefCountedObject(PRUint64 aAddress, PRUint32 refCount,
1571 : const char *aObjectDescription)
1572 : {
1573 0 : if (!mDisableLog) {
1574 : fprintf(mStream, "%p [rc=%u] %s\n", (void*)aAddress, refCount,
1575 0 : aObjectDescription);
1576 : }
1577 0 : if (mWantAfterProcessing) {
1578 0 : CCGraphDescriber* d = mDescribers.AppendElement();
1579 0 : NS_ENSURE_TRUE(d, NS_ERROR_OUT_OF_MEMORY);
1580 0 : mCurrentAddress.AssignLiteral("0x");
1581 0 : mCurrentAddress.AppendInt(aAddress, 16);
1582 0 : d->mType = CCGraphDescriber::eRefCountedObject;
1583 0 : d->mAddress = mCurrentAddress;
1584 0 : d->mCnt = refCount;
1585 0 : d->mName.Append(aObjectDescription);
1586 : }
1587 0 : return NS_OK;
1588 : }
1589 0 : NS_IMETHOD NoteGCedObject(PRUint64 aAddress, bool aMarked,
1590 : const char *aObjectDescription)
1591 : {
1592 0 : if (!mDisableLog) {
1593 : fprintf(mStream, "%p [gc%s] %s\n", (void*)aAddress,
1594 0 : aMarked ? ".marked" : "", aObjectDescription);
1595 : }
1596 0 : if (mWantAfterProcessing) {
1597 0 : CCGraphDescriber* d = mDescribers.AppendElement();
1598 0 : NS_ENSURE_TRUE(d, NS_ERROR_OUT_OF_MEMORY);
1599 0 : mCurrentAddress.AssignLiteral("0x");
1600 0 : mCurrentAddress.AppendInt(aAddress, 16);
1601 : d->mType = aMarked ? CCGraphDescriber::eGCMarkedObject :
1602 0 : CCGraphDescriber::eGCedObject;
1603 0 : d->mAddress = mCurrentAddress;
1604 0 : d->mName.Append(aObjectDescription);
1605 : }
1606 0 : return NS_OK;
1607 : }
1608 0 : NS_IMETHOD NoteEdge(PRUint64 aToAddress, const char *aEdgeName)
1609 : {
1610 0 : if (!mDisableLog) {
1611 0 : fprintf(mStream, "> %p %s\n", (void*)aToAddress, aEdgeName);
1612 : }
1613 0 : if (mWantAfterProcessing) {
1614 0 : CCGraphDescriber* d = mDescribers.AppendElement();
1615 0 : NS_ENSURE_TRUE(d, NS_ERROR_OUT_OF_MEMORY);
1616 0 : d->mType = CCGraphDescriber::eEdge;
1617 0 : d->mAddress = mCurrentAddress;
1618 0 : d->mToAddress.AppendInt(aToAddress, 16);
1619 0 : d->mName.Append(aEdgeName);
1620 : }
1621 0 : return NS_OK;
1622 : }
1623 0 : NS_IMETHOD BeginResults()
1624 : {
1625 0 : if (!mDisableLog) {
1626 0 : fputs("==========\n", mStream);
1627 : }
1628 0 : return NS_OK;
1629 : }
1630 0 : NS_IMETHOD DescribeRoot(PRUint64 aAddress, PRUint32 aKnownEdges)
1631 : {
1632 0 : if (!mDisableLog) {
1633 0 : fprintf(mStream, "%p [known=%u]\n", (void*)aAddress, aKnownEdges);
1634 : }
1635 0 : if (mWantAfterProcessing) {
1636 0 : CCGraphDescriber* d = mDescribers.AppendElement();
1637 0 : NS_ENSURE_TRUE(d, NS_ERROR_OUT_OF_MEMORY);
1638 0 : d->mType = CCGraphDescriber::eRoot;
1639 0 : d->mAddress.AppendInt(aAddress, 16);
1640 0 : d->mCnt = aKnownEdges;
1641 : }
1642 0 : return NS_OK;
1643 : }
1644 0 : NS_IMETHOD DescribeGarbage(PRUint64 aAddress)
1645 : {
1646 0 : if (!mDisableLog) {
1647 0 : fprintf(mStream, "%p [garbage]\n", (void*)aAddress);
1648 : }
1649 0 : if (mWantAfterProcessing) {
1650 0 : CCGraphDescriber* d = mDescribers.AppendElement();
1651 0 : NS_ENSURE_TRUE(d, NS_ERROR_OUT_OF_MEMORY);
1652 0 : d->mType = CCGraphDescriber::eGarbage;
1653 0 : d->mAddress.AppendInt(aAddress, 16);
1654 : }
1655 0 : return NS_OK;
1656 : }
1657 0 : NS_IMETHOD End()
1658 : {
1659 0 : if (!mDisableLog) {
1660 0 : fclose(mStream);
1661 0 : mStream = nsnull;
1662 : }
1663 0 : return NS_OK;
1664 : }
1665 :
1666 0 : NS_IMETHOD ProcessNext(nsICycleCollectorHandler* aHandler,
1667 : bool* aCanContinue)
1668 : {
1669 0 : NS_ENSURE_STATE(aHandler && mWantAfterProcessing);
1670 0 : if (mNextIndex < mDescribers.Length()) {
1671 0 : CCGraphDescriber& d = mDescribers[mNextIndex++];
1672 0 : switch (d.mType) {
1673 : case CCGraphDescriber::eRefCountedObject:
1674 : aHandler->NoteRefCountedObject(d.mAddress,
1675 : d.mCnt,
1676 0 : d.mName);
1677 0 : break;
1678 : case CCGraphDescriber::eGCedObject:
1679 : case CCGraphDescriber::eGCMarkedObject:
1680 : aHandler->NoteGCedObject(d.mAddress,
1681 : d.mType ==
1682 : CCGraphDescriber::eGCMarkedObject,
1683 0 : d.mName);
1684 0 : break;
1685 : case CCGraphDescriber::eEdge:
1686 : aHandler->NoteEdge(d.mAddress,
1687 : d.mToAddress,
1688 0 : d.mName);
1689 0 : break;
1690 : case CCGraphDescriber::eRoot:
1691 : aHandler->DescribeRoot(d.mAddress,
1692 0 : d.mCnt);
1693 0 : break;
1694 : case CCGraphDescriber::eGarbage:
1695 0 : aHandler->DescribeGarbage(d.mAddress);
1696 0 : break;
1697 : case CCGraphDescriber::eUnknown:
1698 0 : NS_NOTREACHED("CCGraphDescriber::eUnknown");
1699 0 : break;
1700 : }
1701 : }
1702 0 : if (!(*aCanContinue = mNextIndex < mDescribers.Length())) {
1703 0 : mCurrentAddress.AssignLiteral("0x");
1704 0 : mDescribers.Clear();
1705 0 : mNextIndex = 0;
1706 : }
1707 0 : return NS_OK;
1708 : }
1709 : private:
1710 : FILE *mStream;
1711 : bool mWantAllTraces;
1712 : bool mDisableLog;
1713 : bool mWantAfterProcessing;
1714 : nsCString mCurrentAddress;
1715 : nsTArray<CCGraphDescriber> mDescribers;
1716 : PRUint32 mNextIndex;
1717 : static PRUint32 gLogCounter;
1718 : };
1719 :
1720 0 : NS_IMPL_ISUPPORTS1(nsCycleCollectorLogger, nsICycleCollectorListener)
1721 :
1722 : PRUint32 nsCycleCollectorLogger::gLogCounter = 0;
1723 :
1724 : nsresult
1725 0 : nsCycleCollectorLoggerConstructor(nsISupports* aOuter,
1726 : const nsIID& aIID,
1727 : void* *aInstancePtr)
1728 : {
1729 0 : NS_ENSURE_TRUE(!aOuter, NS_ERROR_NO_AGGREGATION);
1730 :
1731 0 : nsISupports *logger = new nsCycleCollectorLogger();
1732 :
1733 0 : return logger->QueryInterface(aIID, aInstancePtr);
1734 : }
1735 :
1736 : ////////////////////////////////////////////////////////////////////////
1737 : // Bacon & Rajan's |MarkRoots| routine.
1738 : ////////////////////////////////////////////////////////////////////////
1739 :
1740 : struct PtrToNodeEntry : public PLDHashEntryHdr
1741 : {
1742 : // The key is mNode->mPointer
1743 : PtrInfo *mNode;
1744 : };
1745 :
1746 : static bool
1747 1248881 : PtrToNodeMatchEntry(PLDHashTable *table,
1748 : const PLDHashEntryHdr *entry,
1749 : const void *key)
1750 : {
1751 1248881 : const PtrToNodeEntry *n = static_cast<const PtrToNodeEntry*>(entry);
1752 1248881 : return n->mNode->mPointer == key;
1753 : }
1754 :
1755 : static PLDHashTableOps PtrNodeOps = {
1756 : PL_DHashAllocTable,
1757 : PL_DHashFreeTable,
1758 : PL_DHashVoidPtrKeyStub,
1759 : PtrToNodeMatchEntry,
1760 : PL_DHashMoveEntryStub,
1761 : PL_DHashClearEntryStub,
1762 : PL_DHashFinalizeStub,
1763 : nsnull
1764 : };
1765 :
1766 : class GCGraphBuilder : public nsCycleCollectionTraversalCallback
1767 : {
1768 : private:
1769 : NodePool::Builder mNodeBuilder;
1770 : EdgePool::Builder mEdgeBuilder;
1771 : nsTArray<WeakMapping> &mWeakMaps;
1772 : PLDHashTable mPtrToNodeMap;
1773 : PtrInfo *mCurrPi;
1774 : nsCycleCollectionLanguageRuntime **mRuntimes; // weak, from nsCycleCollector
1775 : nsCString mNextEdgeName;
1776 : nsICycleCollectorListener *mListener;
1777 :
1778 : public:
1779 : GCGraphBuilder(GCGraph &aGraph,
1780 : nsCycleCollectionLanguageRuntime **aRuntimes,
1781 : nsICycleCollectorListener *aListener);
1782 : ~GCGraphBuilder();
1783 : bool Initialized();
1784 :
1785 3835 : PRUint32 Count() const { return mPtrToNodeMap.entryCount; }
1786 :
1787 : #ifdef DEBUG_CC
1788 : PtrInfo* AddNode(void *s, nsCycleCollectionParticipant *aParticipant,
1789 : PRUint32 aLangID);
1790 : #else
1791 : PtrInfo* AddNode(void *s, nsCycleCollectionParticipant *aParticipant);
1792 1850766 : PtrInfo* AddNode(void *s, nsCycleCollectionParticipant *aParticipant,
1793 : PRUint32 aLangID)
1794 : {
1795 1850766 : return AddNode(s, aParticipant);
1796 : }
1797 : #endif
1798 : PtrInfo* AddWeakMapNode(void* node);
1799 : void Traverse(PtrInfo* aPtrInfo);
1800 : void SetLastChild();
1801 :
1802 : // nsCycleCollectionTraversalCallback methods.
1803 : NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports *root);
1804 :
1805 : private:
1806 601885 : void DescribeNode(PRUint32 refCount,
1807 : size_t objSz,
1808 : const char *objName)
1809 : {
1810 601885 : mCurrPi->mRefCount = refCount;
1811 : #ifdef DEBUG_CC
1812 : mCurrPi->mBytes = objSz;
1813 : mCurrPi->mName = PL_strdup(objName);
1814 : sCollector->mStats.mVisitedNode++;
1815 : #endif
1816 601885 : }
1817 :
1818 : NS_IMETHOD_(void) DescribeRefCountedNode(nsrefcnt refCount, size_t objSz,
1819 : const char *objName);
1820 : NS_IMETHOD_(void) DescribeGCedNode(bool isMarked, size_t objSz,
1821 : const char *objName);
1822 : NS_IMETHOD_(void) NoteRoot(PRUint32 langID, void *child,
1823 : nsCycleCollectionParticipant* participant);
1824 : NS_IMETHOD_(void) NoteXPCOMChild(nsISupports *child);
1825 : NS_IMETHOD_(void) NoteNativeChild(void *child,
1826 : nsCycleCollectionParticipant *participant);
1827 : NS_IMETHOD_(void) NoteScriptChild(PRUint32 langID, void *child);
1828 : NS_IMETHOD_(void) NoteNextEdgeName(const char* name);
1829 : NS_IMETHOD_(void) NoteWeakMapping(void *map, void *key, void *val);
1830 : private:
1831 1725048 : NS_IMETHOD_(void) NoteChild(void *child, nsCycleCollectionParticipant *cp,
1832 : PRUint32 langID, nsCString edgeName)
1833 : {
1834 1725048 : PtrInfo *childPi = AddNode(child, cp, langID);
1835 1725048 : if (!childPi)
1836 0 : return;
1837 1725048 : mEdgeBuilder.Add(childPi);
1838 : #ifdef DEBUG_CC
1839 : mCurrPi->mEdgeNames.AppendElement(edgeName);
1840 : #endif
1841 1725048 : if (mListener) {
1842 0 : mListener->NoteEdge((PRUint64)child, edgeName.get());
1843 : }
1844 1725048 : ++childPi->mInternalRefs;
1845 : }
1846 : };
1847 :
1848 1925 : GCGraphBuilder::GCGraphBuilder(GCGraph &aGraph,
1849 : nsCycleCollectionLanguageRuntime **aRuntimes,
1850 : nsICycleCollectorListener *aListener)
1851 : : mNodeBuilder(aGraph.mNodes),
1852 : mEdgeBuilder(aGraph.mEdges),
1853 : mWeakMaps(aGraph.mWeakMaps),
1854 : mRuntimes(aRuntimes),
1855 1925 : mListener(aListener)
1856 : {
1857 1925 : if (!PL_DHashTableInit(&mPtrToNodeMap, &PtrNodeOps, nsnull,
1858 1925 : sizeof(PtrToNodeEntry), 32768))
1859 0 : mPtrToNodeMap.ops = nsnull;
1860 :
1861 1925 : PRUint32 flags = 0;
1862 : #ifdef DEBUG_CC
1863 : flags = nsCycleCollectionTraversalCallback::WANT_DEBUG_INFO |
1864 : nsCycleCollectionTraversalCallback::WANT_ALL_TRACES;
1865 : #endif
1866 1925 : if (!flags && mListener) {
1867 0 : flags = nsCycleCollectionTraversalCallback::WANT_DEBUG_INFO;
1868 0 : bool all = false;
1869 0 : mListener->GetWantAllTraces(&all);
1870 0 : if (all) {
1871 0 : flags |= nsCycleCollectionTraversalCallback::WANT_ALL_TRACES;
1872 : }
1873 : }
1874 :
1875 1925 : mFlags |= flags;
1876 1925 : }
1877 :
1878 3850 : GCGraphBuilder::~GCGraphBuilder()
1879 : {
1880 1925 : if (mPtrToNodeMap.ops)
1881 1925 : PL_DHashTableFinish(&mPtrToNodeMap);
1882 1925 : }
1883 :
1884 : bool
1885 1925 : GCGraphBuilder::Initialized()
1886 : {
1887 1925 : return !!mPtrToNodeMap.ops;
1888 : }
1889 :
1890 : PtrInfo*
1891 1850766 : GCGraphBuilder::AddNode(void *s, nsCycleCollectionParticipant *aParticipant
1892 : IF_DEBUG_CC_PARAM(PRUint32 aLangID)
1893 : )
1894 : {
1895 1850766 : PtrToNodeEntry *e = static_cast<PtrToNodeEntry*>(PL_DHashTableOperate(&mPtrToNodeMap, s, PL_DHASH_ADD));
1896 1850766 : if (!e)
1897 0 : return nsnull;
1898 :
1899 : PtrInfo *result;
1900 1850766 : if (!e->mNode) {
1901 : // New entry.
1902 : result = mNodeBuilder.Add(s, aParticipant
1903 : IF_DEBUG_CC_PARAM(aLangID)
1904 601885 : );
1905 601885 : if (!result) {
1906 0 : PL_DHashTableRawRemove(&mPtrToNodeMap, e);
1907 0 : return nsnull;
1908 : }
1909 601885 : e->mNode = result;
1910 : } else {
1911 1248881 : result = e->mNode;
1912 1248881 : NS_ASSERTION(result->mParticipant == aParticipant,
1913 : "nsCycleCollectionParticipant shouldn't change!");
1914 : }
1915 1850766 : return result;
1916 : }
1917 :
1918 : MOZ_NEVER_INLINE void
1919 601885 : GCGraphBuilder::Traverse(PtrInfo* aPtrInfo)
1920 : {
1921 601885 : mCurrPi = aPtrInfo;
1922 :
1923 : #ifdef DEBUG_CC
1924 : if (!mCurrPi->mParticipant) {
1925 : Fault("unknown pointer during walk", aPtrInfo);
1926 : return;
1927 : }
1928 : #endif
1929 :
1930 601885 : mCurrPi->SetFirstChild(mEdgeBuilder.Mark());
1931 :
1932 601885 : nsresult rv = aPtrInfo->mParticipant->Traverse(aPtrInfo->mPointer, *this);
1933 601885 : if (NS_FAILED(rv)) {
1934 0 : Fault("script pointer traversal failed", aPtrInfo);
1935 : }
1936 601885 : }
1937 :
1938 : void
1939 1927 : GCGraphBuilder::SetLastChild()
1940 : {
1941 1927 : mCurrPi->SetLastChild(mEdgeBuilder.Mark());
1942 1927 : }
1943 :
1944 : NS_IMETHODIMP_(void)
1945 7322 : GCGraphBuilder::NoteXPCOMRoot(nsISupports *root)
1946 : {
1947 7322 : root = canonicalize(root);
1948 7322 : NS_ASSERTION(root,
1949 : "Don't add objects that don't participate in collection!");
1950 :
1951 : #ifdef DEBUG_CC
1952 : if (nsCycleCollector_shouldSuppress(root))
1953 : return;
1954 : #endif
1955 :
1956 : nsXPCOMCycleCollectionParticipant *cp;
1957 7322 : ToParticipant(root, &cp);
1958 :
1959 7322 : NoteRoot(nsIProgrammingLanguage::CPLUSPLUS, root, cp);
1960 7322 : }
1961 :
1962 :
1963 : NS_IMETHODIMP_(void)
1964 13298 : GCGraphBuilder::NoteRoot(PRUint32 langID, void *root,
1965 : nsCycleCollectionParticipant* participant)
1966 : {
1967 13298 : NS_ASSERTION(root, "Don't add a null root!");
1968 :
1969 13298 : if (langID > nsIProgrammingLanguage::MAX || !mRuntimes[langID]) {
1970 0 : Fault("adding root for unregistered language", root);
1971 0 : return;
1972 : }
1973 :
1974 13298 : if (!participant->CanSkipThis(root) || WantAllTraces()) {
1975 13298 : AddNode(root, participant, langID);
1976 : }
1977 : }
1978 :
1979 : NS_IMETHODIMP_(void)
1980 149511 : GCGraphBuilder::DescribeRefCountedNode(nsrefcnt refCount, size_t objSz,
1981 : const char *objName)
1982 : {
1983 149511 : if (refCount == 0)
1984 0 : Fault("zero refcount", mCurrPi);
1985 149511 : if (refCount == PR_UINT32_MAX)
1986 0 : Fault("overflowing refcount", mCurrPi);
1987 149511 : sCollector->mVisitedRefCounted++;
1988 :
1989 149511 : if (mListener) {
1990 : mListener->NoteRefCountedObject((PRUint64)mCurrPi->mPointer, refCount,
1991 0 : objName);
1992 : }
1993 :
1994 149511 : DescribeNode(refCount, objSz, objName);
1995 149511 : }
1996 :
1997 : NS_IMETHODIMP_(void)
1998 452374 : GCGraphBuilder::DescribeGCedNode(bool isMarked, size_t objSz,
1999 : const char *objName)
2000 : {
2001 452374 : PRUint32 refCount = isMarked ? PR_UINT32_MAX : 0;
2002 452374 : sCollector->mVisitedGCed++;
2003 :
2004 452374 : if (mListener) {
2005 : mListener->NoteGCedObject((PRUint64)mCurrPi->mPointer, isMarked,
2006 0 : objName);
2007 : }
2008 :
2009 452374 : DescribeNode(refCount, objSz, objName);
2010 452374 : }
2011 :
2012 : NS_IMETHODIMP_(void)
2013 423319 : GCGraphBuilder::NoteXPCOMChild(nsISupports *child)
2014 : {
2015 846638 : nsCString edgeName;
2016 423319 : if (WantDebugInfo()) {
2017 0 : edgeName.Assign(mNextEdgeName);
2018 0 : mNextEdgeName.Truncate();
2019 : }
2020 423319 : if (!child || !(child = canonicalize(child)))
2021 : return;
2022 :
2023 : #ifdef DEBUG_CC
2024 : if (nsCycleCollector_shouldSuppress(child))
2025 : return;
2026 : #endif
2027 :
2028 : nsXPCOMCycleCollectionParticipant *cp;
2029 360393 : ToParticipant(child, &cp);
2030 360393 : if (cp && (!cp->CanSkipThis(child) || WantAllTraces())) {
2031 360377 : NoteChild(child, cp, nsIProgrammingLanguage::CPLUSPLUS, edgeName);
2032 : }
2033 : }
2034 :
2035 : NS_IMETHODIMP_(void)
2036 14084 : GCGraphBuilder::NoteNativeChild(void *child,
2037 : nsCycleCollectionParticipant *participant)
2038 : {
2039 28168 : nsCString edgeName;
2040 14084 : if (WantDebugInfo()) {
2041 0 : edgeName.Assign(mNextEdgeName);
2042 0 : mNextEdgeName.Truncate();
2043 : }
2044 14084 : if (!child)
2045 : return;
2046 :
2047 12488 : NS_ASSERTION(participant, "Need a nsCycleCollectionParticipant!");
2048 12488 : NoteChild(child, participant, nsIProgrammingLanguage::CPLUSPLUS, edgeName);
2049 : }
2050 :
2051 : NS_IMETHODIMP_(void)
2052 1357231 : GCGraphBuilder::NoteScriptChild(PRUint32 langID, void *child)
2053 : {
2054 2714462 : nsCString edgeName;
2055 1357231 : if (WantDebugInfo()) {
2056 0 : edgeName.Assign(mNextEdgeName);
2057 0 : mNextEdgeName.Truncate();
2058 : }
2059 1357231 : if (!child)
2060 : return;
2061 :
2062 1357230 : if (langID > nsIProgrammingLanguage::MAX) {
2063 0 : Fault("traversing pointer for unknown language", child);
2064 : return;
2065 : }
2066 :
2067 1357230 : if (!mRuntimes[langID]) {
2068 : NS_WARNING("Not collecting cycles involving objects for scripting "
2069 0 : "languages that don't participate in cycle collection.");
2070 : return;
2071 : }
2072 :
2073 : // skip over non-grey JS children
2074 2719507 : if (langID == nsIProgrammingLanguage::JAVASCRIPT &&
2075 1362277 : !xpc_GCThingIsGrayCCThing(child) && !WantAllTraces()) {
2076 : return;
2077 : }
2078 :
2079 1352183 : nsCycleCollectionParticipant *cp = mRuntimes[langID]->ToParticipant(child);
2080 1352183 : if (cp)
2081 1352183 : NoteChild(child, cp, langID, edgeName);
2082 : }
2083 :
2084 : NS_IMETHODIMP_(void)
2085 0 : GCGraphBuilder::NoteNextEdgeName(const char* name)
2086 : {
2087 0 : if (WantDebugInfo()) {
2088 0 : mNextEdgeName = name;
2089 : }
2090 0 : }
2091 :
2092 : PtrInfo*
2093 0 : GCGraphBuilder::AddWeakMapNode(void *node)
2094 : {
2095 : nsCycleCollectionParticipant *cp;
2096 0 : NS_ASSERTION(node, "Weak map node should be non-null.");
2097 :
2098 0 : if (!xpc_GCThingIsGrayCCThing(node) && !WantAllTraces())
2099 0 : return nsnull;
2100 :
2101 0 : cp = mRuntimes[nsIProgrammingLanguage::JAVASCRIPT]->ToParticipant(node);
2102 0 : NS_ASSERTION(cp, "Javascript runtime participant should be non-null.");
2103 0 : return AddNode(node, cp, nsIProgrammingLanguage::JAVASCRIPT);
2104 : }
2105 :
2106 : NS_IMETHODIMP_(void)
2107 0 : GCGraphBuilder::NoteWeakMapping(void *map, void *key, void *val)
2108 : {
2109 0 : PtrInfo *valNode = AddWeakMapNode(val);
2110 :
2111 0 : if (!valNode)
2112 0 : return;
2113 :
2114 0 : WeakMapping *mapping = mWeakMaps.AppendElement();
2115 0 : mapping->mMap = map ? AddWeakMapNode(map) : nsnull;
2116 0 : mapping->mKey = key ? AddWeakMapNode(key) : nsnull;
2117 0 : mapping->mVal = valNode;
2118 : }
2119 :
2120 : // MayHaveChild() will be false after a Traverse if the object does
2121 : // not have any children the CC will visit.
2122 : class ChildFinder : public nsCycleCollectionTraversalCallback
2123 : {
2124 : public:
2125 25631 : ChildFinder() : mMayHaveChild(false) {}
2126 :
2127 : // The logic of the Note*Child functions must mirror that of their
2128 : // respective functions in GCGraphBuilder.
2129 : NS_IMETHOD_(void) NoteXPCOMChild(nsISupports *child);
2130 : NS_IMETHOD_(void) NoteNativeChild(void *child,
2131 : nsCycleCollectionParticipant *helper);
2132 : NS_IMETHOD_(void) NoteScriptChild(PRUint32 langID, void *child);
2133 :
2134 25631 : NS_IMETHOD_(void) DescribeRefCountedNode(nsrefcnt refcount,
2135 : size_t objsz,
2136 25631 : const char *objname) {};
2137 0 : NS_IMETHOD_(void) DescribeGCedNode(bool ismarked,
2138 : size_t objsz,
2139 0 : const char *objname) {};
2140 0 : NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports *root) {};
2141 0 : NS_IMETHOD_(void) NoteRoot(PRUint32 langID, void *root,
2142 0 : nsCycleCollectionParticipant* helper) {};
2143 0 : NS_IMETHOD_(void) NoteNextEdgeName(const char* name) {};
2144 0 : NS_IMETHOD_(void) NoteWeakMapping(void *map, void *key, void *val) {};
2145 25631 : bool MayHaveChild() {
2146 25631 : return mMayHaveChild;
2147 : };
2148 : private:
2149 : bool mMayHaveChild;
2150 : };
2151 :
2152 : NS_IMETHODIMP_(void)
2153 75351 : ChildFinder::NoteXPCOMChild(nsISupports *child)
2154 : {
2155 75351 : if (!child || !(child = canonicalize(child)))
2156 5241 : return;
2157 : nsXPCOMCycleCollectionParticipant *cp;
2158 70110 : ToParticipant(child, &cp);
2159 70110 : if (cp && !cp->CanSkip(child, true))
2160 69934 : mMayHaveChild = true;
2161 : };
2162 :
2163 : NS_IMETHODIMP_(void)
2164 2274 : ChildFinder::NoteNativeChild(void *child,
2165 : nsCycleCollectionParticipant *helper)
2166 : {
2167 2274 : if (child)
2168 2078 : mMayHaveChild = true;
2169 2274 : };
2170 :
2171 : NS_IMETHODIMP_(void)
2172 1 : ChildFinder::NoteScriptChild(PRUint32 langID, void *child)
2173 : {
2174 1 : if (!child)
2175 1 : return;
2176 0 : if (langID == nsIProgrammingLanguage::JAVASCRIPT &&
2177 0 : !xpc_GCThingIsGrayCCThing(child)) {
2178 0 : return;
2179 : }
2180 0 : mMayHaveChild = true;
2181 : };
2182 :
2183 : static bool
2184 115052 : AddPurpleRoot(GCGraphBuilder &builder, nsISupports *root)
2185 : {
2186 115052 : root = canonicalize(root);
2187 115052 : NS_ASSERTION(root,
2188 : "Don't add objects that don't participate in collection!");
2189 :
2190 : nsXPCOMCycleCollectionParticipant *cp;
2191 115052 : ToParticipant(root, &cp);
2192 :
2193 115052 : if (builder.WantAllTraces() || !cp->CanSkipInCC(root)) {
2194 : PtrInfo *pinfo = builder.AddNode(root, cp,
2195 112420 : nsIProgrammingLanguage::CPLUSPLUS);
2196 112420 : if (!pinfo) {
2197 0 : return false;
2198 : }
2199 : }
2200 :
2201 115052 : cp->UnmarkIfPurple(root);
2202 :
2203 115052 : return true;
2204 : }
2205 :
2206 : static bool
2207 25631 : MayHaveChild(nsISupports *o, nsXPCOMCycleCollectionParticipant* cp)
2208 : {
2209 25631 : ChildFinder cf;
2210 25631 : cp->Traverse(o, cf);
2211 25631 : return cf.MayHaveChild();
2212 : }
2213 :
2214 : void
2215 276 : nsPurpleBuffer::RemoveSkippable(bool removeChildlessNodes)
2216 : {
2217 : // Walk through all the blocks.
2218 1882 : for (Block *b = &mFirstBlock; b; b = b->mNext) {
2219 412742 : for (nsPurpleBufferEntry *e = b->mEntries,
2220 1606 : *eEnd = ArrayEnd(b->mEntries);
2221 : e != eEnd; ++e) {
2222 409530 : if (!(PRUword(e->mObject) & PRUword(1))) {
2223 : // This is a real entry (rather than something on the
2224 : // free list).
2225 318755 : if (e->mObject) {
2226 318468 : nsISupports* o = canonicalize(e->mObject);
2227 : nsXPCOMCycleCollectionParticipant* cp;
2228 318468 : ToParticipant(o, &cp);
2229 638932 : if (!cp->CanSkip(o, false) &&
2230 320464 : (!removeChildlessNodes || MayHaveChild(o, cp))) {
2231 294779 : continue;
2232 : }
2233 23689 : cp->UnmarkIfPurple(o);
2234 : }
2235 23976 : Remove(e);
2236 : }
2237 : }
2238 : }
2239 276 : }
2240 :
2241 : #ifdef DEBUG_CC
2242 : static PLDHashOperator
2243 : noteAllCallback(nsVoidPtrHashKey* key, void* userArg)
2244 : {
2245 : GCGraphBuilder *builder = static_cast<GCGraphBuilder*>(userArg);
2246 : builder->NoteXPCOMRoot(
2247 : static_cast<nsISupports *>(const_cast<void*>(key->GetKey())));
2248 : return PL_DHASH_NEXT;
2249 : }
2250 :
2251 : void
2252 : nsPurpleBuffer::NoteAll(GCGraphBuilder &builder)
2253 : {
2254 : mCompatObjects.EnumerateEntries(noteAllCallback, &builder);
2255 :
2256 : for (Block *b = &mFirstBlock; b; b = b->mNext) {
2257 : for (nsPurpleBufferEntry *e = b->mEntries,
2258 : *eEnd = ArrayEnd(b->mEntries);
2259 : e != eEnd; ++e) {
2260 : if (!(PRUword(e->mObject) & PRUword(1)) && e->mObject) {
2261 : builder.NoteXPCOMRoot(e->mObject);
2262 : }
2263 : }
2264 : }
2265 : }
2266 : #endif
2267 :
2268 : void
2269 1925 : nsCycleCollector::SelectPurple(GCGraphBuilder &builder)
2270 : {
2271 1925 : mPurpleBuf.SelectPointers(builder);
2272 1925 : }
2273 :
2274 : void
2275 276 : nsCycleCollector::ForgetSkippable(bool removeChildlessNodes)
2276 : {
2277 552 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
2278 276 : if (obs) {
2279 276 : obs->NotifyObservers(nsnull, "cycle-collector-forget-skippable", nsnull);
2280 : }
2281 276 : mPurpleBuf.RemoveSkippable(removeChildlessNodes);
2282 276 : if (mForgetSkippableCB) {
2283 276 : mForgetSkippableCB();
2284 : }
2285 276 : }
2286 :
2287 : MOZ_NEVER_INLINE void
2288 1910 : nsCycleCollector::MarkRoots(GCGraphBuilder &builder)
2289 : {
2290 1910 : mGraph.mRootCount = builder.Count();
2291 :
2292 : // read the PtrInfo out of the graph that we are building
2293 1910 : NodePool::Enumerator queue(mGraph.mNodes);
2294 605705 : while (!queue.IsDone()) {
2295 601885 : PtrInfo *pi = queue.GetNext();
2296 601885 : CC_AbortIfNull(pi);
2297 601885 : builder.Traverse(pi);
2298 601885 : if (queue.AtBlockEnd())
2299 17 : builder.SetLastChild();
2300 : }
2301 1910 : if (mGraph.mRootCount > 0)
2302 1910 : builder.SetLastChild();
2303 1910 : }
2304 :
2305 :
2306 : ////////////////////////////////////////////////////////////////////////
2307 : // Bacon & Rajan's |ScanRoots| routine.
2308 : ////////////////////////////////////////////////////////////////////////
2309 :
2310 :
2311 : struct ScanBlackVisitor
2312 : {
2313 12783 : ScanBlackVisitor(PRUint32 &aWhiteNodeCount)
2314 12783 : : mWhiteNodeCount(aWhiteNodeCount)
2315 : {
2316 12783 : }
2317 :
2318 1285245 : bool ShouldVisitNode(PtrInfo const *pi)
2319 : {
2320 1285245 : return pi->mColor != black;
2321 : }
2322 :
2323 440370 : MOZ_NEVER_INLINE void VisitNode(PtrInfo *pi)
2324 : {
2325 440370 : if (pi->mColor == white)
2326 493 : --mWhiteNodeCount;
2327 440370 : pi->mColor = black;
2328 : #ifdef DEBUG_CC
2329 : sCollector->mStats.mSetColorBlack++;
2330 : #endif
2331 440370 : }
2332 :
2333 : PRUint32 &mWhiteNodeCount;
2334 : };
2335 :
2336 :
2337 : struct scanVisitor
2338 : {
2339 1910 : scanVisitor(PRUint32 &aWhiteNodeCount) : mWhiteNodeCount(aWhiteNodeCount)
2340 : {
2341 1910 : }
2342 :
2343 591862 : bool ShouldVisitNode(PtrInfo const *pi)
2344 : {
2345 591862 : return pi->mColor == grey;
2346 : }
2347 :
2348 174791 : MOZ_NEVER_INLINE void VisitNode(PtrInfo *pi)
2349 : {
2350 174791 : if (pi->mInternalRefs > pi->mRefCount && pi->mRefCount > 0)
2351 0 : Fault("traversed refs exceed refcount", pi);
2352 :
2353 174791 : if (pi->mInternalRefs == pi->mRefCount || pi->mRefCount == 0) {
2354 162008 : pi->mColor = white;
2355 162008 : ++mWhiteNodeCount;
2356 : #ifdef DEBUG_CC
2357 : sCollector->mStats.mSetColorWhite++;
2358 : #endif
2359 : } else {
2360 12783 : GraphWalker<ScanBlackVisitor>(ScanBlackVisitor(mWhiteNodeCount)).Walk(pi);
2361 12783 : NS_ASSERTION(pi->mColor == black,
2362 : "Why didn't ScanBlackVisitor make pi black?");
2363 : }
2364 174791 : }
2365 :
2366 : PRUint32 &mWhiteNodeCount;
2367 : };
2368 :
2369 : // Iterate over the WeakMaps. If we mark anything while iterating
2370 : // over the WeakMaps, we must iterate over all of the WeakMaps again.
2371 : void
2372 1910 : nsCycleCollector::ScanWeakMaps()
2373 : {
2374 : bool anyChanged;
2375 1910 : do {
2376 1910 : anyChanged = false;
2377 1910 : for (PRUint32 i = 0; i < mGraph.mWeakMaps.Length(); i++) {
2378 0 : WeakMapping *wm = &mGraph.mWeakMaps[i];
2379 :
2380 : // If mMap or mKey are null, the original object was marked black.
2381 0 : uint32 mColor = wm->mMap ? wm->mMap->mColor : black;
2382 0 : uint32 kColor = wm->mKey ? wm->mKey->mColor : black;
2383 0 : PtrInfo *v = wm->mVal;
2384 :
2385 : // All non-null weak mapping maps, keys and values are
2386 : // roots (in the sense of WalkFromRoots) in the cycle
2387 : // collector graph, and thus should have been colored
2388 : // either black or white in ScanRoots().
2389 0 : NS_ASSERTION(mColor != grey, "Uncolored weak map");
2390 0 : NS_ASSERTION(kColor != grey, "Uncolored weak map key");
2391 0 : NS_ASSERTION(v->mColor != grey, "Uncolored weak map value");
2392 :
2393 0 : if (mColor == black && kColor == black && v->mColor != black) {
2394 0 : GraphWalker<ScanBlackVisitor>(ScanBlackVisitor(mWhiteNodeCount)).Walk(v);
2395 0 : anyChanged = true;
2396 : }
2397 : }
2398 : } while (anyChanged);
2399 1910 : }
2400 :
2401 : void
2402 1910 : nsCycleCollector::ScanRoots()
2403 : {
2404 1910 : mWhiteNodeCount = 0;
2405 :
2406 : // On the assumption that most nodes will be black, it's
2407 : // probably faster to use a GraphWalker than a
2408 : // NodePool::Enumerator.
2409 1910 : GraphWalker<scanVisitor>(scanVisitor(mWhiteNodeCount)).WalkFromRoots(mGraph);
2410 :
2411 1910 : ScanWeakMaps();
2412 :
2413 : #ifdef DEBUG_CC
2414 : // Sanity check: scan should have colored all grey nodes black or
2415 : // white. So we ensure we have no grey nodes at this point.
2416 : NodePool::Enumerator etor(mGraph.mNodes);
2417 : while (!etor.IsDone())
2418 : {
2419 : PtrInfo *pinfo = etor.GetNext();
2420 : if (pinfo->mColor == grey) {
2421 : Fault("valid grey node after scanning", pinfo);
2422 : }
2423 : }
2424 : #endif
2425 1910 : }
2426 :
2427 :
2428 : ////////////////////////////////////////////////////////////////////////
2429 : // Bacon & Rajan's |CollectWhite| routine, somewhat modified.
2430 : ////////////////////////////////////////////////////////////////////////
2431 :
2432 : bool
2433 1925 : nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
2434 : {
2435 : // Explanation of "somewhat modified": we have no way to collect the
2436 : // set of whites "all at once", we have to ask each of them to drop
2437 : // their outgoing links and assume this will cause the garbage cycle
2438 : // to *mostly* self-destruct (except for the reference we continue
2439 : // to hold).
2440 : //
2441 : // To do this "safely" we must make sure that the white nodes we're
2442 : // operating on are stable for the duration of our operation. So we
2443 : // make 3 sets of calls to language runtimes:
2444 : //
2445 : // - Root(whites), which should pin the whites in memory.
2446 : // - Unlink(whites), which drops outgoing links on each white.
2447 : // - Unroot(whites), which returns the whites to normal GC.
2448 :
2449 : nsresult rv;
2450 1925 : TimeLog timeLog;
2451 :
2452 1925 : NS_ASSERTION(mWhiteNodes->IsEmpty(),
2453 : "FinishCollection wasn't called?");
2454 :
2455 1925 : mWhiteNodes->SetCapacity(mWhiteNodeCount);
2456 1925 : PRUint32 numWhiteGCed = 0;
2457 :
2458 1925 : NodePool::Enumerator etor(mGraph.mNodes);
2459 605735 : while (!etor.IsDone())
2460 : {
2461 601885 : PtrInfo *pinfo = etor.GetNext();
2462 601885 : if (pinfo->mColor == white && mWhiteNodes->AppendElement(pinfo)) {
2463 161515 : rv = pinfo->mParticipant->Root(pinfo->mPointer);
2464 161515 : if (NS_FAILED(rv)) {
2465 0 : Fault("Failed root call while unlinking", pinfo);
2466 0 : mWhiteNodes->RemoveElementAt(mWhiteNodes->Length() - 1);
2467 161515 : } else if (pinfo->mRefCount == 0) {
2468 : // only JS objects have a refcount of 0
2469 25671 : ++numWhiteGCed;
2470 : }
2471 : }
2472 : }
2473 :
2474 1925 : PRUint32 count = mWhiteNodes->Length();
2475 1925 : NS_ASSERTION(numWhiteGCed <= count,
2476 : "More freed GCed nodes than total freed nodes.");
2477 1925 : if (mResults) {
2478 48 : mResults->mFreedRefCounted += count - numWhiteGCed;
2479 48 : mResults->mFreedGCed += numWhiteGCed;
2480 : }
2481 :
2482 1925 : timeLog.Checkpoint("CollectWhite::Root");
2483 :
2484 1925 : if (mBeforeUnlinkCB) {
2485 1910 : mBeforeUnlinkCB();
2486 1910 : timeLog.Checkpoint("CollectWhite::BeforeUnlinkCB");
2487 : }
2488 : #if defined(DEBUG_CC) && !defined(__MINGW32__) && defined(WIN32)
2489 : struct _CrtMemState ms1, ms2;
2490 : _CrtMemCheckpoint(&ms1);
2491 : #endif
2492 :
2493 1925 : if (aListener) {
2494 0 : for (PRUint32 i = 0; i < count; ++i) {
2495 0 : PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
2496 0 : aListener->DescribeGarbage((PRUint64)pinfo->mPointer);
2497 : }
2498 0 : aListener->End();
2499 : }
2500 :
2501 163440 : for (PRUint32 i = 0; i < count; ++i) {
2502 161515 : PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
2503 161515 : rv = pinfo->mParticipant->Unlink(pinfo->mPointer);
2504 161515 : if (NS_FAILED(rv)) {
2505 0 : Fault("Failed unlink call while unlinking", pinfo);
2506 : #ifdef DEBUG_CC
2507 : mStats.mFailedUnlink++;
2508 : #endif
2509 : }
2510 : else {
2511 : #ifdef DEBUG_CC
2512 : ++mStats.mCollectedNode;
2513 : #endif
2514 : }
2515 : }
2516 1925 : timeLog.Checkpoint("CollectWhite::Unlink");
2517 :
2518 163440 : for (PRUint32 i = 0; i < count; ++i) {
2519 161515 : PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
2520 161515 : rv = pinfo->mParticipant->Unroot(pinfo->mPointer);
2521 161515 : if (NS_FAILED(rv))
2522 0 : Fault("Failed unroot call while unlinking", pinfo);
2523 : }
2524 1925 : timeLog.Checkpoint("CollectWhite::Unroot");
2525 :
2526 : #if defined(DEBUG_CC) && !defined(__MINGW32__) && defined(WIN32)
2527 : _CrtMemCheckpoint(&ms2);
2528 : if (ms2.lTotalCount < ms1.lTotalCount)
2529 : mStats.mFreedBytes += (ms1.lTotalCount - ms2.lTotalCount);
2530 : #endif
2531 :
2532 1925 : return count > 0;
2533 : }
2534 :
2535 :
2536 : #ifdef DEBUG_CC
2537 : ////////////////////////////////////////////////////////////////////////
2538 : // Memory-hooking stuff
2539 : // When debugging wild pointers, it sometimes helps to hook malloc and
2540 : // free. This stuff is disabled unless you set an environment variable.
2541 : ////////////////////////////////////////////////////////////////////////
2542 :
2543 : static bool hookedMalloc = false;
2544 :
2545 : #if defined(__GLIBC__) && !defined(__UCLIBC__)
2546 : #include <malloc.h>
2547 :
2548 : static void* (*old_memalign_hook)(size_t, size_t, const void *);
2549 : static void* (*old_realloc_hook)(void *, size_t, const void *);
2550 : static void* (*old_malloc_hook)(size_t, const void *);
2551 : static void (*old_free_hook)(void *, const void *);
2552 :
2553 : static void* my_memalign_hook(size_t, size_t, const void *);
2554 : static void* my_realloc_hook(void *, size_t, const void *);
2555 : static void* my_malloc_hook(size_t, const void *);
2556 : static void my_free_hook(void *, const void *);
2557 :
2558 : static inline void
2559 : install_old_hooks()
2560 : {
2561 : __memalign_hook = old_memalign_hook;
2562 : __realloc_hook = old_realloc_hook;
2563 : __malloc_hook = old_malloc_hook;
2564 : __free_hook = old_free_hook;
2565 : }
2566 :
2567 : static inline void
2568 : save_old_hooks()
2569 : {
2570 : // Glibc docs recommend re-saving old hooks on
2571 : // return from recursive calls. Strangely when
2572 : // we do this, we find ourselves in infinite
2573 : // recursion.
2574 :
2575 : // old_memalign_hook = __memalign_hook;
2576 : // old_realloc_hook = __realloc_hook;
2577 : // old_malloc_hook = __malloc_hook;
2578 : // old_free_hook = __free_hook;
2579 : }
2580 :
2581 : static inline void
2582 : install_new_hooks()
2583 : {
2584 : __memalign_hook = my_memalign_hook;
2585 : __realloc_hook = my_realloc_hook;
2586 : __malloc_hook = my_malloc_hook;
2587 : __free_hook = my_free_hook;
2588 : }
2589 :
2590 : static void*
2591 : my_realloc_hook(void *ptr, size_t size, const void *caller)
2592 : {
2593 : void *result;
2594 :
2595 : install_old_hooks();
2596 : result = realloc(ptr, size);
2597 : save_old_hooks();
2598 :
2599 : if (sCollector) {
2600 : sCollector->Freed(ptr);
2601 : sCollector->Allocated(result, size);
2602 : }
2603 :
2604 : install_new_hooks();
2605 :
2606 : return result;
2607 : }
2608 :
2609 :
2610 : static void*
2611 : my_memalign_hook(size_t size, size_t alignment, const void *caller)
2612 : {
2613 : void *result;
2614 :
2615 : install_old_hooks();
2616 : result = memalign(size, alignment);
2617 : save_old_hooks();
2618 :
2619 : if (sCollector)
2620 : sCollector->Allocated(result, size);
2621 :
2622 : install_new_hooks();
2623 :
2624 : return result;
2625 : }
2626 :
2627 :
2628 : static void
2629 : my_free_hook (void *ptr, const void *caller)
2630 : {
2631 : install_old_hooks();
2632 : free(ptr);
2633 : save_old_hooks();
2634 :
2635 : if (sCollector)
2636 : sCollector->Freed(ptr);
2637 :
2638 : install_new_hooks();
2639 : }
2640 :
2641 :
2642 : static void*
2643 : my_malloc_hook (size_t size, const void *caller)
2644 : {
2645 : void *result;
2646 :
2647 : install_old_hooks();
2648 : result = malloc (size);
2649 : save_old_hooks();
2650 :
2651 : if (sCollector)
2652 : sCollector->Allocated(result, size);
2653 :
2654 : install_new_hooks();
2655 :
2656 : return result;
2657 : }
2658 :
2659 :
2660 : static void
2661 : InitMemHook(void)
2662 : {
2663 : if (!hookedMalloc) {
2664 : save_old_hooks();
2665 : install_new_hooks();
2666 : hookedMalloc = true;
2667 : }
2668 : }
2669 :
2670 : #elif defined(WIN32)
2671 : #ifndef __MINGW32__
2672 :
2673 : static int
2674 : AllocHook(int allocType, void *userData, size_t size, int
2675 : blockType, long requestNumber, const unsigned char *filename, int
2676 : lineNumber)
2677 : {
2678 : if (allocType == _HOOK_FREE)
2679 : sCollector->Freed(userData);
2680 : return 1;
2681 : }
2682 :
2683 :
2684 : static void InitMemHook(void)
2685 : {
2686 : if (!hookedMalloc) {
2687 : _CrtSetAllocHook (AllocHook);
2688 : hookedMalloc = true;
2689 : }
2690 : }
2691 : #endif // __MINGW32__
2692 :
2693 : #elif 0 // defined(XP_MACOSX)
2694 :
2695 : #include <malloc/malloc.h>
2696 :
2697 : static void (*old_free)(struct _malloc_zone_t *zone, void *ptr);
2698 :
2699 : static void
2700 : freehook(struct _malloc_zone_t *zone, void *ptr)
2701 : {
2702 : if (sCollector)
2703 : sCollector->Freed(ptr);
2704 : old_free(zone, ptr);
2705 : }
2706 :
2707 :
2708 : static void
2709 : InitMemHook(void)
2710 : {
2711 : if (!hookedMalloc) {
2712 : malloc_zone_t *default_zone = malloc_default_zone();
2713 : old_free = default_zone->free;
2714 : default_zone->free = freehook;
2715 : hookedMalloc = true;
2716 : }
2717 : }
2718 :
2719 :
2720 : #else
2721 :
2722 : static void
2723 : InitMemHook(void)
2724 : {
2725 : }
2726 :
2727 : #endif // GLIBC / WIN32 / OSX
2728 : #endif // DEBUG_CC
2729 :
2730 : ////////////////////////////////////////////////////////////////////////
2731 : // Collector implementation
2732 : ////////////////////////////////////////////////////////////////////////
2733 :
2734 1419 : nsCycleCollector::nsCycleCollector() :
2735 : mCollectionInProgress(false),
2736 : mScanInProgress(false),
2737 : mResults(nsnull),
2738 : mWhiteNodes(nsnull),
2739 : mWhiteNodeCount(0),
2740 : mVisitedRefCounted(0),
2741 : mVisitedGCed(0),
2742 : #ifdef DEBUG_CC
2743 : mPurpleBuf(mParams, mStats),
2744 : mPtrLog(nsnull),
2745 : #else
2746 : mPurpleBuf(mParams),
2747 : #endif
2748 : mBeforeUnlinkCB(nsnull),
2749 1419 : mForgetSkippableCB(nsnull)
2750 : {
2751 : #ifdef DEBUG_CC
2752 : mExpectedGarbage.Init();
2753 : #endif
2754 :
2755 1419 : memset(mRuntimes, 0, sizeof(mRuntimes));
2756 1419 : mRuntimes[nsIProgrammingLanguage::CPLUSPLUS] = &mXPCOMRuntime;
2757 1419 : }
2758 :
2759 :
2760 1419 : nsCycleCollector::~nsCycleCollector()
2761 : {
2762 1419 : }
2763 :
2764 :
2765 : void
2766 1404 : nsCycleCollector::RegisterRuntime(PRUint32 langID,
2767 : nsCycleCollectionLanguageRuntime *rt)
2768 : {
2769 1404 : if (mParams.mDoNothing)
2770 0 : return;
2771 :
2772 1404 : if (langID > nsIProgrammingLanguage::MAX)
2773 0 : Fault("unknown language runtime in registration");
2774 :
2775 1404 : if (mRuntimes[langID])
2776 0 : Fault("multiple registrations of language runtime", rt);
2777 :
2778 1404 : mRuntimes[langID] = rt;
2779 : }
2780 :
2781 : nsCycleCollectionLanguageRuntime *
2782 0 : nsCycleCollector::GetRuntime(PRUint32 langID)
2783 : {
2784 0 : if (langID > nsIProgrammingLanguage::MAX)
2785 0 : return nsnull;
2786 :
2787 0 : return mRuntimes[langID];
2788 : }
2789 :
2790 : void
2791 0 : nsCycleCollector::ForgetRuntime(PRUint32 langID)
2792 : {
2793 0 : if (mParams.mDoNothing)
2794 0 : return;
2795 :
2796 0 : if (langID > nsIProgrammingLanguage::MAX)
2797 0 : Fault("unknown language runtime in deregistration");
2798 :
2799 0 : if (! mRuntimes[langID])
2800 0 : Fault("forgetting non-registered language runtime");
2801 :
2802 0 : mRuntimes[langID] = nsnull;
2803 : }
2804 :
2805 : #ifdef DEBUG_CC
2806 :
2807 : class Suppressor :
2808 : public nsCycleCollectionTraversalCallback
2809 : {
2810 : protected:
2811 : static char *sSuppressionList;
2812 : static bool sInitialized;
2813 : bool mSuppressThisNode;
2814 : public:
2815 : Suppressor()
2816 : {
2817 : }
2818 :
2819 : bool shouldSuppress(nsISupports *s)
2820 : {
2821 : if (!sInitialized) {
2822 : sSuppressionList = PR_GetEnv("XPCOM_CC_SUPPRESS");
2823 : sInitialized = true;
2824 : }
2825 : if (sSuppressionList == nsnull) {
2826 : mSuppressThisNode = false;
2827 : } else {
2828 : nsresult rv;
2829 : nsXPCOMCycleCollectionParticipant *cp;
2830 : rv = CallQueryInterface(s, &cp);
2831 : if (NS_FAILED(rv)) {
2832 : Fault("checking suppression on wrong type of pointer", s);
2833 : return true;
2834 : }
2835 : cp->Traverse(s, *this);
2836 : }
2837 : return mSuppressThisNode;
2838 : }
2839 :
2840 : NS_IMETHOD_(void) DescribeRefCountedNode(nsrefcnt refCount, size_t objSz,
2841 : const char *objName)
2842 : {
2843 : mSuppressThisNode = (PL_strstr(sSuppressionList, objName) != nsnull);
2844 : }
2845 :
2846 : NS_IMETHOD_(void) DescribeGCedNode(bool isMarked, size_t objSz,
2847 : const char *objName)
2848 : {
2849 : mSuppressThisNode = (PL_strstr(sSuppressionList, objName) != nsnull);
2850 : }
2851 :
2852 : NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports *root) {};
2853 : NS_IMETHOD_(void) NoteRoot(PRUint32 langID, void *root,
2854 : nsCycleCollectionParticipant* participant) {};
2855 : NS_IMETHOD_(void) NoteXPCOMChild(nsISupports *child) {}
2856 : NS_IMETHOD_(void) NoteScriptChild(PRUint32 langID, void *child) {}
2857 : NS_IMETHOD_(void) NoteNativeChild(void *child,
2858 : nsCycleCollectionParticipant *participant) {}
2859 : NS_IMETHOD_(void) NoteNextEdgeName(const char* name) {}
2860 : NS_IMETHOD_(void) NoteWeakMapping(void *map, void *key, void *val) {}
2861 : };
2862 :
2863 : char *Suppressor::sSuppressionList = nsnull;
2864 : bool Suppressor::sInitialized = false;
2865 :
2866 : static bool
2867 : nsCycleCollector_shouldSuppress(nsISupports *s)
2868 : {
2869 : Suppressor supp;
2870 : return supp.shouldSuppress(s);
2871 : }
2872 : #endif
2873 :
2874 : #ifdef DEBUG
2875 : static bool
2876 1489498 : nsCycleCollector_isScanSafe(nsISupports *s)
2877 : {
2878 1489498 : if (!s)
2879 0 : return false;
2880 :
2881 : nsXPCOMCycleCollectionParticipant *cp;
2882 1489498 : ToParticipant(s, &cp);
2883 :
2884 1489498 : return cp != nsnull;
2885 : }
2886 : #endif
2887 :
2888 : bool
2889 0 : nsCycleCollector::Suspect(nsISupports *n)
2890 : {
2891 0 : AbortIfOffMainThreadIfCheckFast();
2892 :
2893 : // Re-entering ::Suspect during collection used to be a fault, but
2894 : // we are canonicalizing nsISupports pointers using QI, so we will
2895 : // see some spurious refcount traffic here.
2896 :
2897 0 : if (mScanInProgress)
2898 0 : return false;
2899 :
2900 0 : NS_ASSERTION(nsCycleCollector_isScanSafe(n),
2901 : "suspected a non-scansafe pointer");
2902 :
2903 0 : if (mParams.mDoNothing)
2904 0 : return false;
2905 :
2906 : #ifdef DEBUG_CC
2907 : mStats.mSuspectNode++;
2908 :
2909 : if (nsCycleCollector_shouldSuppress(n))
2910 : return false;
2911 :
2912 : #ifndef __MINGW32__
2913 : if (mParams.mHookMalloc)
2914 : InitMemHook();
2915 : #endif
2916 :
2917 : if (mParams.mLogPointers) {
2918 : if (!mPtrLog)
2919 : mPtrLog = fopen("pointer_log", "w");
2920 : fprintf(mPtrLog, "S %p\n", static_cast<void*>(n));
2921 : }
2922 : #endif
2923 :
2924 0 : return mPurpleBuf.PutCompatObject(n);
2925 : }
2926 :
2927 :
2928 : bool
2929 0 : nsCycleCollector::Forget(nsISupports *n)
2930 : {
2931 0 : AbortIfOffMainThreadIfCheckFast();
2932 :
2933 : // Re-entering ::Forget during collection used to be a fault, but
2934 : // we are canonicalizing nsISupports pointers using QI, so we will
2935 : // see some spurious refcount traffic here.
2936 :
2937 0 : if (mScanInProgress)
2938 0 : return false;
2939 :
2940 0 : if (mParams.mDoNothing)
2941 0 : return true; // it's as good as forgotten
2942 :
2943 : #ifdef DEBUG_CC
2944 : mStats.mForgetNode++;
2945 :
2946 : #ifndef __MINGW32__
2947 : if (mParams.mHookMalloc)
2948 : InitMemHook();
2949 : #endif
2950 :
2951 : if (mParams.mLogPointers) {
2952 : if (!mPtrLog)
2953 : mPtrLog = fopen("pointer_log", "w");
2954 : fprintf(mPtrLog, "F %p\n", static_cast<void*>(n));
2955 : }
2956 : #endif
2957 :
2958 0 : mPurpleBuf.RemoveCompatObject(n);
2959 0 : return true;
2960 : }
2961 :
2962 : nsPurpleBufferEntry*
2963 1489498 : nsCycleCollector::Suspect2(nsISupports *n)
2964 : {
2965 1489498 : AbortIfOffMainThreadIfCheckFast();
2966 :
2967 : // Re-entering ::Suspect during collection used to be a fault, but
2968 : // we are canonicalizing nsISupports pointers using QI, so we will
2969 : // see some spurious refcount traffic here.
2970 :
2971 1489498 : if (mScanInProgress)
2972 0 : return nsnull;
2973 :
2974 1489498 : NS_ASSERTION(nsCycleCollector_isScanSafe(n),
2975 : "suspected a non-scansafe pointer");
2976 :
2977 1489498 : if (mParams.mDoNothing)
2978 0 : return nsnull;
2979 :
2980 : #ifdef DEBUG_CC
2981 : mStats.mSuspectNode++;
2982 :
2983 : if (nsCycleCollector_shouldSuppress(n))
2984 : return nsnull;
2985 :
2986 : #ifndef __MINGW32__
2987 : if (mParams.mHookMalloc)
2988 : InitMemHook();
2989 : #endif
2990 :
2991 : if (mParams.mLogPointers) {
2992 : if (!mPtrLog)
2993 : mPtrLog = fopen("pointer_log", "w");
2994 : fprintf(mPtrLog, "S %p\n", static_cast<void*>(n));
2995 : }
2996 : #endif
2997 :
2998 : // Caller is responsible for filling in result's mRefCnt.
2999 1489498 : return mPurpleBuf.Put(n);
3000 : }
3001 :
3002 :
3003 : bool
3004 1350470 : nsCycleCollector::Forget2(nsPurpleBufferEntry *e)
3005 : {
3006 1350470 : AbortIfOffMainThreadIfCheckFast();
3007 :
3008 : // Re-entering ::Forget during collection used to be a fault, but
3009 : // we are canonicalizing nsISupports pointers using QI, so we will
3010 : // see some spurious refcount traffic here.
3011 :
3012 1350470 : if (mScanInProgress)
3013 0 : return false;
3014 :
3015 : #ifdef DEBUG_CC
3016 : LogPurpleRemoval(e->mObject);
3017 : #endif
3018 :
3019 1350470 : mPurpleBuf.Remove(e);
3020 1350470 : return true;
3021 : }
3022 :
3023 : #ifdef DEBUG_CC
3024 : void
3025 : nsCycleCollector_logPurpleRemoval(void* aObject)
3026 : {
3027 : if (sCollector) {
3028 : sCollector->LogPurpleRemoval(aObject);
3029 : }
3030 : }
3031 :
3032 : void
3033 : nsCycleCollector::LogPurpleRemoval(void* aObject)
3034 : {
3035 : AbortIfOffMainThreadIfCheckFast();
3036 :
3037 : mStats.mForgetNode++;
3038 :
3039 : #ifndef __MINGW32__
3040 : if (mParams.mHookMalloc)
3041 : InitMemHook();
3042 : #endif
3043 :
3044 : if (mParams.mLogPointers) {
3045 : if (!mPtrLog)
3046 : mPtrLog = fopen("pointer_log", "w");
3047 : fprintf(mPtrLog, "F %p\n", aObject);
3048 : }
3049 : mPurpleBuf.mNormalObjects.RemoveEntry(aObject);
3050 : }
3051 :
3052 : void
3053 : nsCycleCollector::Allocated(void *n, size_t sz)
3054 : {
3055 : }
3056 :
3057 : void
3058 : nsCycleCollector::Freed(void *n)
3059 : {
3060 : mStats.mFreeCalls++;
3061 :
3062 : if (!n) {
3063 : // Ignore null pointers coming through
3064 : return;
3065 : }
3066 :
3067 : if (mPurpleBuf.Exists(n)) {
3068 : mStats.mForgetNode++;
3069 : mStats.mFreedWhilePurple++;
3070 : Fault("freed while purple", n);
3071 :
3072 : if (mParams.mLogPointers) {
3073 : if (!mPtrLog)
3074 : mPtrLog = fopen("pointer_log", "w");
3075 : fprintf(mPtrLog, "R %p\n", n);
3076 : }
3077 : }
3078 : }
3079 : #endif
3080 :
3081 : // The cycle collector uses the mark bitmap to discover what JS objects
3082 : // were reachable only from XPConnect roots that might participate in
3083 : // cycles. We ask the JS runtime whether we need to force a GC before
3084 : // this CC. It returns true on startup (before the mark bits have been set),
3085 : // and also when UnmarkGray has run out of stack. We also force GCs on shut
3086 : // down to collect cycles involving both DOM and JS.
3087 : void
3088 1925 : nsCycleCollector::GCIfNeeded(bool aForceGC)
3089 : {
3090 1925 : NS_ASSERTION(NS_IsMainThread(),
3091 : "nsCycleCollector::GCIfNeeded() must be called on the main thread.");
3092 :
3093 1925 : if (mParams.mDoNothing)
3094 0 : return;
3095 :
3096 1925 : if (!mRuntimes[nsIProgrammingLanguage::JAVASCRIPT])
3097 15 : return;
3098 :
3099 : nsCycleCollectionJSRuntime* rt =
3100 : static_cast<nsCycleCollectionJSRuntime*>
3101 1910 : (mRuntimes[nsIProgrammingLanguage::JAVASCRIPT]);
3102 1910 : if (!aForceGC) {
3103 48 : bool needGC = rt->NeedCollect();
3104 : // Only do a telemetry ping for non-shutdown CCs.
3105 48 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_NEED_GC, needGC);
3106 48 : if (!needGC)
3107 46 : return;
3108 2 : if (mResults)
3109 0 : mResults->mForcedGC = true;
3110 : }
3111 :
3112 1864 : TimeLog timeLog;
3113 :
3114 : // rt->Collect() must be called from the main thread,
3115 : // because it invokes XPCJSRuntime::GCCallback(cx, JSGC_BEGIN)
3116 : // which returns false if not in the main thread.
3117 1864 : rt->Collect(js::gcreason::CC_FORCED, nsGCNormal);
3118 1864 : timeLog.Checkpoint("GC()");
3119 : }
3120 :
3121 : bool
3122 1467 : nsCycleCollector::PrepareForCollection(nsCycleCollectorResults *aResults,
3123 : nsTArray<PtrInfo*> *aWhiteNodes)
3124 : {
3125 : #if defined(DEBUG_CC) && !defined(__MINGW32__)
3126 : if (!mParams.mDoNothing && mParams.mHookMalloc)
3127 : InitMemHook();
3128 : #endif
3129 :
3130 : // This can legitimately happen in a few cases. See bug 383651.
3131 1467 : if (mCollectionInProgress)
3132 0 : return false;
3133 :
3134 1467 : TimeLog timeLog;
3135 :
3136 1467 : mCollectionStart = TimeStamp::Now();
3137 1467 : mVisitedRefCounted = 0;
3138 1467 : mVisitedGCed = 0;
3139 :
3140 1467 : mCollectionInProgress = true;
3141 :
3142 : nsCOMPtr<nsIObserverService> obs =
3143 2934 : mozilla::services::GetObserverService();
3144 1467 : if (obs)
3145 48 : obs->NotifyObservers(nsnull, "cycle-collector-begin", nsnull);
3146 :
3147 1467 : mFollowupCollection = false;
3148 :
3149 1467 : mResults = aResults;
3150 1467 : mWhiteNodes = aWhiteNodes;
3151 :
3152 1467 : timeLog.Checkpoint("PrepareForCollection()");
3153 :
3154 1467 : return true;
3155 : }
3156 :
3157 : void
3158 1467 : nsCycleCollector::CleanupAfterCollection()
3159 : {
3160 1467 : mWhiteNodes = nsnull;
3161 1467 : mCollectionInProgress = false;
3162 :
3163 : #ifdef XP_OS2
3164 : // Now that the cycle collector has freed some memory, we can try to
3165 : // force the C library to give back as much memory to the system as
3166 : // possible.
3167 : _heapmin();
3168 : #endif
3169 :
3170 1467 : PRUint32 interval = (PRUint32) ((TimeStamp::Now() - mCollectionStart).ToMilliseconds());
3171 : #ifdef COLLECT_TIME_DEBUG
3172 : printf("cc: total cycle collector time was %ums\n", interval);
3173 : if (mResults) {
3174 : printf("cc: visited %u ref counted and %u GCed objects, freed %d ref counted and %d GCed objects.\n",
3175 : mVisitedRefCounted, mVisitedGCed,
3176 : mResults->mFreedRefCounted, mResults->mFreedGCed);
3177 : } else {
3178 : printf("cc: visited %u ref counted and %u GCed objects, freed %d.\n",
3179 : mVisitedRefCounted, mVisitedGCed, mWhiteNodeCount);
3180 : }
3181 : printf("cc: \n");
3182 : #endif
3183 1467 : if (mResults) {
3184 48 : mResults->mVisitedRefCounted = mVisitedRefCounted;
3185 48 : mResults->mVisitedGCed = mVisitedGCed;
3186 48 : mResults = nsnull;
3187 : }
3188 1467 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR, interval);
3189 1467 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_REF_COUNTED, mVisitedRefCounted);
3190 1467 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_GCED, mVisitedGCed);
3191 1467 : Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_COLLECTED, mWhiteNodeCount);
3192 :
3193 : #ifdef DEBUG_CC
3194 : ExplainLiveExpectedGarbage();
3195 : #endif
3196 1467 : }
3197 :
3198 : void
3199 1419 : nsCycleCollector::Collect(nsCycleCollectorResults *aResults,
3200 : PRUint32 aTryCollections,
3201 : nsICycleCollectorListener *aListener)
3202 : {
3203 2838 : nsAutoTArray<PtrInfo*, 4000> whiteNodes;
3204 :
3205 1419 : if (!PrepareForCollection(aResults, &whiteNodes))
3206 : return;
3207 :
3208 1419 : PRUint32 totalCollections = 0;
3209 3296 : while (aTryCollections > totalCollections) {
3210 : // Synchronous cycle collection. Always force a JS GC beforehand.
3211 1877 : GCIfNeeded(true);
3212 1877 : if (aListener && NS_FAILED(aListener->Begin()))
3213 0 : aListener = nsnull;
3214 3296 : if (!(BeginCollection(aListener) &&
3215 1877 : FinishCollection(aListener)))
3216 1419 : break;
3217 :
3218 458 : ++totalCollections;
3219 : }
3220 :
3221 1419 : CleanupAfterCollection();
3222 : }
3223 :
3224 : bool
3225 1925 : nsCycleCollector::BeginCollection(nsICycleCollectorListener *aListener)
3226 : {
3227 : // aListener should be Begin()'d before this
3228 1925 : TimeLog timeLog;
3229 :
3230 1925 : if (mParams.mDoNothing)
3231 0 : return false;
3232 :
3233 3850 : GCGraphBuilder builder(mGraph, mRuntimes, aListener);
3234 1925 : if (!builder.Initialized())
3235 0 : return false;
3236 :
3237 23100 : for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
3238 21175 : if (mRuntimes[i])
3239 3835 : mRuntimes[i]->BeginCycleCollection(builder, false);
3240 : }
3241 :
3242 1925 : timeLog.Checkpoint("mRuntimes[*]->BeginCycleCollection()");
3243 :
3244 : #ifdef DEBUG_CC
3245 : PRUint32 purpleStart = builder.Count();
3246 : #endif
3247 1925 : mScanInProgress = true;
3248 1925 : SelectPurple(builder);
3249 : #ifdef DEBUG_CC
3250 : PRUint32 purpleEnd = builder.Count();
3251 :
3252 : if (purpleStart != purpleEnd) {
3253 : #ifndef __MINGW32__
3254 : if (mParams.mHookMalloc)
3255 : InitMemHook();
3256 : #endif
3257 : if (mParams.mLogPointers && !mPtrLog)
3258 : mPtrLog = fopen("pointer_log", "w");
3259 :
3260 : PRUint32 i = 0;
3261 : NodePool::Enumerator queue(mGraph.mNodes);
3262 : while (i++ < purpleStart) {
3263 : queue.GetNext();
3264 : }
3265 : while (i++ < purpleEnd) {
3266 : mStats.mForgetNode++;
3267 : if (mParams.mLogPointers)
3268 : fprintf(mPtrLog, "F %p\n", queue.GetNext()->mPointer);
3269 : }
3270 : }
3271 : #endif
3272 :
3273 1925 : timeLog.Checkpoint("SelectPurple()");
3274 :
3275 1925 : if (builder.Count() > 0) {
3276 : // The main Bacon & Rajan collection algorithm.
3277 :
3278 1910 : MarkRoots(builder);
3279 1910 : timeLog.Checkpoint("MarkRoots()");
3280 :
3281 1910 : ScanRoots();
3282 1910 : timeLog.Checkpoint("ScanRoots()");
3283 :
3284 1910 : mScanInProgress = false;
3285 :
3286 1910 : if (aListener) {
3287 0 : aListener->BeginResults();
3288 :
3289 0 : NodePool::Enumerator etor(mGraph.mNodes);
3290 0 : while (!etor.IsDone()) {
3291 0 : PtrInfo *pi = etor.GetNext();
3292 0 : if (pi->mColor == black &&
3293 : pi->mRefCount > 0 && pi->mRefCount < PR_UINT32_MAX &&
3294 : pi->mInternalRefs != pi->mRefCount) {
3295 : aListener->DescribeRoot((PRUint64)pi->mPointer,
3296 0 : pi->mInternalRefs);
3297 : }
3298 : }
3299 : }
3300 :
3301 : #ifdef DEBUG_CC
3302 : if (mFollowupCollection && purpleStart != purpleEnd) {
3303 : PRUint32 i = 0;
3304 : NodePool::Enumerator queue(mGraph.mNodes);
3305 : while (i++ < purpleStart) {
3306 : queue.GetNext();
3307 : }
3308 : while (i++ < purpleEnd) {
3309 : PtrInfo *pi = queue.GetNext();
3310 : if (pi->mColor == white) {
3311 : printf("nsCycleCollector: a later shutdown collection collected the additional\n"
3312 : " suspect %p %s\n"
3313 : " (which could be fixed by improving traversal)\n",
3314 : pi->mPointer, pi->mName);
3315 : }
3316 : }
3317 : }
3318 : #endif
3319 :
3320 22920 : for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
3321 21010 : if (mRuntimes[i])
3322 3820 : mRuntimes[i]->FinishTraverse();
3323 : }
3324 1910 : timeLog.Checkpoint("mRuntimes[*]->FinishTraverse()");
3325 : }
3326 : else {
3327 15 : mScanInProgress = false;
3328 : }
3329 :
3330 1925 : return true;
3331 : }
3332 :
3333 : bool
3334 1925 : nsCycleCollector::FinishCollection(nsICycleCollectorListener *aListener)
3335 : {
3336 1925 : TimeLog timeLog;
3337 1925 : bool collected = CollectWhite(aListener);
3338 1925 : timeLog.Checkpoint("CollectWhite()");
3339 :
3340 : #ifdef DEBUG_CC
3341 : mStats.mCollection++;
3342 : if (mParams.mReportStats)
3343 : mStats.Dump();
3344 : #endif
3345 :
3346 23100 : for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
3347 21175 : if (mRuntimes[i])
3348 3835 : mRuntimes[i]->FinishCycleCollection();
3349 : }
3350 1925 : timeLog.Checkpoint("mRuntimes[*]->FinishCycleCollection()");
3351 :
3352 1925 : mFollowupCollection = true;
3353 :
3354 : #ifdef DEBUG_CC
3355 : // We wait until after FinishCollection to check the white nodes because
3356 : // some objects may outlive CollectWhite but then be freed by
3357 : // FinishCycleCollection (like XPConnect's deferred release of native
3358 : // objects).
3359 : PRUint32 i, count = mWhiteNodes->Length();
3360 : for (i = 0; i < count; ++i) {
3361 : PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
3362 : if (pinfo->mLangID == nsIProgrammingLanguage::CPLUSPLUS &&
3363 : mPurpleBuf.Exists(pinfo->mPointer)) {
3364 : printf("nsCycleCollector: %s object @%p is still alive after\n"
3365 : " calling RootAndUnlinkJSObjects, Unlink, and Unroot on"
3366 : " it! This probably\n"
3367 : " means the Unlink implementation was insufficient.\n",
3368 : pinfo->mName, pinfo->mPointer);
3369 : }
3370 : }
3371 : #endif
3372 :
3373 1925 : mWhiteNodes->Clear();
3374 1925 : ClearGraph();
3375 1925 : timeLog.Checkpoint("ClearGraph()");
3376 :
3377 1925 : mParams.mDoNothing = false;
3378 :
3379 1925 : return collected;
3380 : }
3381 :
3382 : PRUint32
3383 23251 : nsCycleCollector::SuspectedCount()
3384 : {
3385 23251 : return mPurpleBuf.Count();
3386 : }
3387 :
3388 : void
3389 1419 : nsCycleCollector::Shutdown()
3390 : {
3391 : // Here we want to run a final collection and then permanently
3392 : // disable the collector because the program is shutting down.
3393 :
3394 2838 : nsCOMPtr<nsCycleCollectorLogger> listener;
3395 1419 : if (mParams.mLogGraphs) {
3396 0 : listener = new nsCycleCollectorLogger();
3397 : }
3398 1419 : Collect(nsnull, SHUTDOWN_COLLECTIONS(mParams), listener);
3399 :
3400 : #ifdef DEBUG_CC
3401 : GCGraphBuilder builder(mGraph, mRuntimes, nsnull);
3402 : mScanInProgress = true;
3403 : SelectPurple(builder);
3404 : mScanInProgress = false;
3405 : if (builder.Count() != 0) {
3406 : printf("Might have been able to release more cycles if the cycle collector would "
3407 : "run once more at shutdown.\n");
3408 : }
3409 : ClearGraph();
3410 : #endif
3411 1419 : mParams.mDoNothing = true;
3412 1419 : }
3413 :
3414 : #ifdef DEBUG_CC
3415 :
3416 : static PLDHashOperator
3417 : AddExpectedGarbage(nsVoidPtrHashKey *p, void *arg)
3418 : {
3419 : GCGraphBuilder *builder = static_cast<GCGraphBuilder*>(arg);
3420 : nsISupports *root =
3421 : static_cast<nsISupports*>(const_cast<void*>(p->GetKey()));
3422 : builder->NoteXPCOMRoot(root);
3423 : return PL_DHASH_NEXT;
3424 : }
3425 :
3426 : struct SetSCCVisitor
3427 : {
3428 : SetSCCVisitor(PRUint32 aIndex) : mIndex(aIndex) {}
3429 : bool ShouldVisitNode(PtrInfo const *pi) { return pi->mSCCIndex == 0; }
3430 : void VisitNode(PtrInfo *pi) { pi->mSCCIndex = mIndex; }
3431 : private:
3432 : PRUint32 mIndex;
3433 : };
3434 :
3435 : struct SetNonRootGreyVisitor
3436 : {
3437 : bool ShouldVisitNode(PtrInfo const *pi) { return pi->mColor == white; }
3438 : void VisitNode(PtrInfo *pi) { pi->mColor = grey; }
3439 : };
3440 :
3441 : static void
3442 : PrintPathToExpectedGarbage(PtrInfo *pi)
3443 : {
3444 : printf(" An object expected to be garbage could be "
3445 : "reached from it by the path:\n");
3446 : for (PtrInfo *path = pi, *prev = nsnull; prev != path;
3447 : prev = path,
3448 : path = path->mShortestPathToExpectedGarbage) {
3449 : if (prev) {
3450 : nsCString *edgeName = prev
3451 : ->mShortestPathToExpectedGarbageEdgeName;
3452 : printf(" via %s\n",
3453 : edgeName->IsEmpty() ? "<unknown edge>"
3454 : : edgeName->get());
3455 : }
3456 : printf(" %s %p\n", path->mName, path->mPointer);
3457 : }
3458 : }
3459 :
3460 : void
3461 : nsCycleCollector::ExplainLiveExpectedGarbage()
3462 : {
3463 : if (mScanInProgress || mCollectionInProgress)
3464 : Fault("can't explain expected garbage during collection itself");
3465 :
3466 : if (mParams.mDoNothing) {
3467 : printf("nsCycleCollector: not explaining expected garbage since\n"
3468 : " cycle collection disabled\n");
3469 : return;
3470 : }
3471 :
3472 : mCollectionInProgress = true;
3473 : mScanInProgress = true;
3474 :
3475 : {
3476 : GCGraphBuilder builder(mGraph, mRuntimes, nsnull);
3477 :
3478 : // Instead of adding roots from the purple buffer, we add them
3479 : // from the list of nodes we were expected to collect.
3480 : // Put the expected garbage in *before* calling
3481 : // BeginCycleCollection so that we can separate the expected
3482 : // garbage from the NoteRoot calls in such a way that something
3483 : // that's in both is considered expected garbage.
3484 : mExpectedGarbage.EnumerateEntries(&AddExpectedGarbage, &builder);
3485 :
3486 : PRUint32 expectedGarbageCount = builder.Count();
3487 :
3488 : for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
3489 : if (mRuntimes[i])
3490 : mRuntimes[i]->BeginCycleCollection(builder, true);
3491 : }
3492 :
3493 : // But just for extra information, add entries from the purple
3494 : // buffer too, since it may give us extra information about
3495 : // traversal deficiencies.
3496 : mPurpleBuf.NoteAll(builder);
3497 :
3498 : MarkRoots(builder);
3499 : ScanRoots();
3500 :
3501 : mScanInProgress = false;
3502 :
3503 : for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
3504 : if (mRuntimes[i]) {
3505 : mRuntimes[i]->FinishTraverse();
3506 : }
3507 : }
3508 :
3509 : bool describeExtraRefcounts = false;
3510 : bool findCycleRoots = false;
3511 : {
3512 : NodePool::Enumerator queue(mGraph.mNodes);
3513 : PRUint32 i = 0;
3514 : while (!queue.IsDone()) {
3515 : PtrInfo *pi = queue.GetNext();
3516 : if (pi->mColor == white) {
3517 : findCycleRoots = true;
3518 : }
3519 :
3520 : if (pi->mInternalRefs != pi->mRefCount &&
3521 : (i < expectedGarbageCount || i >= mGraph.mRootCount)) {
3522 : // This check isn't particularly useful anymore
3523 : // given that we need to enter this part for i >=
3524 : // mGraph.mRootCount and there are plenty of
3525 : // NoteRoot roots.
3526 : describeExtraRefcounts = true;
3527 : }
3528 : ++i;
3529 : }
3530 : }
3531 :
3532 : if ((describeExtraRefcounts || findCycleRoots) &&
3533 : CreateReversedEdges()) {
3534 : // Note that the external references may have been external
3535 : // to a different node in the cycle collection that just
3536 : // happened, if that different node was purple and then
3537 : // black.
3538 :
3539 : // Use mSCCIndex temporarily to track whether we've reached
3540 : // nodes in the breadth-first search.
3541 : const PRUint32 INDEX_UNREACHED = 0;
3542 : const PRUint32 INDEX_REACHED = 1;
3543 : NodePool::Enumerator etor_clear(mGraph.mNodes);
3544 : while (!etor_clear.IsDone()) {
3545 : PtrInfo *pi = etor_clear.GetNext();
3546 : pi->mSCCIndex = INDEX_UNREACHED;
3547 : }
3548 :
3549 : nsDeque queue; // for breadth-first search
3550 : NodePool::Enumerator etor_roots(mGraph.mNodes);
3551 : for (PRUint32 i = 0; i < mGraph.mRootCount; ++i) {
3552 : PtrInfo *root_pi = etor_roots.GetNext();
3553 : if (i < expectedGarbageCount) {
3554 : root_pi->mSCCIndex = INDEX_REACHED;
3555 : root_pi->mShortestPathToExpectedGarbage = root_pi;
3556 : queue.Push(root_pi);
3557 : }
3558 : }
3559 :
3560 : while (queue.GetSize() > 0) {
3561 : PtrInfo *pi = (PtrInfo*)queue.PopFront();
3562 : for (ReversedEdge *e = pi->mReversedEdges; e; e = e->mNext) {
3563 : if (e->mTarget->mSCCIndex == INDEX_UNREACHED) {
3564 : e->mTarget->mSCCIndex = INDEX_REACHED;
3565 : PtrInfo *target = e->mTarget;
3566 : if (!target->mShortestPathToExpectedGarbage) {
3567 : target->mShortestPathToExpectedGarbage = pi;
3568 : target->mShortestPathToExpectedGarbageEdgeName =
3569 : e->mEdgeName;
3570 : }
3571 : queue.Push(target);
3572 : }
3573 : }
3574 :
3575 : if (pi->mRefCount == PR_UINT32_MAX ||
3576 : (pi->mInternalRefs != pi->mRefCount && pi->mRefCount > 0)) {
3577 : if (pi->mRefCount == PR_UINT32_MAX) {
3578 : printf("nsCycleCollector: %s %p was not collected due "
3579 : "to \n"
3580 : " external references\n",
3581 : pi->mName, pi->mPointer);
3582 : }
3583 : else {
3584 : printf("nsCycleCollector: %s %p was not collected due "
3585 : "to %d\n"
3586 : " external references (%d total - %d known)\n",
3587 : pi->mName, pi->mPointer,
3588 : pi->mRefCount - pi->mInternalRefs,
3589 : pi->mRefCount, pi->mInternalRefs);
3590 : }
3591 :
3592 : PrintPathToExpectedGarbage(pi);
3593 :
3594 : if (pi->mRefCount == PR_UINT32_MAX) {
3595 : printf(" The known references to it were from:\n");
3596 : }
3597 : else {
3598 : printf(" The %d known references to it were from:\n",
3599 : pi->mInternalRefs);
3600 : }
3601 : for (ReversedEdge *e = pi->mReversedEdges;
3602 : e; e = e->mNext) {
3603 : printf(" %s %p",
3604 : e->mTarget->mName, e->mTarget->mPointer);
3605 : if (!e->mEdgeName->IsEmpty()) {
3606 : printf(" via %s", e->mEdgeName->get());
3607 : }
3608 : printf("\n");
3609 : }
3610 : mRuntimes[pi->mLangID]->PrintAllReferencesTo(pi->mPointer);
3611 : }
3612 : }
3613 :
3614 : if (findCycleRoots) {
3615 : // NOTE: This code changes the white nodes that are not
3616 : // roots to gray.
3617 :
3618 : // Put the nodes in post-order traversal order from a
3619 : // depth-first search.
3620 : nsDeque DFSPostOrder;
3621 :
3622 : {
3623 : // Use mSCCIndex temporarily to track the DFS numbering:
3624 : const PRUint32 INDEX_UNREACHED = 0;
3625 : const PRUint32 INDEX_TRAVERSING = 1;
3626 : const PRUint32 INDEX_NUMBERED = 2;
3627 :
3628 : NodePool::Enumerator etor_clear(mGraph.mNodes);
3629 : while (!etor_clear.IsDone()) {
3630 : PtrInfo *pi = etor_clear.GetNext();
3631 : pi->mSCCIndex = INDEX_UNREACHED;
3632 : }
3633 :
3634 : nsDeque stack;
3635 :
3636 : NodePool::Enumerator etor_roots(mGraph.mNodes);
3637 : for (PRUint32 i = 0; i < mGraph.mRootCount; ++i) {
3638 : PtrInfo *root_pi = etor_roots.GetNext();
3639 : stack.Push(root_pi);
3640 : }
3641 :
3642 : while (stack.GetSize() > 0) {
3643 : PtrInfo *pi = (PtrInfo*)stack.Peek();
3644 : if (pi->mSCCIndex == INDEX_UNREACHED) {
3645 : pi->mSCCIndex = INDEX_TRAVERSING;
3646 : for (EdgePool::Iterator child = pi->FirstChild(),
3647 : child_end = pi->LastChild();
3648 : child != child_end; ++child) {
3649 : stack.Push(*child);
3650 : }
3651 : } else {
3652 : stack.Pop();
3653 : // Somebody else might have numbered it already
3654 : // (since this is depth-first, not breadth-first).
3655 : // This happens if a node is pushed on the stack
3656 : // a second time while it is on the stack in
3657 : // UNREACHED state.
3658 : if (pi->mSCCIndex == INDEX_TRAVERSING) {
3659 : pi->mSCCIndex = INDEX_NUMBERED;
3660 : DFSPostOrder.Push(pi);
3661 : }
3662 : }
3663 : }
3664 : }
3665 :
3666 : // Put the nodes into strongly-connected components.
3667 : {
3668 : NodePool::Enumerator etor_clear(mGraph.mNodes);
3669 : while (!etor_clear.IsDone()) {
3670 : PtrInfo *pi = etor_clear.GetNext();
3671 : pi->mSCCIndex = 0;
3672 : }
3673 :
3674 : PRUint32 currentSCC = 1;
3675 :
3676 : while (DFSPostOrder.GetSize() > 0) {
3677 : GraphWalker<SetSCCVisitor>(SetSCCVisitor(currentSCC)).Walk((PtrInfo*)DFSPostOrder.PopFront());
3678 : ++currentSCC;
3679 : }
3680 : }
3681 :
3682 : // Mark any white nodes reachable from other components as
3683 : // grey.
3684 : {
3685 : NodePool::Enumerator queue(mGraph.mNodes);
3686 : while (!queue.IsDone()) {
3687 : PtrInfo *pi = queue.GetNext();
3688 : if (pi->mColor != white)
3689 : continue;
3690 : for (EdgePool::Iterator child = pi->FirstChild(),
3691 : child_end = pi->LastChild();
3692 : child != child_end; ++child) {
3693 : if ((*child)->mSCCIndex != pi->mSCCIndex) {
3694 : GraphWalker<SetNonRootGreyVisitor>(SetNonRootGreyVisitor()).Walk(*child);
3695 : }
3696 : }
3697 : }
3698 : }
3699 :
3700 : {
3701 : NodePool::Enumerator queue(mGraph.mNodes);
3702 : while (!queue.IsDone()) {
3703 : PtrInfo *pi = queue.GetNext();
3704 : if (pi->mColor == white) {
3705 : if (pi->mLangID ==
3706 : nsIProgrammingLanguage::CPLUSPLUS &&
3707 : mPurpleBuf.Exists(pi->mPointer)) {
3708 : printf(
3709 : "nsCycleCollector: %s %p in component %d\n"
3710 : " which was reference counted during the root/unlink/unroot phase of the\n"
3711 : " last collection was not collected due to failure to unlink (see other\n"
3712 : " warnings) or deficiency in traverse that causes cycles referenced only\n"
3713 : " from other cycles to require multiple rounds of cycle collection in which\n"
3714 : " this object was likely the reachable object\n",
3715 : pi->mName, pi->mPointer, pi->mSCCIndex);
3716 : } else {
3717 : printf(
3718 : "nsCycleCollector: %s %p in component %d\n"
3719 : " was not collected due to missing call to suspect, failure to unlink (see\n"
3720 : " other warnings), or deficiency in traverse that causes cycles referenced\n"
3721 : " only from other cycles to require multiple rounds of cycle collection\n",
3722 : pi->mName, pi->mPointer, pi->mSCCIndex);
3723 : }
3724 : if (pi->mShortestPathToExpectedGarbage)
3725 : PrintPathToExpectedGarbage(pi);
3726 : }
3727 : }
3728 : }
3729 : }
3730 :
3731 : DestroyReversedEdges();
3732 : }
3733 : }
3734 :
3735 : ClearGraph();
3736 :
3737 : mCollectionInProgress = false;
3738 :
3739 : for (PRUint32 i = 0; i <= nsIProgrammingLanguage::MAX; ++i) {
3740 : if (mRuntimes[i])
3741 : mRuntimes[i]->FinishCycleCollection();
3742 : }
3743 : }
3744 :
3745 : bool
3746 : nsCycleCollector::CreateReversedEdges()
3747 : {
3748 : // Count the edges in the graph.
3749 : PRUint32 edgeCount = 0;
3750 : NodePool::Enumerator countQueue(mGraph.mNodes);
3751 : while (!countQueue.IsDone()) {
3752 : PtrInfo *pi = countQueue.GetNext();
3753 : for (EdgePool::Iterator e = pi->FirstChild(), e_end = pi->LastChild();
3754 : e != e_end; ++e, ++edgeCount) {
3755 : }
3756 : }
3757 :
3758 : // Allocate a pool to hold all of the edges.
3759 : mGraph.mReversedEdges = new ReversedEdge[edgeCount];
3760 : if (mGraph.mReversedEdges == nsnull) {
3761 : NS_NOTREACHED("allocation failure creating reversed edges");
3762 : return false;
3763 : }
3764 :
3765 : // Fill in the reversed edges by scanning all forward edges.
3766 : ReversedEdge *current = mGraph.mReversedEdges;
3767 : NodePool::Enumerator buildQueue(mGraph.mNodes);
3768 : while (!buildQueue.IsDone()) {
3769 : PtrInfo *pi = buildQueue.GetNext();
3770 : PRInt32 i = 0;
3771 : for (EdgePool::Iterator e = pi->FirstChild(), e_end = pi->LastChild();
3772 : e != e_end; ++e) {
3773 : current->mTarget = pi;
3774 : current->mEdgeName = &pi->mEdgeNames[i];
3775 : current->mNext = (*e)->mReversedEdges;
3776 : (*e)->mReversedEdges = current;
3777 : ++current;
3778 : ++i;
3779 : }
3780 : }
3781 : NS_ASSERTION(current - mGraph.mReversedEdges == ptrdiff_t(edgeCount),
3782 : "misallocation");
3783 : return true;
3784 : }
3785 :
3786 : void
3787 : nsCycleCollector::DestroyReversedEdges()
3788 : {
3789 : NodePool::Enumerator queue(mGraph.mNodes);
3790 : while (!queue.IsDone()) {
3791 : PtrInfo *pi = queue.GetNext();
3792 : pi->mReversedEdges = nsnull;
3793 : }
3794 :
3795 : delete mGraph.mReversedEdges;
3796 : mGraph.mReversedEdges = nsnull;
3797 : }
3798 :
3799 : void
3800 : nsCycleCollector::ShouldBeFreed(nsISupports *n)
3801 : {
3802 : if (n) {
3803 : mExpectedGarbage.PutEntry(n);
3804 : }
3805 : }
3806 :
3807 : void
3808 : nsCycleCollector::WasFreed(nsISupports *n)
3809 : {
3810 : if (n) {
3811 : mExpectedGarbage.RemoveEntry(n);
3812 : }
3813 : }
3814 : #endif
3815 :
3816 :
3817 : ////////////////////////
3818 : // Memory reporter
3819 : ////////////////////////
3820 :
3821 : static PRInt64
3822 0 : ReportCycleCollectorMem()
3823 : {
3824 0 : if (!sCollector)
3825 0 : return 0;
3826 : PRInt64 size = sizeof(nsCycleCollector) +
3827 0 : sCollector->mPurpleBuf.BlocksSize() +
3828 0 : sCollector->mGraph.BlocksSize();
3829 0 : if (sCollector->mWhiteNodes)
3830 0 : size += sCollector->mWhiteNodes->Capacity() * sizeof(PtrInfo*);
3831 0 : return size;
3832 : }
3833 :
3834 1413 : NS_MEMORY_REPORTER_IMPLEMENT(CycleCollector,
3835 : "explicit/cycle-collector",
3836 : KIND_HEAP,
3837 : UNITS_BYTES,
3838 : ReportCycleCollectorMem,
3839 : "Memory used by the cycle collector. This "
3840 : "includes the cycle collector structure, the "
3841 : "purple buffer, the graph, and the white nodes. "
3842 : "The latter two are expected to be empty when the "
3843 4278 : "cycle collector is idle.")
3844 :
3845 :
3846 : ////////////////////////////////////////////////////////////////////////
3847 : // Module public API (exported in nsCycleCollector.h)
3848 : // Just functions that redirect into the singleton, once it's built.
3849 : ////////////////////////////////////////////////////////////////////////
3850 :
3851 : void
3852 1404 : nsCycleCollector_registerRuntime(PRUint32 langID,
3853 : nsCycleCollectionLanguageRuntime *rt)
3854 : {
3855 : static bool regMemReport = true;
3856 1404 : if (sCollector)
3857 1404 : sCollector->RegisterRuntime(langID, rt);
3858 1404 : if (regMemReport) {
3859 1404 : regMemReport = false;
3860 1404 : NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(CycleCollector));
3861 : }
3862 1404 : }
3863 :
3864 : nsCycleCollectionLanguageRuntime *
3865 0 : nsCycleCollector_getRuntime(PRUint32 langID)
3866 : {
3867 0 : if (sCollector)
3868 0 : sCollector->GetRuntime(langID);
3869 0 : return nsnull;
3870 : }
3871 :
3872 : void
3873 1403 : nsCycleCollector_forgetRuntime(PRUint32 langID)
3874 : {
3875 1403 : if (sCollector)
3876 0 : sCollector->ForgetRuntime(langID);
3877 1403 : }
3878 :
3879 :
3880 : bool
3881 0 : NS_CycleCollectorSuspect(nsISupports *n)
3882 : {
3883 0 : if (sCollector)
3884 0 : return sCollector->Suspect(n);
3885 0 : return false;
3886 : }
3887 :
3888 : bool
3889 0 : NS_CycleCollectorForget(nsISupports *n)
3890 : {
3891 0 : return sCollector ? sCollector->Forget(n) : true;
3892 : }
3893 :
3894 : nsPurpleBufferEntry*
3895 1489498 : NS_CycleCollectorSuspect2(nsISupports *n)
3896 : {
3897 1489498 : if (sCollector)
3898 1489498 : return sCollector->Suspect2(n);
3899 0 : return nsnull;
3900 : }
3901 :
3902 : bool
3903 1350470 : NS_CycleCollectorForget2(nsPurpleBufferEntry *e)
3904 : {
3905 1350470 : return sCollector ? sCollector->Forget2(e) : true;
3906 : }
3907 :
3908 : PRUint32
3909 23251 : nsCycleCollector_suspectedCount()
3910 : {
3911 23251 : return sCollector ? sCollector->SuspectedCount() : 0;
3912 : }
3913 :
3914 : #ifdef DEBUG
3915 : void
3916 0 : nsCycleCollector_DEBUG_shouldBeFreed(nsISupports *n)
3917 : {
3918 : #ifdef DEBUG_CC
3919 : if (sCollector)
3920 : sCollector->ShouldBeFreed(n);
3921 : #endif
3922 0 : }
3923 :
3924 : void
3925 1271 : nsCycleCollector_DEBUG_wasFreed(nsISupports *n)
3926 : {
3927 : #ifdef DEBUG_CC
3928 : if (sCollector)
3929 : sCollector->WasFreed(n);
3930 : #endif
3931 1271 : }
3932 : #endif
3933 :
3934 : class nsCycleCollectorRunner : public nsRunnable
3935 5676 : {
3936 : nsCycleCollector *mCollector;
3937 : nsICycleCollectorListener *mListener;
3938 : Mutex mLock;
3939 : CondVar mRequest;
3940 : CondVar mReply;
3941 : bool mRunning;
3942 : bool mShutdown;
3943 : bool mCollected;
3944 :
3945 57 : nsCycleCollectionJSRuntime *GetJSRuntime()
3946 : {
3947 : return static_cast<nsCycleCollectionJSRuntime*>
3948 57 : (mCollector->mRuntimes[nsIProgrammingLanguage::JAVASCRIPT]);
3949 : }
3950 :
3951 : public:
3952 1419 : NS_IMETHOD Run()
3953 : {
3954 : #ifdef XP_WIN
3955 : TlsSetValue(gTLSThreadIDIndex,
3956 : (void*) mozilla::threads::CycleCollector);
3957 : #elif defined(NS_TLS)
3958 1419 : gTLSThreadID = mozilla::threads::CycleCollector;
3959 : #else
3960 : gCycleCollectorThread = PR_GetCurrentThread();
3961 : #endif
3962 :
3963 1419 : NS_ASSERTION(NS_IsCycleCollectorThread() && !NS_IsMainThread(),
3964 : "Wrong thread!");
3965 :
3966 2838 : MutexAutoLock autoLock(mLock);
3967 :
3968 1419 : if (mShutdown)
3969 0 : return NS_OK;
3970 :
3971 1419 : mRunning = true;
3972 :
3973 3 : while (1) {
3974 1422 : mRequest.Wait();
3975 :
3976 1422 : if (!mRunning) {
3977 1419 : mReply.Notify();
3978 1419 : return NS_OK;
3979 : }
3980 :
3981 3 : GetJSRuntime()->NotifyEnterCycleCollectionThread();
3982 3 : mCollected = mCollector->BeginCollection(mListener);
3983 3 : GetJSRuntime()->NotifyLeaveCycleCollectionThread();
3984 :
3985 3 : mReply.Notify();
3986 : }
3987 :
3988 : return NS_OK;
3989 : }
3990 :
3991 1419 : nsCycleCollectorRunner(nsCycleCollector *collector)
3992 : : mCollector(collector),
3993 : mListener(nsnull),
3994 : mLock("cycle collector lock"),
3995 : mRequest(mLock, "cycle collector request condvar"),
3996 : mReply(mLock, "cycle collector reply condvar"),
3997 : mRunning(false),
3998 : mShutdown(false),
3999 1419 : mCollected(false)
4000 : {
4001 1419 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4002 1419 : }
4003 :
4004 48 : void Collect(nsCycleCollectorResults *aResults,
4005 : nsICycleCollectorListener *aListener)
4006 : {
4007 48 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4008 :
4009 : // On a WantAllTraces CC, force a synchronous global GC to prevent
4010 : // hijinks from ForgetSkippable and compartmental GCs.
4011 48 : bool wantAllTraces = false;
4012 48 : if (aListener) {
4013 0 : aListener->GetWantAllTraces(&wantAllTraces);
4014 : }
4015 48 : mCollector->GCIfNeeded(wantAllTraces);
4016 :
4017 96 : MutexAutoLock autoLock(mLock);
4018 :
4019 48 : if (!mRunning)
4020 : return;
4021 :
4022 96 : nsAutoTArray<PtrInfo*, 4000> whiteNodes;
4023 48 : if (!mCollector->PrepareForCollection(aResults, &whiteNodes))
4024 : return;
4025 :
4026 48 : NS_ASSERTION(!mListener, "Should have cleared this already!");
4027 48 : if (aListener && NS_FAILED(aListener->Begin()))
4028 0 : aListener = nsnull;
4029 48 : mListener = aListener;
4030 :
4031 48 : if (GetJSRuntime()->NotifyLeaveMainThread()) {
4032 3 : mRequest.Notify();
4033 3 : mReply.Wait();
4034 3 : GetJSRuntime()->NotifyEnterMainThread();
4035 : } else {
4036 45 : mCollected = mCollector->BeginCollection(mListener);
4037 : }
4038 :
4039 48 : mListener = nsnull;
4040 :
4041 48 : if (mCollected) {
4042 48 : mCollector->FinishCollection(aListener);
4043 48 : mCollector->CleanupAfterCollection();
4044 : }
4045 : }
4046 :
4047 1419 : void Shutdown()
4048 : {
4049 1419 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4050 :
4051 2838 : MutexAutoLock autoLock(mLock);
4052 :
4053 1419 : mShutdown = true;
4054 :
4055 1419 : if (!mRunning)
4056 : return;
4057 :
4058 1419 : mRunning = false;
4059 1419 : mRequest.Notify();
4060 1419 : mReply.Wait();
4061 : }
4062 : };
4063 :
4064 : // Holds a reference.
4065 : static nsCycleCollectorRunner* sCollectorRunner;
4066 :
4067 : // Holds a reference.
4068 : static nsIThread* sCollectorThread;
4069 :
4070 : nsresult
4071 1419 : nsCycleCollector_startup()
4072 : {
4073 1419 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4074 1419 : NS_ASSERTION(!sCollector, "Forgot to call nsCycleCollector_shutdown?");
4075 :
4076 1419 : sCollector = new nsCycleCollector();
4077 :
4078 : nsRefPtr<nsCycleCollectorRunner> runner =
4079 2838 : new nsCycleCollectorRunner(sCollector);
4080 :
4081 2838 : nsCOMPtr<nsIThread> thread;
4082 1419 : nsresult rv = NS_NewThread(getter_AddRefs(thread), runner);
4083 1419 : NS_ENSURE_SUCCESS(rv, rv);
4084 :
4085 1419 : runner.swap(sCollectorRunner);
4086 1419 : thread.swap(sCollectorThread);
4087 :
4088 1419 : return rv;
4089 : }
4090 :
4091 : void
4092 1404 : nsCycleCollector_setBeforeUnlinkCallback(CC_BeforeUnlinkCallback aCB)
4093 : {
4094 1404 : if (sCollector) {
4095 1404 : sCollector->mBeforeUnlinkCB = aCB;
4096 : }
4097 1404 : }
4098 :
4099 : void
4100 1404 : nsCycleCollector_setForgetSkippableCallback(CC_ForgetSkippableCallback aCB)
4101 : {
4102 1404 : if (sCollector) {
4103 1404 : sCollector->mForgetSkippableCB = aCB;
4104 : }
4105 1404 : }
4106 :
4107 : void
4108 276 : nsCycleCollector_forgetSkippable(bool aRemoveChildlessNodes)
4109 : {
4110 276 : if (sCollector) {
4111 552 : SAMPLE_LABEL("CC", "nsCycleCollector_forgetSkippable");
4112 276 : TimeLog timeLog;
4113 276 : sCollector->ForgetSkippable(aRemoveChildlessNodes);
4114 276 : timeLog.Checkpoint("ForgetSkippable()");
4115 : }
4116 276 : }
4117 :
4118 : void
4119 48 : nsCycleCollector_collect(nsCycleCollectorResults *aResults,
4120 : nsICycleCollectorListener *aListener)
4121 : {
4122 48 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4123 96 : SAMPLE_LABEL("CC", "nsCycleCollector_collect");
4124 96 : nsCOMPtr<nsICycleCollectorListener> listener(aListener);
4125 48 : if (!aListener && sCollector && sCollector->mParams.mLogGraphs) {
4126 0 : listener = new nsCycleCollectorLogger();
4127 : }
4128 :
4129 48 : if (sCollectorRunner) {
4130 48 : sCollectorRunner->Collect(aResults, listener);
4131 0 : } else if (sCollector) {
4132 0 : sCollector->Collect(aResults, 1, listener);
4133 : }
4134 48 : }
4135 :
4136 : void
4137 1419 : nsCycleCollector_shutdownThreads()
4138 : {
4139 1419 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4140 1419 : if (sCollectorRunner) {
4141 2838 : nsRefPtr<nsCycleCollectorRunner> runner;
4142 1419 : runner.swap(sCollectorRunner);
4143 1419 : runner->Shutdown();
4144 : }
4145 :
4146 1419 : if (sCollectorThread) {
4147 2838 : nsCOMPtr<nsIThread> thread;
4148 1419 : thread.swap(sCollectorThread);
4149 1419 : thread->Shutdown();
4150 : }
4151 1419 : }
4152 :
4153 : void
4154 1419 : nsCycleCollector_shutdown()
4155 : {
4156 1419 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
4157 1419 : NS_ASSERTION(!sCollectorRunner, "Should have finished before!");
4158 1419 : NS_ASSERTION(!sCollectorThread, "Should have finished before!");
4159 :
4160 1419 : if (sCollector) {
4161 2838 : SAMPLE_LABEL("CC", "nsCycleCollector_shutdown");
4162 1419 : sCollector->Shutdown();
4163 1419 : delete sCollector;
4164 1419 : sCollector = nsnull;
4165 : }
4166 1419 : }
|