Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/js/src/gc/GCMarker.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 * vim: set ts=8 sts=4 et sw=4 tw=99:
3
 * This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef gc_GCMarker_h
8
#define gc_GCMarker_h
9
10
#include "ds/OrderedHashTable.h"
11
#include "js/SliceBudget.h"
12
#include "js/TracingAPI.h"
13
#include "js/TypeDecls.h"
14
15
namespace js {
16
17
class AutoAccessAtomsZone;
18
class WeakMapBase;
19
20
static const size_t NON_INCREMENTAL_MARK_STACK_BASE_CAPACITY = 4096;
21
static const size_t INCREMENTAL_MARK_STACK_BASE_CAPACITY = 32768;
22
23
namespace gc {
24
25
struct Cell;
26
27
struct WeakKeyTableHashPolicy {
28
    typedef JS::GCCellPtr Lookup;
29
0
    static HashNumber hash(const Lookup& v, const mozilla::HashCodeScrambler&) {
30
0
        return mozilla::HashGeneric(v.asCell());
31
0
    }
32
0
    static bool match(const JS::GCCellPtr& k, const Lookup& l) { return k == l; }
33
0
    static bool isEmpty(const JS::GCCellPtr& v) { return !v; }
34
0
    static void makeEmpty(JS::GCCellPtr* vp) { *vp = nullptr; }
35
};
36
37
struct WeakMarkable {
38
    WeakMapBase* weakmap;
39
    JS::GCCellPtr key;
40
41
    WeakMarkable(WeakMapBase* weakmapArg, JS::GCCellPtr keyArg)
42
0
      : weakmap(weakmapArg), key(keyArg) {}
43
};
44
45
using WeakEntryVector = Vector<WeakMarkable, 2, js::SystemAllocPolicy>;
46
47
using WeakKeyTable = OrderedHashMap<JS::GCCellPtr,
48
                                    WeakEntryVector,
49
                                    WeakKeyTableHashPolicy,
50
                                    js::SystemAllocPolicy>;
51
52
/*
53
 * When the native stack is low, the GC does not call js::TraceChildren to mark
54
 * the reachable "children" of the thing. Rather the thing is put aside and
55
 * js::TraceChildren is called later with more space on the C stack.
56
 *
57
 * To implement such delayed marking of the children with minimal overhead for
58
 * the normal case of sufficient native stack, the code adds a field per arena.
59
 * The field markingDelay->link links all arenas with delayed things into a
60
 * stack list with the pointer to stack top in GCMarker::unmarkedArenaStackTop.
61
 * GCMarker::delayMarkingChildren adds arenas to the stack as necessary while
62
 * markDelayedChildren pops the arenas from the stack until it empties.
63
 */
64
class MarkStack
65
{
66
  public:
67
    /*
68
     * We use a common mark stack to mark GC things of different types and use
69
     * the explicit tags to distinguish them when it cannot be deduced from
70
     * the context of push or pop operation.
71
     */
72
    enum Tag {
73
        ValueArrayTag,
74
        ObjectTag,
75
        GroupTag,
76
        SavedValueArrayTag,
77
        JitCodeTag,
78
        ScriptTag,
79
        TempRopeTag,
80
81
        LastTag = TempRopeTag
82
    };
83
84
    static const uintptr_t TagMask = 7;
85
    static_assert(TagMask >= uintptr_t(LastTag), "The tag mask must subsume the tags.");
86
    static_assert(TagMask <= gc::CellAlignMask, "The tag mask must be embeddable in a Cell*.");
87
88
    class TaggedPtr
89
    {
90
        uintptr_t bits;
91
92
        Cell* ptr() const;
93
94
      public:
95
12.2k
        TaggedPtr() {}
96
        TaggedPtr(Tag tag, Cell* ptr);
97
        Tag tag() const;
98
        template <typename T> T* as() const;
99
100
        JSObject* asValueArrayObject() const;
101
        JSObject* asSavedValueArrayObject() const;
102
        JSRope* asTempRope() const;
103
104
        void assertValid() const;
105
    };
106
107
    struct ValueArray
108
    {
109
        ValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end);
110
        void assertValid() const;
111
112
        HeapSlot* end;
113
        HeapSlot* start;
114
        TaggedPtr ptr;
115
    };
116
117
    struct SavedValueArray
118
    {
119
        SavedValueArray(JSObject* obj, size_t index, HeapSlot::Kind kind);
120
        void assertValid() const;
121
122
        uintptr_t kind;
123
        uintptr_t index;
124
        TaggedPtr ptr;
125
    };
126
127
    explicit MarkStack(size_t maxCapacity = DefaultCapacity);
128
    ~MarkStack();
129
130
    static const size_t DefaultCapacity = SIZE_MAX;
131
132
15.4k
    size_t capacity() { return stack().length(); }
133
134
18
    size_t position() const {
135
18
        return topIndex_;
136
18
    }
137
138
    MOZ_MUST_USE bool init(JSGCMode gcMode);
139
140
    MOZ_MUST_USE bool setCapacityForMode(JSGCMode mode);
141
142
0
    size_t maxCapacity() const { return maxCapacity_; }
143
    void setMaxCapacity(size_t maxCapacity);
144
145
    template <typename T>
146
    MOZ_MUST_USE bool push(T* ptr);
147
148
    MOZ_MUST_USE bool push(JSObject* obj, HeapSlot* start, HeapSlot* end);
149
    MOZ_MUST_USE bool push(const ValueArray& array);
150
    MOZ_MUST_USE bool push(const SavedValueArray& array);
151
152
    // GCMarker::eagerlyMarkChildren uses unused marking stack as temporary
153
    // storage to hold rope pointers.
154
    MOZ_MUST_USE bool pushTempRope(JSRope* ptr);
155
156
15.6k
    bool isEmpty() const {
157
15.6k
        return topIndex_ == 0;
158
15.6k
    }
159
160
    Tag peekTag() const;
161
    TaggedPtr popPtr();
162
    ValueArray popValueArray();
163
    SavedValueArray popSavedValueArray();
164
165
18
    void clear() {
166
18
        topIndex_ = 0;
167
18
    }
168
169
    void setGCMode(JSGCMode gcMode);
170
171
    void poisonUnused();
172
173
    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
174
175
  private:
176
    using StackVector = Vector<TaggedPtr, 0, SystemAllocPolicy>;
177
25.2k
    const StackVector& stack() const { return stack_.ref(); }
178
47.1k
    StackVector& stack() { return stack_.ref(); }
179
180
    MOZ_MUST_USE bool ensureSpace(size_t count);
181
182
    /* Grow the stack, ensuring there is space for at least count elements. */
183
    MOZ_MUST_USE bool enlarge(size_t count);
184
185
    MOZ_MUST_USE bool resize(size_t newCapacity);
186
187
    TaggedPtr* topPtr();
188
189
    const TaggedPtr& peekPtr() const;
190
    MOZ_MUST_USE bool pushTaggedPtr(Tag tag, Cell* ptr);
191
192
    // Index of the top of the stack.
193
    MainThreadData<size_t> topIndex_;
194
195
    // The maximum stack capacity to grow to.
196
    MainThreadData<size_t> maxCapacity_;
197
198
    // Vector containing allocated stack memory. Unused beyond topIndex_.
199
    MainThreadData<StackVector> stack_;
200
201
#ifdef DEBUG
202
    mutable size_t iteratorCount_;
203
#endif
204
205
    friend class MarkStackIter;
206
};
207
208
class MarkStackIter
209
{
210
    MarkStack& stack_;
211
    size_t pos_;
212
213
  public:
214
    explicit MarkStackIter(MarkStack& stack);
215
    ~MarkStackIter();
216
217
    bool done() const;
218
    MarkStack::Tag peekTag() const;
219
    MarkStack::TaggedPtr peekPtr() const;
220
    MarkStack::ValueArray peekValueArray() const;
221
    void next();
222
    void nextPtr();
223
    void nextArray();
224
225
    // Mutate the current ValueArray to a SavedValueArray.
226
    void saveValueArray(const MarkStack::SavedValueArray& savedArray);
227
228
  private:
229
    size_t position() const;
230
};
231
232
} /* namespace gc */
233
234
class GCMarker : public JSTracer
235
{
236
  public:
237
    explicit GCMarker(JSRuntime* rt);
238
    MOZ_MUST_USE bool init(JSGCMode gcMode);
239
240
0
    void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); }
241
0
    size_t maxCapacity() const { return stack.maxCapacity(); }
242
243
    void start();
244
    void stop();
245
    void reset();
246
247
    // Mark the given GC thing and traverse its children at some point.
248
    template <typename T> void traverse(T thing);
249
250
    // Calls traverse on target after making additional assertions.
251
    template <typename S, typename T> void traverseEdge(S source, T* target);
252
    template <typename S, typename T> void traverseEdge(S source, const T& target);
253
254
    // Helper methods that coerce their second argument to the base pointer
255
    // type.
256
1.31k
    template <typename S> void traverseObjectEdge(S source, JSObject* target) {
257
1.31k
        traverseEdge(source, target);
258
1.31k
    }
259
4.87k
    template <typename S> void traverseStringEdge(S source, JSString* target) {
260
4.87k
        traverseEdge(source, target);
261
4.87k
    }
262
263
    // Notes a weak graph edge for later sweeping.
264
    template <typename T> void noteWeakEdge(T* edge);
265
266
    /*
267
     * Care must be taken changing the mark color from gray to black. The cycle
268
     * collector depends on the invariant that there are no black to gray edges
269
     * in the GC heap. This invariant lets the CC not trace through black
270
     * objects. If this invariant is violated, the cycle collector may free
271
     * objects that are still reachable.
272
     */
273
18
    void setMarkColorGray() {
274
18
        MOZ_ASSERT(isDrained());
275
18
        MOZ_ASSERT(color == gc::MarkColor::Black);
276
18
        color = gc::MarkColor::Gray;
277
18
    }
278
18
    void setMarkColorBlack() {
279
18
        MOZ_ASSERT(isDrained());
280
18
        MOZ_ASSERT(color == gc::MarkColor::Gray);
281
18
        color = gc::MarkColor::Black;
282
18
    }
283
3.49M
    gc::MarkColor markColor() const { return color; }
284
285
    void enterWeakMarkingMode();
286
    void leaveWeakMarkingMode();
287
0
    void abortLinearWeakMarking() {
288
0
        leaveWeakMarkingMode();
289
0
        linearWeakMarkingDisabled_ = true;
290
0
    }
291
292
    void delayMarkingArena(gc::Arena* arena);
293
    void delayMarkingChildren(const void* thing);
294
    void markDelayedChildren(gc::Arena* arena);
295
    MOZ_MUST_USE bool markDelayedChildren(SliceBudget& budget);
296
172
    bool hasDelayedChildren() const {
297
172
        return !!unmarkedArenaStackTop;
298
172
    }
299
300
0
    bool isDrained() {
301
0
        return isMarkStackEmpty() && !unmarkedArenaStackTop;
302
0
    }
303
304
    MOZ_MUST_USE bool drainMarkStack(SliceBudget& budget);
305
306
3
    void setGCMode(JSGCMode mode) { stack.setGCMode(mode); }
307
308
    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
309
310
#ifdef DEBUG
311
312
    bool shouldCheckCompartments() { return strictCompartmentChecking; }
313
314
    JS::Zone* stackContainsCrossZonePointerTo(const gc::Cell* cell);
315
316
#endif
317
318
    void markEphemeronValues(gc::Cell* markedCell, gc::WeakEntryVector& entry);
319
320
3.44M
    static GCMarker* fromTracer(JSTracer* trc) {
321
3.44M
        MOZ_ASSERT(trc->isMarkingTracer());
322
3.44M
        return static_cast<GCMarker*>(trc);
323
3.44M
    }
324
325
  private:
326
#ifdef DEBUG
327
    void checkZone(void* p);
328
#else
329
17.5k
    void checkZone(void* p) {}
330
#endif
331
332
    // Push an object onto the stack for later tracing and assert that it has
333
    // already been marked.
334
    inline void repush(JSObject* obj);
335
336
    template <typename T> void markAndTraceChildren(T* thing);
337
    template <typename T> void markAndPush(T* thing);
338
    template <typename T> void markAndScan(T* thing);
339
    template <typename T> void markImplicitEdgesHelper(T oldThing);
340
    template <typename T> void markImplicitEdges(T* oldThing);
341
    void eagerlyMarkChildren(JSLinearString* str);
342
    void eagerlyMarkChildren(JSRope* rope);
343
    void eagerlyMarkChildren(JSString* str);
344
    void eagerlyMarkChildren(LazyScript *thing);
345
    void eagerlyMarkChildren(Shape* shape);
346
    void eagerlyMarkChildren(Scope* scope);
347
    void lazilyMarkChildren(ObjectGroup* group);
348
349
    // We may not have concrete types yet, so this has to be outside the header.
350
    template <typename T>
351
    void dispatchToTraceChildren(T* thing);
352
353
    // Mark the given GC thing, but do not trace its children. Return true
354
    // if the thing became marked.
355
    template <typename T>
356
    MOZ_MUST_USE bool mark(T* thing);
357
358
    template <typename T>
359
    inline void pushTaggedPtr(T* ptr);
360
361
    inline void pushValueArray(JSObject* obj, HeapSlot* start, HeapSlot* end);
362
363
0
    bool isMarkStackEmpty() {
364
0
        return stack.isEmpty();
365
0
    }
366
367
    MOZ_MUST_USE bool restoreValueArray(const gc::MarkStack::SavedValueArray& array,
368
                                        HeapSlot** vpp, HeapSlot** endp);
369
    gc::MarkStack::ValueArray restoreValueArray(const gc::MarkStack::SavedValueArray& savedArray);
370
371
    void saveValueRanges();
372
    gc::MarkStack::SavedValueArray saveValueRange(const gc::MarkStack::ValueArray& array);
373
374
    inline void processMarkStackTop(SliceBudget& budget);
375
376
    /* The mark stack. Pointers in this stack are "gray" in the GC sense. */
377
    gc::MarkStack stack;
378
379
    /* The color is only applied to objects and functions. */
380
    MainThreadData<gc::MarkColor> color;
381
382
    /* Pointer to the top of the stack of arenas we are delaying marking on. */
383
    MainThreadData<js::gc::Arena*> unmarkedArenaStackTop;
384
385
    /*
386
     * If the weakKeys table OOMs, disable the linear algorithm and fall back
387
     * to iterating until the next GC.
388
     */
389
    MainThreadData<bool> linearWeakMarkingDisabled_;
390
391
#ifdef DEBUG
392
    /* Count of arenas that are currently in the stack. */
393
    MainThreadData<size_t> markLaterArenas;
394
395
    /* Assert that start and stop are called with correct ordering. */
396
    MainThreadData<bool> started;
397
398
    /*
399
     * If this is true, all marked objects must belong to a compartment being
400
     * GCed. This is used to look for compartment bugs.
401
     */
402
    MainThreadData<bool> strictCompartmentChecking;
403
#endif // DEBUG
404
};
405
406
} /* namespace js */
407
408
// Exported for Tracer.cpp
409
10.5M
inline bool ThingIsPermanentAtomOrWellKnownSymbol(js::gc::Cell* thing) { return false; }
410
bool ThingIsPermanentAtomOrWellKnownSymbol(JSString*);
411
bool ThingIsPermanentAtomOrWellKnownSymbol(JSFlatString*);
412
bool ThingIsPermanentAtomOrWellKnownSymbol(JSLinearString*);
413
bool ThingIsPermanentAtomOrWellKnownSymbol(JSAtom*);
414
bool ThingIsPermanentAtomOrWellKnownSymbol(js::PropertyName*);
415
bool ThingIsPermanentAtomOrWellKnownSymbol(JS::Symbol*);
416
417
#endif /* gc_GCMarker_h */