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