1 | /* |
2 | * Copyright (C) 2012-2019 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(StructureTransitionStructureStubClearing, StructureTransitionStructureStubClearingWatchpoint) |
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 | // Make sure that the state can be represented in 2 bits. |
165 | enum WatchpointState : uint8_t { |
166 | ClearWatchpoint = 0, |
167 | IsWatched = 1, |
168 | IsInvalidated = 2 |
169 | }; |
170 | |
171 | class InlineWatchpointSet; |
172 | class DeferredWatchpointFire; |
173 | class VM; |
174 | |
175 | class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> { |
176 | friend class LLIntOffsetsExtractor; |
177 | friend class DeferredWatchpointFire; |
178 | public: |
179 | JS_EXPORT_PRIVATE WatchpointSet(WatchpointState); |
180 | |
181 | // FIXME: In many cases, it would be amazing if this *did* fire the watchpoints. I suspect that |
182 | // this might be hard to get right, but still, it might be awesome. |
183 | 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. |
184 | |
185 | static Ref<WatchpointSet> create(WatchpointState state) |
186 | { |
187 | return adoptRef(*new WatchpointSet(state)); |
188 | } |
189 | |
190 | // Fast way of getting the state, which only works from the main thread. |
191 | WatchpointState stateOnJSThread() const |
192 | { |
193 | return static_cast<WatchpointState>(m_state); |
194 | } |
195 | |
196 | // It is safe to call this from another thread. It may return an old |
197 | // state. Guarantees that if *first* read the state() of the thing being |
198 | // watched and it returned IsWatched and *second* you actually read its |
199 | // value then it's safe to assume that if the state being watched changes |
200 | // then also the watchpoint state() will change to IsInvalidated. |
201 | WatchpointState state() const |
202 | { |
203 | WTF::loadLoadFence(); |
204 | WatchpointState result = static_cast<WatchpointState>(m_state); |
205 | WTF::loadLoadFence(); |
206 | return result; |
207 | } |
208 | |
209 | // It is safe to call this from another thread. It may return true |
210 | // even if the set actually had been invalidated, but that ought to happen |
211 | // only in the case of races, and should be rare. Guarantees that if you |
212 | // call this after observing something that must imply that the set is |
213 | // invalidated, then you will see this return false. This is ensured by |
214 | // issuing a load-load fence prior to querying the state. |
215 | bool isStillValid() const |
216 | { |
217 | return state() != IsInvalidated; |
218 | } |
219 | // Like isStillValid(), may be called from another thread. |
220 | bool hasBeenInvalidated() const { return !isStillValid(); } |
221 | |
222 | // As a convenience, this will ignore 0. That's because code paths in the DFG |
223 | // that create speculation watchpoints may choose to bail out if speculation |
224 | // had already been terminated. |
225 | void add(Watchpoint*); |
226 | |
227 | // Force the watchpoint set to behave as if it was being watched even if no |
228 | // watchpoints have been installed. This will result in invalidation if the |
229 | // watchpoint would have fired. That's a pretty good indication that you |
230 | // probably don't want to set watchpoints, since we typically don't want to |
231 | // set watchpoints that we believe will actually be fired. |
232 | void startWatching() |
233 | { |
234 | ASSERT(m_state != IsInvalidated); |
235 | if (m_state == IsWatched) |
236 | return; |
237 | WTF::storeStoreFence(); |
238 | m_state = IsWatched; |
239 | WTF::storeStoreFence(); |
240 | } |
241 | |
242 | template <typename T> |
243 | void fireAll(VM& vm, T& fireDetails) |
244 | { |
245 | if (LIKELY(m_state != IsWatched)) |
246 | return; |
247 | fireAllSlow(vm, fireDetails); |
248 | } |
249 | |
250 | void touch(VM& vm, const FireDetail& detail) |
251 | { |
252 | if (state() == ClearWatchpoint) |
253 | startWatching(); |
254 | else |
255 | fireAll(vm, detail); |
256 | } |
257 | |
258 | void touch(VM& vm, const char* reason) |
259 | { |
260 | touch(vm, StringFireDetail(reason)); |
261 | } |
262 | |
263 | void invalidate(VM& vm, const FireDetail& detail) |
264 | { |
265 | if (state() == IsWatched) |
266 | fireAll(vm, detail); |
267 | m_state = IsInvalidated; |
268 | } |
269 | |
270 | void invalidate(VM& vm, const char* reason) |
271 | { |
272 | invalidate(vm, StringFireDetail(reason)); |
273 | } |
274 | |
275 | bool isBeingWatched() const |
276 | { |
277 | return m_setIsNotEmpty; |
278 | } |
279 | |
280 | int8_t* addressOfState() { return &m_state; } |
281 | static ptrdiff_t offsetOfState() { return OBJECT_OFFSETOF(WatchpointSet, m_state); } |
282 | int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; } |
283 | |
284 | JS_EXPORT_PRIVATE void fireAllSlow(VM&, const FireDetail&); // Call only if you've checked isWatched. |
285 | JS_EXPORT_PRIVATE void fireAllSlow(VM&, DeferredWatchpointFire* deferredWatchpoints); // Ditto. |
286 | JS_EXPORT_PRIVATE void fireAllSlow(VM&, const char* reason); // Ditto. |
287 | |
288 | private: |
289 | void fireAllWatchpoints(VM&, const FireDetail&); |
290 | void take(WatchpointSet* other); |
291 | |
292 | friend class InlineWatchpointSet; |
293 | |
294 | int8_t m_state; |
295 | int8_t m_setIsNotEmpty; |
296 | |
297 | SentinelLinkedList<Watchpoint, PackedRawSentinelNode<Watchpoint>> m_set; |
298 | }; |
299 | |
300 | // InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which |
301 | // it is not possible to quickly query whether it is being watched in a single |
302 | // branch. There is a fairly simple tradeoff between WatchpointSet and |
303 | // InlineWatchpointSet: |
304 | // |
305 | // Do you have to emit JIT code that rapidly tests whether the watchpoint set |
306 | // is being watched? If so, use WatchpointSet. |
307 | // |
308 | // Do you need multiple parties to have pointers to the same WatchpointSet? |
309 | // If so, use WatchpointSet. |
310 | // |
311 | // Do you have to allocate a lot of watchpoint sets? If so, use |
312 | // InlineWatchpointSet unless you answered "yes" to the previous questions. |
313 | // |
314 | // InlineWatchpointSet will use just one pointer-width word of memory unless |
315 | // you actually add watchpoints to it, in which case it internally inflates |
316 | // to a pointer to a WatchpointSet, and transfers its state to the |
317 | // WatchpointSet. |
318 | |
319 | class InlineWatchpointSet { |
320 | WTF_MAKE_NONCOPYABLE(InlineWatchpointSet); |
321 | public: |
322 | InlineWatchpointSet(WatchpointState state) |
323 | : m_data(encodeState(state)) |
324 | { |
325 | } |
326 | |
327 | ~InlineWatchpointSet() |
328 | { |
329 | if (isThin()) |
330 | return; |
331 | freeFat(); |
332 | } |
333 | |
334 | // Fast way of getting the state, which only works from the main thread. |
335 | WatchpointState stateOnJSThread() const |
336 | { |
337 | uintptr_t data = m_data; |
338 | if (isFat(data)) |
339 | return fat(data)->stateOnJSThread(); |
340 | return decodeState(data); |
341 | } |
342 | |
343 | // It is safe to call this from another thread. It may return a prior state, |
344 | // but that should be fine since you should only perform actions based on the |
345 | // state if you also add a watchpoint. |
346 | WatchpointState state() const |
347 | { |
348 | WTF::loadLoadFence(); |
349 | uintptr_t data = m_data; |
350 | WTF::loadLoadFence(); |
351 | if (isFat(data)) |
352 | return fat(data)->state(); |
353 | return decodeState(data); |
354 | } |
355 | |
356 | // It is safe to call this from another thread. It may return false |
357 | // even if the set actually had been invalidated, but that ought to happen |
358 | // only in the case of races, and should be rare. |
359 | bool hasBeenInvalidated() const |
360 | { |
361 | return state() == IsInvalidated; |
362 | } |
363 | |
364 | // Like hasBeenInvalidated(), may be called from another thread. |
365 | bool isStillValid() const |
366 | { |
367 | return !hasBeenInvalidated(); |
368 | } |
369 | |
370 | void add(Watchpoint*); |
371 | |
372 | void startWatching() |
373 | { |
374 | if (isFat()) { |
375 | fat()->startWatching(); |
376 | return; |
377 | } |
378 | ASSERT(decodeState(m_data) != IsInvalidated); |
379 | m_data = encodeState(IsWatched); |
380 | } |
381 | |
382 | template <typename T> |
383 | void fireAll(VM& vm, T fireDetails) |
384 | { |
385 | if (isFat()) { |
386 | fat()->fireAll(vm, fireDetails); |
387 | return; |
388 | } |
389 | if (decodeState(m_data) == ClearWatchpoint) |
390 | return; |
391 | m_data = encodeState(IsInvalidated); |
392 | WTF::storeStoreFence(); |
393 | } |
394 | |
395 | void invalidate(VM& vm, const FireDetail& detail) |
396 | { |
397 | if (isFat()) |
398 | fat()->invalidate(vm, detail); |
399 | else |
400 | m_data = encodeState(IsInvalidated); |
401 | } |
402 | |
403 | JS_EXPORT_PRIVATE void fireAll(VM&, const char* reason); |
404 | |
405 | void touch(VM& vm, const FireDetail& detail) |
406 | { |
407 | if (isFat()) { |
408 | fat()->touch(vm, detail); |
409 | return; |
410 | } |
411 | uintptr_t data = m_data; |
412 | if (decodeState(data) == IsInvalidated) |
413 | return; |
414 | WTF::storeStoreFence(); |
415 | if (decodeState(data) == ClearWatchpoint) |
416 | m_data = encodeState(IsWatched); |
417 | else |
418 | m_data = encodeState(IsInvalidated); |
419 | WTF::storeStoreFence(); |
420 | } |
421 | |
422 | void touch(VM& vm, const char* reason) |
423 | { |
424 | touch(vm, StringFireDetail(reason)); |
425 | } |
426 | |
427 | // Note that for any watchpoint that is visible from the DFG, it would be incorrect to write code like: |
428 | // |
429 | // if (w.isBeingWatched()) |
430 | // w.fireAll() |
431 | // |
432 | // Concurrently to this, the DFG could do: |
433 | // |
434 | // if (w.isStillValid()) |
435 | // perform optimizations; |
436 | // if (!w.isStillValid()) |
437 | // retry compilation; |
438 | // |
439 | // Note that the DFG algorithm is widespread, and sound, because fireAll() and invalidate() will leave |
440 | // the watchpoint in a !isStillValid() state. Hence, if fireAll() or invalidate() interleaved between |
441 | // the first isStillValid() check and the second one, then it would simply cause the DFG to retry |
442 | // compilation later. |
443 | // |
444 | // But, if you change some piece of state that the DFG might optimize for, but invalidate the |
445 | // watchpoint by doing: |
446 | // |
447 | // if (w.isBeingWatched()) |
448 | // w.fireAll() |
449 | // |
450 | // then the DFG would never know that you invalidated state between the two checks. |
451 | // |
452 | // There are two ways to work around this: |
453 | // |
454 | // - Call fireAll() without a isBeingWatched() check. Then, the DFG will know that the watchpoint has |
455 | // been invalidated when it does its second check. |
456 | // |
457 | // - Do not expose the watchpoint set to the DFG directly, and have your own way of validating whether |
458 | // the assumptions that the DFG thread used are still valid when the DFG code is installed. |
459 | bool isBeingWatched() const |
460 | { |
461 | if (isFat()) |
462 | return fat()->isBeingWatched(); |
463 | return false; |
464 | } |
465 | |
466 | // We expose this because sometimes a client knows its about to start |
467 | // watching this InlineWatchpointSet, hence it'll become inflated regardless. |
468 | // Such clients may find it useful to have a WatchpointSet* pointer, for example, |
469 | // if they collect a Vector of WatchpointSet*. |
470 | WatchpointSet* inflate() |
471 | { |
472 | if (LIKELY(isFat())) |
473 | return fat(); |
474 | return inflateSlow(); |
475 | } |
476 | |
477 | private: |
478 | static constexpr uintptr_t IsThinFlag = 1; |
479 | static constexpr uintptr_t StateMask = 6; |
480 | static constexpr uintptr_t StateShift = 1; |
481 | |
482 | static bool isThin(uintptr_t data) { return data & IsThinFlag; } |
483 | static bool isFat(uintptr_t data) { return !isThin(data); } |
484 | |
485 | static WatchpointState decodeState(uintptr_t data) |
486 | { |
487 | ASSERT(isThin(data)); |
488 | return static_cast<WatchpointState>((data & StateMask) >> StateShift); |
489 | } |
490 | |
491 | static uintptr_t encodeState(WatchpointState state) |
492 | { |
493 | return (static_cast<uintptr_t>(state) << StateShift) | IsThinFlag; |
494 | } |
495 | |
496 | bool isThin() const { return isThin(m_data); } |
497 | bool isFat() const { return isFat(m_data); }; |
498 | |
499 | static WatchpointSet* fat(uintptr_t data) |
500 | { |
501 | return bitwise_cast<WatchpointSet*>(data); |
502 | } |
503 | |
504 | WatchpointSet* fat() |
505 | { |
506 | ASSERT(isFat()); |
507 | return fat(m_data); |
508 | } |
509 | |
510 | const WatchpointSet* fat() const |
511 | { |
512 | ASSERT(isFat()); |
513 | return fat(m_data); |
514 | } |
515 | |
516 | JS_EXPORT_PRIVATE WatchpointSet* inflateSlow(); |
517 | JS_EXPORT_PRIVATE void freeFat(); |
518 | |
519 | uintptr_t m_data; |
520 | }; |
521 | |
522 | class DeferredWatchpointFire : public FireDetail { |
523 | WTF_MAKE_NONCOPYABLE(DeferredWatchpointFire); |
524 | public: |
525 | JS_EXPORT_PRIVATE DeferredWatchpointFire(VM&); |
526 | JS_EXPORT_PRIVATE ~DeferredWatchpointFire(); |
527 | |
528 | JS_EXPORT_PRIVATE void takeWatchpointsToFire(WatchpointSet*); |
529 | JS_EXPORT_PRIVATE void fireAll(); |
530 | |
531 | void dump(PrintStream& out) const override = 0; |
532 | private: |
533 | VM& m_vm; |
534 | WatchpointSet m_watchpointsToFire; |
535 | }; |
536 | |
537 | } // namespace JSC |
538 | |
539 | namespace WTF { |
540 | |
541 | void printInternal(PrintStream& out, JSC::WatchpointState); |
542 | |
543 | } // namespace WTF |
544 | |
545 | |