1 | /* |
2 | * Copyright (C) 2012-2016 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #pragma once |
27 | |
28 | #include <wtf/Atomics.h> |
29 | #include <wtf/FastMalloc.h> |
30 | #include <wtf/Noncopyable.h> |
31 | #include <wtf/Nonmovable.h> |
32 | #include <wtf/PrintStream.h> |
33 | #include <wtf/ScopedLambda.h> |
34 | #include <wtf/SentinelLinkedList.h> |
35 | #include <wtf/ThreadSafeRefCounted.h> |
36 | |
37 | namespace JSC { |
38 | |
39 | class VM; |
40 | |
41 | class FireDetail { |
42 | void* operator new(size_t) = delete; |
43 | |
44 | public: |
45 | FireDetail() |
46 | { |
47 | } |
48 | |
49 | virtual ~FireDetail() |
50 | { |
51 | } |
52 | |
53 | virtual void dump(PrintStream&) const = 0; |
54 | }; |
55 | |
56 | class StringFireDetail : public FireDetail { |
57 | public: |
58 | StringFireDetail(const char* string) |
59 | : m_string(string) |
60 | { |
61 | } |
62 | |
63 | void dump(PrintStream& out) const override; |
64 | |
65 | private: |
66 | const char* m_string; |
67 | }; |
68 | |
69 | template<typename... Types> |
70 | class LazyFireDetail : public FireDetail { |
71 | public: |
72 | LazyFireDetail(const Types&... args) |
73 | { |
74 | m_lambda = scopedLambda<void(PrintStream&)>([&] (PrintStream& out) { |
75 | out.print(args...); |
76 | }); |
77 | } |
78 | |
79 | void dump(PrintStream& out) const override { m_lambda(out); } |
80 | |
81 | private: |
82 | ScopedLambda<void(PrintStream&)> m_lambda; |
83 | }; |
84 | |
85 | template<typename... Types> |
86 | LazyFireDetail<Types...> createLazyFireDetail(const Types&... types) |
87 | { |
88 | return LazyFireDetail<Types...>(types...); |
89 | } |
90 | |
91 | class WatchpointSet; |
92 | |
93 | // Really unfortunately, we do not have the way to dispatch appropriate destructor in base class' destructor |
94 | // based on enum type. If we call destructor explicitly in the base class, it ends up calling the base destructor |
95 | // twice. C++20 allows this by using std::std::destroying_delete_t. But we are not using C++20 right now. |
96 | // |
97 | // Because we cannot dispatch destructors of derived classes in the destructor of the base class, what it means is, |
98 | // 1. Calling Watchpoint::~Watchpoint directly is illegal. |
99 | // 2. `delete watchpoint` where watchpoint is non-final derived class is illegal. If watchpoint is final derived class, it works. |
100 | // 3. If we really want to do (2), we need to call `watchpoint->destroy()` instead, and dispatch an appropriate destructor in Watchpoint::destroy. |
101 | // |
102 | // Luckily, none of our derived watchpoint classes have members which require destructors. So we do not dispatch |
103 | // the destructor call to the drived class in the base class. If it becomes really required, we can introduce |
104 | // a custom deleter for some classes which directly call "delete" to the allocated non-final Watchpoint class |
105 | // (e.g. std::unique_ptr<Watchpoint>, RefPtr<Watchpoint>), and call Watchpoint::destroy instead of "delete" |
106 | // operator. But since we do not require it for now, we are doing the simplest thing. |
107 | #define JSC_WATCHPOINT_TYPES_WITHOUT_JIT(macro) \ |
108 | macro(AdaptiveInferredPropertyValueStructure, AdaptiveInferredPropertyValueWatchpointBase::StructureWatchpoint) \ |
109 | macro(AdaptiveInferredPropertyValueProperty, AdaptiveInferredPropertyValueWatchpointBase::PropertyWatchpoint) \ |
110 | macro(CodeBlockJettisoning, CodeBlockJettisoningWatchpoint) \ |
111 | macro(LLIntPrototypeLoadAdaptiveStructure, LLIntPrototypeLoadAdaptiveStructureWatchpoint) \ |
112 | macro(FunctionRareDataAllocationProfileClearing, FunctionRareData::AllocationProfileClearingWatchpoint) \ |
113 | macro(ObjectToStringAdaptiveStructure, ObjectToStringAdaptiveStructureWatchpoint) |
114 | |
115 | #if ENABLE(JIT) |
116 | #define JSC_WATCHPOINT_TYPES_WITHOUT_DFG(macro) \ |
117 | JSC_WATCHPOINT_TYPES_WITHOUT_JIT(macro) \ |
118 | macro(StructureStubClearing, StructureStubClearingWatchpoint) |
119 | |
120 | #if ENABLE(DFG_JIT) |
121 | #define JSC_WATCHPOINT_TYPES(macro) \ |
122 | JSC_WATCHPOINT_TYPES_WITHOUT_DFG(macro) \ |
123 | macro(AdaptiveStructure, DFG::AdaptiveStructureWatchpoint) |
124 | #else |
125 | #define JSC_WATCHPOINT_TYPES(macro) \ |
126 | JSC_WATCHPOINT_TYPES_WITHOUT_DFG(macro) |
127 | #endif |
128 | |
129 | #else |
130 | #define JSC_WATCHPOINT_TYPES(macro) \ |
131 | JSC_WATCHPOINT_TYPES_WITHOUT_JIT(macro) |
132 | #endif |
133 | |
134 | #define JSC_WATCHPOINT_FIELD(type, member) \ |
135 | type member; \ |
136 | static_assert(std::is_trivially_destructible<type>::value, ""); \ |
137 | |
138 | |
139 | class Watchpoint : public PackedRawSentinelNode<Watchpoint> { |
140 | WTF_MAKE_NONCOPYABLE(Watchpoint); |
141 | WTF_MAKE_NONMOVABLE(Watchpoint); |
142 | WTF_MAKE_FAST_ALLOCATED; |
143 | public: |
144 | #define JSC_DEFINE_WATCHPOINT_TYPES(type, _) type, |
145 | enum class Type : uint8_t { |
146 | JSC_WATCHPOINT_TYPES(JSC_DEFINE_WATCHPOINT_TYPES) |
147 | }; |
148 | #undef JSC_DEFINE_WATCHPOINT_TYPES |
149 | |
150 | Watchpoint(Type type) |
151 | : m_type(type) |
152 | { } |
153 | |
154 | protected: |
155 | ~Watchpoint(); |
156 | |
157 | private: |
158 | friend class WatchpointSet; |
159 | void fire(VM&, const FireDetail&); |
160 | |
161 | Type m_type; |
162 | }; |
163 | |
164 | enum WatchpointState { |
165 | ClearWatchpoint, |
166 | IsWatched, |
167 | IsInvalidated |
168 | }; |
169 | |
170 | class InlineWatchpointSet; |
171 | class DeferredWatchpointFire; |
172 | class VM; |
173 | |
174 | class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> { |
175 | friend class LLIntOffsetsExtractor; |
176 | friend class DeferredWatchpointFire; |
177 | public: |
178 | JS_EXPORT_PRIVATE WatchpointSet(WatchpointState); |
179 | |
180 | // FIXME: In many cases, it would be amazing if this *did* fire the watchpoints. I suspect that |
181 | // this might be hard to get right, but still, it might be awesome. |
182 | JS_EXPORT_PRIVATE ~WatchpointSet(); // Note that this will not fire any of the watchpoints; if you need to know when a WatchpointSet dies then you need a separate mechanism for this. |
183 | |
184 | static Ref<WatchpointSet> create(WatchpointState state) |
185 | { |
186 | return adoptRef(*new WatchpointSet(state)); |
187 | } |
188 | |
189 | // Fast way of getting the state, which only works from the main thread. |
190 | WatchpointState stateOnJSThread() const |
191 | { |
192 | return static_cast<WatchpointState>(m_state); |
193 | } |
194 | |
195 | // It is safe to call this from another thread. It may return an old |
196 | // state. Guarantees that if *first* read the state() of the thing being |
197 | // watched and it returned IsWatched and *second* you actually read its |
198 | // value then it's safe to assume that if the state being watched changes |
199 | // then also the watchpoint state() will change to IsInvalidated. |
200 | WatchpointState state() const |
201 | { |
202 | WTF::loadLoadFence(); |
203 | WatchpointState result = static_cast<WatchpointState>(m_state); |
204 | WTF::loadLoadFence(); |
205 | return result; |
206 | } |
207 | |
208 | // It is safe to call this from another thread. It may return true |
209 | // even if the set actually had been invalidated, but that ought to happen |
210 | // only in the case of races, and should be rare. Guarantees that if you |
211 | // call this after observing something that must imply that the set is |
212 | // invalidated, then you will see this return false. This is ensured by |
213 | // issuing a load-load fence prior to querying the state. |
214 | bool isStillValid() const |
215 | { |
216 | return state() != IsInvalidated; |
217 | } |
218 | // Like isStillValid(), may be called from another thread. |
219 | bool hasBeenInvalidated() const { return !isStillValid(); } |
220 | |
221 | // As a convenience, this will ignore 0. That's because code paths in the DFG |
222 | // that create speculation watchpoints may choose to bail out if speculation |
223 | // had already been terminated. |
224 | void add(Watchpoint*); |
225 | |
226 | // Force the watchpoint set to behave as if it was being watched even if no |
227 | // watchpoints have been installed. This will result in invalidation if the |
228 | // watchpoint would have fired. That's a pretty good indication that you |
229 | // probably don't want to set watchpoints, since we typically don't want to |
230 | // set watchpoints that we believe will actually be fired. |
231 | void startWatching() |
232 | { |
233 | ASSERT(m_state != IsInvalidated); |
234 | if (m_state == IsWatched) |
235 | return; |
236 | WTF::storeStoreFence(); |
237 | m_state = IsWatched; |
238 | WTF::storeStoreFence(); |
239 | } |
240 | |
241 | template <typename T> |
242 | void fireAll(VM& vm, T& fireDetails) |
243 | { |
244 | if (LIKELY(m_state != IsWatched)) |
245 | return; |
246 | fireAllSlow(vm, fireDetails); |
247 | } |
248 | |
249 | void touch(VM& vm, const FireDetail& detail) |
250 | { |
251 | if (state() == ClearWatchpoint) |
252 | startWatching(); |
253 | else |
254 | fireAll(vm, detail); |
255 | } |
256 | |
257 | void touch(VM& vm, const char* reason) |
258 | { |
259 | touch(vm, StringFireDetail(reason)); |
260 | } |
261 | |
262 | void invalidate(VM& vm, const FireDetail& detail) |
263 | { |
264 | if (state() == IsWatched) |
265 | fireAll(vm, detail); |
266 | m_state = IsInvalidated; |
267 | } |
268 | |
269 | void invalidate(VM& vm, const char* reason) |
270 | { |
271 | invalidate(vm, StringFireDetail(reason)); |
272 | } |
273 | |
274 | bool isBeingWatched() const |
275 | { |
276 | return m_setIsNotEmpty; |
277 | } |
278 | |
279 | int8_t* addressOfState() { return &m_state; } |
280 | static ptrdiff_t offsetOfState() { return OBJECT_OFFSETOF(WatchpointSet, m_state); } |
281 | int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; } |
282 | |
283 | JS_EXPORT_PRIVATE void fireAllSlow(VM&, const FireDetail&); // Call only if you've checked isWatched. |
284 | JS_EXPORT_PRIVATE void fireAllSlow(VM&, DeferredWatchpointFire* deferredWatchpoints); // Ditto. |
285 | JS_EXPORT_PRIVATE void fireAllSlow(VM&, const char* reason); // Ditto. |
286 | |
287 | private: |
288 | void fireAllWatchpoints(VM&, const FireDetail&); |
289 | void take(WatchpointSet* other); |
290 | |
291 | friend class InlineWatchpointSet; |
292 | |
293 | int8_t m_state; |
294 | int8_t m_setIsNotEmpty; |
295 | |
296 | SentinelLinkedList<Watchpoint, PackedRawSentinelNode<Watchpoint>> m_set; |
297 | }; |
298 | |
299 | // InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which |
300 | // it is not possible to quickly query whether it is being watched in a single |
301 | // branch. There is a fairly simple tradeoff between WatchpointSet and |
302 | // InlineWatchpointSet: |
303 | // |
304 | // Do you have to emit JIT code that rapidly tests whether the watchpoint set |
305 | // is being watched? If so, use WatchpointSet. |
306 | // |
307 | // Do you need multiple parties to have pointers to the same WatchpointSet? |
308 | // If so, use WatchpointSet. |
309 | // |
310 | // Do you have to allocate a lot of watchpoint sets? If so, use |
311 | // InlineWatchpointSet unless you answered "yes" to the previous questions. |
312 | // |
313 | // InlineWatchpointSet will use just one pointer-width word of memory unless |
314 | // you actually add watchpoints to it, in which case it internally inflates |
315 | // to a pointer to a WatchpointSet, and transfers its state to the |
316 | // WatchpointSet. |
317 | |
318 | class InlineWatchpointSet { |
319 | WTF_MAKE_NONCOPYABLE(InlineWatchpointSet); |
320 | public: |
321 | InlineWatchpointSet(WatchpointState state) |
322 | : m_data(encodeState(state)) |
323 | { |
324 | } |
325 | |
326 | ~InlineWatchpointSet() |
327 | { |
328 | if (isThin()) |
329 | return; |
330 | freeFat(); |
331 | } |
332 | |
333 | // Fast way of getting the state, which only works from the main thread. |
334 | WatchpointState stateOnJSThread() const |
335 | { |
336 | uintptr_t data = m_data; |
337 | if (isFat(data)) |
338 | return fat(data)->stateOnJSThread(); |
339 | return decodeState(data); |
340 | } |
341 | |
342 | // It is safe to call this from another thread. It may return a prior state, |
343 | // but that should be fine since you should only perform actions based on the |
344 | // state if you also add a watchpoint. |
345 | WatchpointState state() const |
346 | { |
347 | WTF::loadLoadFence(); |
348 | uintptr_t data = m_data; |
349 | WTF::loadLoadFence(); |
350 | if (isFat(data)) |
351 | return fat(data)->state(); |
352 | return decodeState(data); |
353 | } |
354 | |
355 | // It is safe to call this from another thread. It may return false |
356 | // even if the set actually had been invalidated, but that ought to happen |
357 | // only in the case of races, and should be rare. |
358 | bool hasBeenInvalidated() const |
359 | { |
360 | return state() == IsInvalidated; |
361 | } |
362 | |
363 | // Like hasBeenInvalidated(), may be called from another thread. |
364 | bool isStillValid() const |
365 | { |
366 | return !hasBeenInvalidated(); |
367 | } |
368 | |
369 | void add(Watchpoint*); |
370 | |
371 | void startWatching() |
372 | { |
373 | if (isFat()) { |
374 | fat()->startWatching(); |
375 | return; |
376 | } |
377 | ASSERT(decodeState(m_data) != IsInvalidated); |
378 | m_data = encodeState(IsWatched); |
379 | } |
380 | |
381 | template <typename T> |
382 | void fireAll(VM& vm, T fireDetails) |
383 | { |
384 | if (isFat()) { |
385 | fat()->fireAll(vm, fireDetails); |
386 | return; |
387 | } |
388 | if (decodeState(m_data) == ClearWatchpoint) |
389 | return; |
390 | m_data = encodeState(IsInvalidated); |
391 | WTF::storeStoreFence(); |
392 | } |
393 | |
394 | void invalidate(VM& vm, const FireDetail& detail) |
395 | { |
396 | if (isFat()) |
397 | fat()->invalidate(vm, detail); |
398 | else |
399 | m_data = encodeState(IsInvalidated); |
400 | } |
401 | |
402 | JS_EXPORT_PRIVATE void fireAll(VM&, const char* reason); |
403 | |
404 | void touch(VM& vm, const FireDetail& detail) |
405 | { |
406 | if (isFat()) { |
407 | fat()->touch(vm, detail); |
408 | return; |
409 | } |
410 | uintptr_t data = m_data; |
411 | if (decodeState(data) == IsInvalidated) |
412 | return; |
413 | WTF::storeStoreFence(); |
414 | if (decodeState(data) == ClearWatchpoint) |
415 | m_data = encodeState(IsWatched); |
416 | else |
417 | m_data = encodeState(IsInvalidated); |
418 | WTF::storeStoreFence(); |
419 | } |
420 | |
421 | void touch(VM& vm, const char* reason) |
422 | { |
423 | touch(vm, StringFireDetail(reason)); |
424 | } |
425 | |
426 | // Note that for any watchpoint that is visible from the DFG, it would be incorrect to write code like: |
427 | // |
428 | // if (w.isBeingWatched()) |
429 | // w.fireAll() |
430 | // |
431 | // Concurrently to this, the DFG could do: |
432 | // |
433 | // if (w.isStillValid()) |
434 | // perform optimizations; |
435 | // if (!w.isStillValid()) |
436 | // retry compilation; |
437 | // |
438 | // Note that the DFG algorithm is widespread, and sound, because fireAll() and invalidate() will leave |
439 | // the watchpoint in a !isStillValid() state. Hence, if fireAll() or invalidate() interleaved between |
440 | // the first isStillValid() check and the second one, then it would simply cause the DFG to retry |
441 | // compilation later. |
442 | // |
443 | // But, if you change some piece of state that the DFG might optimize for, but invalidate the |
444 | // watchpoint by doing: |
445 | // |
446 | // if (w.isBeingWatched()) |
447 | // w.fireAll() |
448 | // |
449 | // then the DFG would never know that you invalidated state between the two checks. |
450 | // |
451 | // There are two ways to work around this: |
452 | // |
453 | // - Call fireAll() without a isBeingWatched() check. Then, the DFG will know that the watchpoint has |
454 | // been invalidated when it does its second check. |
455 | // |
456 | // - Do not expose the watchpoint set to the DFG directly, and have your own way of validating whether |
457 | // the assumptions that the DFG thread used are still valid when the DFG code is installed. |
458 | bool isBeingWatched() const |
459 | { |
460 | if (isFat()) |
461 | return fat()->isBeingWatched(); |
462 | return false; |
463 | } |
464 | |
465 | // We expose this because sometimes a client knows its about to start |
466 | // watching this InlineWatchpointSet, hence it'll become inflated regardless. |
467 | // Such clients may find it useful to have a WatchpointSet* pointer, for example, |
468 | // if they collect a Vector of WatchpointSet*. |
469 | WatchpointSet* inflate() |
470 | { |
471 | if (LIKELY(isFat())) |
472 | return fat(); |
473 | return inflateSlow(); |
474 | } |
475 | |
476 | private: |
477 | static const uintptr_t IsThinFlag = 1; |
478 | static const uintptr_t StateMask = 6; |
479 | static const uintptr_t StateShift = 1; |
480 | |
481 | static bool isThin(uintptr_t data) { return data & IsThinFlag; } |
482 | static bool isFat(uintptr_t data) { return !isThin(data); } |
483 | |
484 | static WatchpointState decodeState(uintptr_t data) |
485 | { |
486 | ASSERT(isThin(data)); |
487 | return static_cast<WatchpointState>((data & StateMask) >> StateShift); |
488 | } |
489 | |
490 | static uintptr_t encodeState(WatchpointState state) |
491 | { |
492 | return (static_cast<uintptr_t>(state) << StateShift) | IsThinFlag; |
493 | } |
494 | |
495 | bool isThin() const { return isThin(m_data); } |
496 | bool isFat() const { return isFat(m_data); }; |
497 | |
498 | static WatchpointSet* fat(uintptr_t data) |
499 | { |
500 | return bitwise_cast<WatchpointSet*>(data); |
501 | } |
502 | |
503 | WatchpointSet* fat() |
504 | { |
505 | ASSERT(isFat()); |
506 | return fat(m_data); |
507 | } |
508 | |
509 | const WatchpointSet* fat() const |
510 | { |
511 | ASSERT(isFat()); |
512 | return fat(m_data); |
513 | } |
514 | |
515 | JS_EXPORT_PRIVATE WatchpointSet* inflateSlow(); |
516 | JS_EXPORT_PRIVATE void freeFat(); |
517 | |
518 | uintptr_t m_data; |
519 | }; |
520 | |
521 | class DeferredWatchpointFire : public FireDetail { |
522 | WTF_MAKE_NONCOPYABLE(DeferredWatchpointFire); |
523 | public: |
524 | JS_EXPORT_PRIVATE DeferredWatchpointFire(VM&); |
525 | JS_EXPORT_PRIVATE ~DeferredWatchpointFire(); |
526 | |
527 | JS_EXPORT_PRIVATE void takeWatchpointsToFire(WatchpointSet*); |
528 | JS_EXPORT_PRIVATE void fireAll(); |
529 | |
530 | void dump(PrintStream& out) const override = 0; |
531 | private: |
532 | VM& m_vm; |
533 | WatchpointSet m_watchpointsToFire; |
534 | }; |
535 | |
536 | } // namespace JSC |
537 | |
538 | namespace WTF { |
539 | |
540 | void printInternal(PrintStream& out, JSC::WatchpointState); |
541 | |
542 | } // namespace WTF |
543 | |
544 | |