|
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 #include "gc/Zone.h" |
|
8 |
|
9 #include "jsgc.h" |
|
10 |
|
11 #ifdef JS_ION |
|
12 #include "jit/BaselineJIT.h" |
|
13 #include "jit/Ion.h" |
|
14 #include "jit/JitCompartment.h" |
|
15 #endif |
|
16 #include "vm/Debugger.h" |
|
17 #include "vm/Runtime.h" |
|
18 |
|
19 #include "jsgcinlines.h" |
|
20 |
|
21 using namespace js; |
|
22 using namespace js::gc; |
|
23 |
|
24 JS::Zone::Zone(JSRuntime *rt) |
|
25 : JS::shadow::Zone(rt, &rt->gcMarker), |
|
26 allocator(this), |
|
27 ionUsingBarriers_(false), |
|
28 active(false), |
|
29 gcScheduled(false), |
|
30 gcState(NoGC), |
|
31 gcPreserveCode(false), |
|
32 gcBytes(0), |
|
33 gcTriggerBytes(0), |
|
34 gcHeapGrowthFactor(3.0), |
|
35 isSystem(false), |
|
36 usedByExclusiveThread(false), |
|
37 scheduledForDestruction(false), |
|
38 maybeAlive(true), |
|
39 gcMallocBytes(0), |
|
40 gcMallocGCTriggered(false), |
|
41 gcGrayRoots(), |
|
42 data(nullptr), |
|
43 types(this) |
|
44 #ifdef JS_ION |
|
45 , jitZone_(nullptr) |
|
46 #endif |
|
47 { |
|
48 /* Ensure that there are no vtables to mess us up here. */ |
|
49 JS_ASSERT(reinterpret_cast<JS::shadow::Zone *>(this) == |
|
50 static_cast<JS::shadow::Zone *>(this)); |
|
51 |
|
52 setGCMaxMallocBytes(rt->gcMaxMallocBytes * 0.9); |
|
53 } |
|
54 |
|
55 Zone::~Zone() |
|
56 { |
|
57 if (this == runtimeFromMainThread()->systemZone) |
|
58 runtimeFromMainThread()->systemZone = nullptr; |
|
59 |
|
60 #ifdef JS_ION |
|
61 js_delete(jitZone_); |
|
62 #endif |
|
63 } |
|
64 |
|
65 void |
|
66 Zone::setNeedsBarrier(bool needs, ShouldUpdateIon updateIon) |
|
67 { |
|
68 #ifdef JS_ION |
|
69 if (updateIon == UpdateIon && needs != ionUsingBarriers_) { |
|
70 jit::ToggleBarriers(this, needs); |
|
71 ionUsingBarriers_ = needs; |
|
72 } |
|
73 #endif |
|
74 |
|
75 if (needs && runtimeFromMainThread()->isAtomsZone(this)) |
|
76 JS_ASSERT(!runtimeFromMainThread()->exclusiveThreadsPresent()); |
|
77 |
|
78 JS_ASSERT_IF(needs, canCollect()); |
|
79 needsBarrier_ = needs; |
|
80 } |
|
81 |
|
82 void |
|
83 Zone::resetGCMallocBytes() |
|
84 { |
|
85 gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); |
|
86 gcMallocGCTriggered = false; |
|
87 } |
|
88 |
|
89 void |
|
90 Zone::setGCMaxMallocBytes(size_t value) |
|
91 { |
|
92 /* |
|
93 * For compatibility treat any value that exceeds PTRDIFF_T_MAX to |
|
94 * mean that value. |
|
95 */ |
|
96 gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1; |
|
97 resetGCMallocBytes(); |
|
98 } |
|
99 |
|
100 void |
|
101 Zone::onTooMuchMalloc() |
|
102 { |
|
103 if (!gcMallocGCTriggered) |
|
104 gcMallocGCTriggered = TriggerZoneGC(this, JS::gcreason::TOO_MUCH_MALLOC); |
|
105 } |
|
106 |
|
107 void |
|
108 Zone::sweep(FreeOp *fop, bool releaseTypes, bool *oom) |
|
109 { |
|
110 /* |
|
111 * Periodically release observed types for all scripts. This is safe to |
|
112 * do when there are no frames for the zone on the stack. |
|
113 */ |
|
114 if (active) |
|
115 releaseTypes = false; |
|
116 |
|
117 { |
|
118 gcstats::AutoPhase ap(fop->runtime()->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); |
|
119 types.sweep(fop, releaseTypes, oom); |
|
120 } |
|
121 |
|
122 if (!fop->runtime()->debuggerList.isEmpty()) |
|
123 sweepBreakpoints(fop); |
|
124 |
|
125 active = false; |
|
126 } |
|
127 |
|
128 void |
|
129 Zone::sweepBreakpoints(FreeOp *fop) |
|
130 { |
|
131 /* |
|
132 * Sweep all compartments in a zone at the same time, since there is no way |
|
133 * to iterate over the scripts belonging to a single compartment in a zone. |
|
134 */ |
|
135 |
|
136 gcstats::AutoPhase ap1(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_TABLES); |
|
137 gcstats::AutoPhase ap2(fop->runtime()->gcStats, gcstats::PHASE_SWEEP_TABLES_BREAKPOINT); |
|
138 |
|
139 JS_ASSERT(isGCSweeping()); |
|
140 for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { |
|
141 JSScript *script = i.get<JSScript>(); |
|
142 JS_ASSERT(script->zone()->isGCSweeping()); |
|
143 if (!script->hasAnyBreakpointsOrStepMode()) |
|
144 continue; |
|
145 |
|
146 bool scriptGone = IsScriptAboutToBeFinalized(&script); |
|
147 JS_ASSERT(script == i.get<JSScript>()); |
|
148 for (unsigned i = 0; i < script->length(); i++) { |
|
149 BreakpointSite *site = script->getBreakpointSite(script->offsetToPC(i)); |
|
150 if (!site) |
|
151 continue; |
|
152 |
|
153 Breakpoint *nextbp; |
|
154 for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) { |
|
155 nextbp = bp->nextInSite(); |
|
156 HeapPtrObject& dbgobj = bp->debugger->toJSObjectRef(); |
|
157 JS_ASSERT(dbgobj->zone()->isGCSweeping()); |
|
158 bool dying = scriptGone || IsObjectAboutToBeFinalized(&dbgobj); |
|
159 JS_ASSERT_IF(!dying, bp->getHandler()->isMarked()); |
|
160 if (dying) |
|
161 bp->destroy(fop); |
|
162 } |
|
163 } |
|
164 } |
|
165 } |
|
166 |
|
167 void |
|
168 Zone::discardJitCode(FreeOp *fop) |
|
169 { |
|
170 #ifdef JS_ION |
|
171 if (!jitZone()) |
|
172 return; |
|
173 |
|
174 if (isPreservingCode()) { |
|
175 PurgeJITCaches(this); |
|
176 } else { |
|
177 |
|
178 # ifdef DEBUG |
|
179 /* Assert no baseline scripts are marked as active. */ |
|
180 for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { |
|
181 JSScript *script = i.get<JSScript>(); |
|
182 JS_ASSERT_IF(script->hasBaselineScript(), !script->baselineScript()->active()); |
|
183 } |
|
184 # endif |
|
185 |
|
186 /* Mark baseline scripts on the stack as active. */ |
|
187 jit::MarkActiveBaselineScripts(this); |
|
188 |
|
189 /* Only mark OSI points if code is being discarded. */ |
|
190 jit::InvalidateAll(fop, this); |
|
191 |
|
192 for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { |
|
193 JSScript *script = i.get<JSScript>(); |
|
194 jit::FinishInvalidation<SequentialExecution>(fop, script); |
|
195 |
|
196 // Preserve JIT code that have been recently used in |
|
197 // parallel. Note that we mark their baseline scripts as active as |
|
198 // well to preserve them. |
|
199 if (script->hasParallelIonScript()) { |
|
200 if (jit::ShouldPreserveParallelJITCode(runtimeFromMainThread(), script)) { |
|
201 script->parallelIonScript()->purgeCaches(); |
|
202 script->baselineScript()->setActive(); |
|
203 } else { |
|
204 jit::FinishInvalidation<ParallelExecution>(fop, script); |
|
205 } |
|
206 } |
|
207 |
|
208 /* |
|
209 * Discard baseline script if it's not marked as active. Note that |
|
210 * this also resets the active flag. |
|
211 */ |
|
212 jit::FinishDiscardBaselineScript(fop, script); |
|
213 |
|
214 /* |
|
215 * Use counts for scripts are reset on GC. After discarding code we |
|
216 * need to let it warm back up to get information such as which |
|
217 * opcodes are setting array holes or accessing getter properties. |
|
218 */ |
|
219 script->resetUseCount(); |
|
220 } |
|
221 |
|
222 jitZone()->optimizedStubSpace()->free(); |
|
223 } |
|
224 #endif |
|
225 } |
|
226 |
|
227 uint64_t |
|
228 Zone::gcNumber() |
|
229 { |
|
230 // Zones in use by exclusive threads are not collected, and threads using |
|
231 // them cannot access the main runtime's gcNumber without racing. |
|
232 return usedByExclusiveThread ? 0 : runtimeFromMainThread()->gcNumber; |
|
233 } |
|
234 |
|
235 #ifdef JS_ION |
|
236 js::jit::JitZone * |
|
237 Zone::createJitZone(JSContext *cx) |
|
238 { |
|
239 MOZ_ASSERT(!jitZone_); |
|
240 |
|
241 if (!cx->runtime()->getJitRuntime(cx)) |
|
242 return nullptr; |
|
243 |
|
244 jitZone_ = cx->new_<js::jit::JitZone>(); |
|
245 return jitZone_; |
|
246 } |
|
247 #endif |
|
248 |
|
249 JS::Zone * |
|
250 js::ZoneOfObject(const JSObject &obj) |
|
251 { |
|
252 return obj.zone(); |
|
253 } |
|
254 |
|
255 JS::Zone * |
|
256 js::ZoneOfObjectFromAnyThread(const JSObject &obj) |
|
257 { |
|
258 return obj.zoneFromAnyThread(); |
|
259 } |
|
260 |
|
261 bool |
|
262 Zone::hasMarkedCompartments() |
|
263 { |
|
264 for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) { |
|
265 if (comp->marked) |
|
266 return true; |
|
267 } |
|
268 return false; |
|
269 } |