1/* 2 * Copyright (C) 2012, 2013 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/SentinelLinkedList.h> 31#include <wtf/ThreadSafeRefCounted.h> 32 33namespace JSC { 34 35class Watchpoint : public BasicRawSentinelNode<Watchpoint> { 36public: 37 Watchpoint() 38 { 39 } 40 41 virtual ~Watchpoint(); 42 43 void fire() { fireInternal(); } 44 45protected: 46 virtual void fireInternal() = 0; 47}; 48 49enum WatchpointState { 50 ClearWatchpoint, 51 IsWatched, 52 IsInvalidated 53}; 54 55class InlineWatchpointSet; 56 57class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> { 58 friend class LLIntOffsetsExtractor; 59public: 60 JS_EXPORT_PRIVATE WatchpointSet(WatchpointState); 61 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. 62 63 // It is safe to call this from another thread. It may return an old 64 // state. Guarantees that if *first* read the state() of the thing being 65 // watched and it returned IsWatched and *second* you actually read its 66 // value then it's safe to assume that if the state being watched changes 67 // then also the watchpoint state() will change to IsInvalidated. 68 WatchpointState state() const 69 { 70 WTF::loadLoadFence(); 71 WatchpointState result = static_cast<WatchpointState>(m_state); 72 WTF::loadLoadFence(); 73 return result; 74 } 75 76 // It is safe to call this from another thread. It may return true 77 // even if the set actually had been invalidated, but that ought to happen 78 // only in the case of races, and should be rare. Guarantees that if you 79 // call this after observing something that must imply that the set is 80 // invalidated, then you will see this return false. This is ensured by 81 // issuing a load-load fence prior to querying the state. 82 bool isStillValid() const 83 { 84 return state() != IsInvalidated; 85 } 86 // Like isStillValid(), may be called from another thread. 87 bool hasBeenInvalidated() const { return !isStillValid(); } 88 89 // As a convenience, this will ignore 0. That's because code paths in the DFG 90 // that create speculation watchpoints may choose to bail out if speculation 91 // had already been terminated. 92 void add(Watchpoint*); 93 94 // Force the watchpoint set to behave as if it was being watched even if no 95 // watchpoints have been installed. This will result in invalidation if the 96 // watchpoint would have fired. That's a pretty good indication that you 97 // probably don't want to set watchpoints, since we typically don't want to 98 // set watchpoints that we believe will actually be fired. 99 void startWatching() 100 { 101 ASSERT(state() != IsInvalidated); 102 m_state = IsWatched; 103 } 104 105 void fireAll() 106 { 107 if (state() != IsWatched) 108 return; 109 fireAllSlow(); 110 } 111 112 void touch() 113 { 114 if (state() == ClearWatchpoint) 115 startWatching(); 116 else 117 fireAll(); 118 } 119 120 void invalidate() 121 { 122 if (state() == IsWatched) 123 fireAll(); 124 m_state = IsInvalidated; 125 } 126 127 int8_t* addressOfState() { return &m_state; } 128 int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; } 129 130 JS_EXPORT_PRIVATE void fireAllSlow(); // Call only if you've checked isWatched. 131 132private: 133 void fireAllWatchpoints(); 134 135 friend class InlineWatchpointSet; 136 137 int8_t m_state; 138 int8_t m_setIsNotEmpty; 139 140 SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set; 141}; 142 143// InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which 144// it is not possible to quickly query whether it is being watched in a single 145// branch. There is a fairly simple tradeoff between WatchpointSet and 146// InlineWatchpointSet: 147// 148// Do you have to emit JIT code that rapidly tests whether the watchpoint set 149// is being watched? If so, use WatchpointSet. 150// 151// Do you need multiple parties to have pointers to the same WatchpointSet? 152// If so, use WatchpointSet. 153// 154// Do you have to allocate a lot of watchpoint sets? If so, use 155// InlineWatchpointSet unless you answered "yes" to the previous questions. 156// 157// InlineWatchpointSet will use just one pointer-width word of memory unless 158// you actually add watchpoints to it, in which case it internally inflates 159// to a pointer to a WatchpointSet, and transfers its state to the 160// WatchpointSet. 161 162class InlineWatchpointSet { 163 WTF_MAKE_NONCOPYABLE(InlineWatchpointSet); 164public: 165 InlineWatchpointSet(WatchpointState state) 166 : m_data(encodeState(state)) 167 { 168 } 169 170 ~InlineWatchpointSet() 171 { 172 if (isThin()) 173 return; 174 freeFat(); 175 } 176 177 // It is safe to call this from another thread. It may return false 178 // even if the set actually had been invalidated, but that ought to happen 179 // only in the case of races, and should be rare. 180 bool hasBeenInvalidated() const 181 { 182 WTF::loadLoadFence(); 183 uintptr_t data = m_data; 184 if (isFat(data)) { 185 WTF::loadLoadFence(); 186 return fat(data)->hasBeenInvalidated(); 187 } 188 return decodeState(data) == IsInvalidated; 189 } 190 191 // Like hasBeenInvalidated(), may be called from another thread. 192 bool isStillValid() const 193 { 194 return !hasBeenInvalidated(); 195 } 196 197 void add(Watchpoint*); 198 199 void startWatching() 200 { 201 if (isFat()) { 202 fat()->startWatching(); 203 return; 204 } 205 ASSERT(decodeState(m_data) != IsInvalidated); 206 m_data = encodeState(IsWatched); 207 } 208 209 void fireAll() 210 { 211 if (isFat()) { 212 fat()->fireAll(); 213 return; 214 } 215 if (decodeState(m_data) == ClearWatchpoint) 216 return; 217 m_data = encodeState(IsInvalidated); 218 WTF::storeStoreFence(); 219 } 220 221 void touch() 222 { 223 if (isFat()) { 224 fat()->touch(); 225 return; 226 } 227 if (decodeState(m_data) == ClearWatchpoint) 228 m_data = encodeState(IsWatched); 229 else 230 m_data = encodeState(IsInvalidated); 231 WTF::storeStoreFence(); 232 } 233 234private: 235 static const uintptr_t IsThinFlag = 1; 236 static const uintptr_t StateMask = 6; 237 static const uintptr_t StateShift = 1; 238 239 static bool isThin(uintptr_t data) { return data & IsThinFlag; } 240 static bool isFat(uintptr_t data) { return !isThin(data); } 241 242 static WatchpointState decodeState(uintptr_t data) 243 { 244 ASSERT(isThin(data)); 245 return static_cast<WatchpointState>((data & StateMask) >> StateShift); 246 } 247 248 static uintptr_t encodeState(WatchpointState state) 249 { 250 return (state << StateShift) | IsThinFlag; 251 } 252 253 bool isThin() const { return isThin(m_data); } 254 bool isFat() const { return isFat(m_data); }; 255 256 static WatchpointSet* fat(uintptr_t data) 257 { 258 return bitwise_cast<WatchpointSet*>(data); 259 } 260 261 WatchpointSet* fat() 262 { 263 ASSERT(isFat()); 264 return fat(m_data); 265 } 266 267 const WatchpointSet* fat() const 268 { 269 ASSERT(isFat()); 270 return fat(m_data); 271 } 272 273 WatchpointSet* inflate() 274 { 275 if (LIKELY(isFat())) 276 return fat(); 277 return inflateSlow(); 278 } 279 280 JS_EXPORT_PRIVATE WatchpointSet* inflateSlow(); 281 JS_EXPORT_PRIVATE void freeFat(); 282 283 uintptr_t m_data; 284}; 285 286} // namespace JSC 287 288#endif // Watchpoint_h 289 290