]> git.saurik.com Git - apple/javascriptcore.git/blame_incremental - bytecode/Watchpoint.h
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / bytecode / Watchpoint.h
... / ...
CommitLineData
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/PrintStream.h>
31#include <wtf/SentinelLinkedList.h>
32#include <wtf/ThreadSafeRefCounted.h>
33
34namespace JSC {
35
36class FireDetail {
37 void* operator new(size_t) = delete;
38
39public:
40 FireDetail()
41 {
42 }
43
44 virtual ~FireDetail()
45 {
46 }
47
48 virtual void dump(PrintStream&) const = 0;
49};
50
51class StringFireDetail : public FireDetail {
52public:
53 StringFireDetail(const char* string)
54 : m_string(string)
55 {
56 }
57
58 virtual void dump(PrintStream& out) const override;
59
60private:
61 const char* m_string;
62};
63
64class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
65public:
66 Watchpoint()
67 {
68 }
69
70 virtual ~Watchpoint();
71
72 void fire(const FireDetail& detail) { fireInternal(detail); }
73
74protected:
75 virtual void fireInternal(const FireDetail&) = 0;
76};
77
78enum WatchpointState {
79 ClearWatchpoint,
80 IsWatched,
81 IsInvalidated
82};
83
84class InlineWatchpointSet;
85
86class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> {
87 friend class LLIntOffsetsExtractor;
88public:
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.
91
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
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(); }
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.
134 void startWatching()
135 {
136 ASSERT(m_state != IsInvalidated);
137 if (m_state == IsWatched)
138 return;
139 WTF::storeStoreFence();
140 m_state = IsWatched;
141 WTF::storeStoreFence();
142 }
143
144 void fireAll(const FireDetail& detail)
145 {
146 if (LIKELY(m_state != IsWatched))
147 return;
148 fireAllSlow(detail);
149 }
150
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)
159 {
160 if (state() == ClearWatchpoint)
161 startWatching();
162 else
163 fireAll(detail);
164 }
165
166 void touch(const char* reason)
167 {
168 touch(StringFireDetail(reason));
169 }
170
171 void invalidate(const FireDetail& detail)
172 {
173 if (state() == IsWatched)
174 fireAll(detail);
175 m_state = IsInvalidated;
176 }
177
178 void invalidate(const char* reason)
179 {
180 invalidate(StringFireDetail(reason));
181 }
182
183 int8_t* addressOfState() { return &m_state; }
184 int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
185
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.
188
189private:
190 void fireAllWatchpoints(const FireDetail&);
191
192 friend class InlineWatchpointSet;
193
194 int8_t m_state;
195 int8_t m_setIsNotEmpty;
196
197 SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set;
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
219class InlineWatchpointSet {
220 WTF_MAKE_NONCOPYABLE(InlineWatchpointSet);
221public:
222 InlineWatchpointSet(WatchpointState state)
223 : m_data(encodeState(state))
224 {
225 }
226
227 ~InlineWatchpointSet()
228 {
229 if (isThin())
230 return;
231 freeFat();
232 }
233
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
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.
259 bool hasBeenInvalidated() const
260 {
261 return state() == IsInvalidated;
262 }
263
264 // Like hasBeenInvalidated(), may be called from another thread.
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 }
278 ASSERT(decodeState(m_data) != IsInvalidated);
279 m_data = encodeState(IsWatched);
280 }
281
282 void fireAll(const FireDetail& detail)
283 {
284 if (isFat()) {
285 fat()->fireAll(detail);
286 return;
287 }
288 if (decodeState(m_data) == ClearWatchpoint)
289 return;
290 m_data = encodeState(IsInvalidated);
291 WTF::storeStoreFence();
292 }
293
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)
305 {
306 if (isFat()) {
307 fat()->touch(detail);
308 return;
309 }
310 uintptr_t data = m_data;
311 if (decodeState(data) == IsInvalidated)
312 return;
313 WTF::storeStoreFence();
314 if (decodeState(data) == ClearWatchpoint)
315 m_data = encodeState(IsWatched);
316 else
317 m_data = encodeState(IsInvalidated);
318 WTF::storeStoreFence();
319 }
320
321 void touch(const char* reason)
322 {
323 touch(StringFireDetail(reason));
324 }
325
326private:
327 static const uintptr_t IsThinFlag = 1;
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); }
333
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 {
342 return (static_cast<uintptr_t>(state) << StateShift) | IsThinFlag;
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 }
352
353 WatchpointSet* fat()
354 {
355 ASSERT(isFat());
356 return fat(m_data);
357 }
358
359 const WatchpointSet* fat() const
360 {
361 ASSERT(isFat());
362 return fat(m_data);
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