Loading cnn.com in MiniBrowser hits Structure::dump() under DFG::AdaptiveInferredProp...
[WebKit-https.git] / Source / JavaScriptCore / bytecode / Watchpoint.h
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/PrintStream.h>
32 #include <wtf/ScopedLambda.h>
33 #include <wtf/SentinelLinkedList.h>
34 #include <wtf/ThreadSafeRefCounted.h>
35
36 namespace JSC {
37
38 class FireDetail {
39     void* operator new(size_t) = delete;
40     
41 public:
42     FireDetail()
43     {
44     }
45     
46     virtual ~FireDetail()
47     {
48     }
49     
50     virtual void dump(PrintStream&) const = 0;
51 };
52
53 class StringFireDetail : public FireDetail {
54 public:
55     StringFireDetail(const char* string)
56         : m_string(string)
57     {
58     }
59     
60     void dump(PrintStream& out) const override;
61
62 private:
63     const char* m_string;
64 };
65
66 template<typename... Types>
67 class LazyFireDetail : public FireDetail {
68 public:
69     LazyFireDetail(const Types&... args)
70     {
71         m_lambda = scopedLambda<void(PrintStream&)>([&] (PrintStream& out) {
72             out.print(args...);
73         });
74     }
75
76     void dump(PrintStream& out) const override { m_lambda(out); }
77
78 private:
79     ScopedLambda<void(PrintStream&)> m_lambda;
80 };
81
82 template<typename... Types>
83 LazyFireDetail<Types...> createLazyFireDetail(const Types&... types)
84 {
85     return LazyFireDetail<Types...>(types...);
86 }
87
88 class WatchpointSet;
89
90 class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
91     WTF_MAKE_NONCOPYABLE(Watchpoint);
92     WTF_MAKE_FAST_ALLOCATED;
93 public:
94     Watchpoint() = default;
95     
96     virtual ~Watchpoint();
97
98 protected:
99     virtual void fireInternal(const FireDetail&) = 0;
100
101 private:
102     friend class WatchpointSet;
103     void fire(const FireDetail&);
104 };
105
106 enum WatchpointState {
107     ClearWatchpoint,
108     IsWatched,
109     IsInvalidated
110 };
111
112 class InlineWatchpointSet;
113 class DeferredWatchpointFire;
114 class VM;
115
116 class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> {
117     friend class LLIntOffsetsExtractor;
118     friend class DeferredWatchpointFire;
119 public:
120     JS_EXPORT_PRIVATE WatchpointSet(WatchpointState);
121     
122     // FIXME: In many cases, it would be amazing if this *did* fire the watchpoints. I suspect that
123     // this might be hard to get right, but still, it might be awesome.
124     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.
125     
126     // Fast way of getting the state, which only works from the main thread.
127     WatchpointState stateOnJSThread() const
128     {
129         return static_cast<WatchpointState>(m_state);
130     }
131     
132     // It is safe to call this from another thread. It may return an old
133     // state. Guarantees that if *first* read the state() of the thing being
134     // watched and it returned IsWatched and *second* you actually read its
135     // value then it's safe to assume that if the state being watched changes
136     // then also the watchpoint state() will change to IsInvalidated.
137     WatchpointState state() const
138     {
139         WTF::loadLoadFence();
140         WatchpointState result = static_cast<WatchpointState>(m_state);
141         WTF::loadLoadFence();
142         return result;
143     }
144     
145     // It is safe to call this from another thread.  It may return true
146     // even if the set actually had been invalidated, but that ought to happen
147     // only in the case of races, and should be rare. Guarantees that if you
148     // call this after observing something that must imply that the set is
149     // invalidated, then you will see this return false. This is ensured by
150     // issuing a load-load fence prior to querying the state.
151     bool isStillValid() const
152     {
153         return state() != IsInvalidated;
154     }
155     // Like isStillValid(), may be called from another thread.
156     bool hasBeenInvalidated() const { return !isStillValid(); }
157     
158     // As a convenience, this will ignore 0. That's because code paths in the DFG
159     // that create speculation watchpoints may choose to bail out if speculation
160     // had already been terminated.
161     void add(Watchpoint*);
162     
163     // Force the watchpoint set to behave as if it was being watched even if no
164     // watchpoints have been installed. This will result in invalidation if the
165     // watchpoint would have fired. That's a pretty good indication that you
166     // probably don't want to set watchpoints, since we typically don't want to
167     // set watchpoints that we believe will actually be fired.
168     void startWatching()
169     {
170         ASSERT(m_state != IsInvalidated);
171         if (m_state == IsWatched)
172             return;
173         WTF::storeStoreFence();
174         m_state = IsWatched;
175         WTF::storeStoreFence();
176     }
177
178     template <typename T>
179     void fireAll(VM& vm, T& fireDetails)
180     {
181         if (LIKELY(m_state != IsWatched))
182             return;
183         fireAllSlow(vm, fireDetails);
184     }
185
186     void touch(VM& vm, const FireDetail& detail)
187     {
188         if (state() == ClearWatchpoint)
189             startWatching();
190         else
191             fireAll(vm, detail);
192     }
193     
194     void touch(VM& vm, const char* reason)
195     {
196         touch(vm, StringFireDetail(reason));
197     }
198     
199     void invalidate(VM& vm, const FireDetail& detail)
200     {
201         if (state() == IsWatched)
202             fireAll(vm, detail);
203         m_state = IsInvalidated;
204     }
205     
206     void invalidate(VM& vm, const char* reason)
207     {
208         invalidate(vm, StringFireDetail(reason));
209     }
210     
211     bool isBeingWatched() const
212     {
213         return m_setIsNotEmpty;
214     }
215     
216     int8_t* addressOfState() { return &m_state; }
217     static ptrdiff_t offsetOfState() { return OBJECT_OFFSETOF(WatchpointSet, m_state); }
218     int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
219     
220     JS_EXPORT_PRIVATE void fireAllSlow(VM&, const FireDetail&); // Call only if you've checked isWatched.
221     JS_EXPORT_PRIVATE void fireAllSlow(VM&, DeferredWatchpointFire* deferredWatchpoints); // Ditto.
222     JS_EXPORT_PRIVATE void fireAllSlow(VM&, const char* reason); // Ditto.
223     
224 private:
225     void fireAllWatchpoints(VM&, const FireDetail&);
226     void take(WatchpointSet* other);
227     
228     friend class InlineWatchpointSet;
229
230     int8_t m_state;
231     int8_t m_setIsNotEmpty;
232
233     SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set;
234 };
235
236 // InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which
237 // it is not possible to quickly query whether it is being watched in a single
238 // branch. There is a fairly simple tradeoff between WatchpointSet and
239 // InlineWatchpointSet:
240 //
241 // Do you have to emit JIT code that rapidly tests whether the watchpoint set
242 // is being watched?  If so, use WatchpointSet.
243 //
244 // Do you need multiple parties to have pointers to the same WatchpointSet?
245 // If so, use WatchpointSet.
246 //
247 // Do you have to allocate a lot of watchpoint sets?  If so, use
248 // InlineWatchpointSet unless you answered "yes" to the previous questions.
249 //
250 // InlineWatchpointSet will use just one pointer-width word of memory unless
251 // you actually add watchpoints to it, in which case it internally inflates
252 // to a pointer to a WatchpointSet, and transfers its state to the
253 // WatchpointSet.
254
255 class InlineWatchpointSet {
256     WTF_MAKE_NONCOPYABLE(InlineWatchpointSet);
257 public:
258     InlineWatchpointSet(WatchpointState state)
259         : m_data(encodeState(state))
260     {
261     }
262     
263     ~InlineWatchpointSet()
264     {
265         if (isThin())
266             return;
267         freeFat();
268     }
269     
270     // Fast way of getting the state, which only works from the main thread.
271     WatchpointState stateOnJSThread() const
272     {
273         uintptr_t data = m_data;
274         if (isFat(data))
275             return fat(data)->stateOnJSThread();
276         return decodeState(data);
277     }
278
279     // It is safe to call this from another thread. It may return a prior state,
280     // but that should be fine since you should only perform actions based on the
281     // state if you also add a watchpoint.
282     WatchpointState state() const
283     {
284         WTF::loadLoadFence();
285         uintptr_t data = m_data;
286         WTF::loadLoadFence();
287         if (isFat(data))
288             return fat(data)->state();
289         return decodeState(data);
290     }
291     
292     // It is safe to call this from another thread.  It may return false
293     // even if the set actually had been invalidated, but that ought to happen
294     // only in the case of races, and should be rare.
295     bool hasBeenInvalidated() const
296     {
297         return state() == IsInvalidated;
298     }
299     
300     // Like hasBeenInvalidated(), may be called from another thread.
301     bool isStillValid() const
302     {
303         return !hasBeenInvalidated();
304     }
305     
306     void add(Watchpoint*);
307     
308     void startWatching()
309     {
310         if (isFat()) {
311             fat()->startWatching();
312             return;
313         }
314         ASSERT(decodeState(m_data) != IsInvalidated);
315         m_data = encodeState(IsWatched);
316     }
317
318     template <typename T>
319     void fireAll(VM& vm, T fireDetails)
320     {
321         if (isFat()) {
322             fat()->fireAll(vm, fireDetails);
323             return;
324         }
325         if (decodeState(m_data) == ClearWatchpoint)
326             return;
327         m_data = encodeState(IsInvalidated);
328         WTF::storeStoreFence();
329     }
330
331     void invalidate(VM& vm, const FireDetail& detail)
332     {
333         if (isFat())
334             fat()->invalidate(vm, detail);
335         else
336             m_data = encodeState(IsInvalidated);
337     }
338     
339     JS_EXPORT_PRIVATE void fireAll(VM&, const char* reason);
340     
341     void touch(VM& vm, const FireDetail& detail)
342     {
343         if (isFat()) {
344             fat()->touch(vm, detail);
345             return;
346         }
347         uintptr_t data = m_data;
348         if (decodeState(data) == IsInvalidated)
349             return;
350         WTF::storeStoreFence();
351         if (decodeState(data) == ClearWatchpoint)
352             m_data = encodeState(IsWatched);
353         else
354             m_data = encodeState(IsInvalidated);
355         WTF::storeStoreFence();
356     }
357     
358     void touch(VM& vm, const char* reason)
359     {
360         touch(vm, StringFireDetail(reason));
361     }
362
363     // Note that for any watchpoint that is visible from the DFG, it would be incorrect to write code like:
364     //
365     // if (w.isBeingWatched())
366     //     w.fireAll()
367     //
368     // Concurrently to this, the DFG could do:
369     //
370     // if (w.isStillValid())
371     //     perform optimizations;
372     // if (!w.isStillValid())
373     //     retry compilation;
374     //
375     // Note that the DFG algorithm is widespread, and sound, because fireAll() and invalidate() will leave
376     // the watchpoint in a !isStillValid() state. Hence, if fireAll() or invalidate() interleaved between
377     // the first isStillValid() check and the second one, then it would simply cause the DFG to retry
378     // compilation later.
379     //
380     // But, if you change some piece of state that the DFG might optimize for, but invalidate the
381     // watchpoint by doing:
382     //
383     // if (w.isBeingWatched())
384     //     w.fireAll()
385     //
386     // then the DFG would never know that you invalidated state between the two checks.
387     //
388     // There are two ways to work around this:
389     //
390     // - Call fireAll() without a isBeingWatched() check. Then, the DFG will know that the watchpoint has
391     //   been invalidated when it does its second check.
392     //
393     // - Do not expose the watchpoint set to the DFG directly, and have your own way of validating whether
394     //   the assumptions that the DFG thread used are still valid when the DFG code is installed.
395     bool isBeingWatched() const
396     {
397         if (isFat())
398             return fat()->isBeingWatched();
399         return false;
400     }
401
402     // We expose this because sometimes a client knows its about to start
403     // watching this InlineWatchpointSet, hence it'll become inflated regardless.
404     // Such clients may find it useful to have a WatchpointSet* pointer, for example,
405     // if they collect a Vector of WatchpointSet*.
406     WatchpointSet* inflate()
407     {
408         if (LIKELY(isFat()))
409             return fat();
410         return inflateSlow();
411     }
412     
413 private:
414     static const uintptr_t IsThinFlag        = 1;
415     static const uintptr_t StateMask         = 6;
416     static const uintptr_t StateShift        = 1;
417     
418     static bool isThin(uintptr_t data) { return data & IsThinFlag; }
419     static bool isFat(uintptr_t data) { return !isThin(data); }
420     
421     static WatchpointState decodeState(uintptr_t data)
422     {
423         ASSERT(isThin(data));
424         return static_cast<WatchpointState>((data & StateMask) >> StateShift);
425     }
426     
427     static uintptr_t encodeState(WatchpointState state)
428     {
429         return (static_cast<uintptr_t>(state) << StateShift) | IsThinFlag;
430     }
431     
432     bool isThin() const { return isThin(m_data); }
433     bool isFat() const { return isFat(m_data); };
434     
435     static WatchpointSet* fat(uintptr_t data)
436     {
437         return bitwise_cast<WatchpointSet*>(data);
438     }
439     
440     WatchpointSet* fat()
441     {
442         ASSERT(isFat());
443         return fat(m_data);
444     }
445     
446     const WatchpointSet* fat() const
447     {
448         ASSERT(isFat());
449         return fat(m_data);
450     }
451     
452     JS_EXPORT_PRIVATE WatchpointSet* inflateSlow();
453     JS_EXPORT_PRIVATE void freeFat();
454     
455     uintptr_t m_data;
456 };
457
458 class DeferredWatchpointFire : public FireDetail {
459     WTF_MAKE_NONCOPYABLE(DeferredWatchpointFire);
460 public:
461     JS_EXPORT_PRIVATE DeferredWatchpointFire(VM&);
462     JS_EXPORT_PRIVATE ~DeferredWatchpointFire();
463
464     JS_EXPORT_PRIVATE void takeWatchpointsToFire(WatchpointSet*);
465     JS_EXPORT_PRIVATE void fireAll();
466
467     void dump(PrintStream& out) const override = 0;
468 private:
469     VM& m_vm;
470     WatchpointSet m_watchpointsToFire;
471 };
472
473 } // namespace JSC