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