Watchpoint.h 8.66 KB
Newer Older
1
/*
2
 * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
3 4 5 6
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
7 8 9 10 11
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
12
 *
13 14 15 16 17 18 19 20 21 22 23
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24 25 26 27 28 29
 */

#ifndef Watchpoint_h
#define Watchpoint_h

#include <wtf/SentinelLinkedList.h>
30
#include <wtf/ThreadSafeRefCounted.h>
31 32 33 34 35 36 37 38 39

namespace JSC {

class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
public:
    Watchpoint()
    {
    }
    
40 41 42
    virtual ~Watchpoint();

    void fire() { fireInternal(); }
43
    
44 45
protected:
    virtual void fireInternal() = 0;
46 47
};

48 49 50 51 52
enum WatchpointState {
    ClearWatchpoint,
    IsWatched,
    IsInvalidated
};
53 54 55

class InlineWatchpointSet;

56
class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> {
57
    friend class LLIntOffsetsExtractor;
58
public:
59
    WatchpointSet(WatchpointState);
60
    ~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.
61
    
62 63 64 65 66 67 68 69 70 71 72 73
    // It is safe to call this from another thread. It may return an old
    // state. Guarantees that if *first* read the state() of the thing being
    // watched and it returned IsWatched and *second* you actually read its
    // value then it's safe to assume that if the state being watched changes
    // then also the watchpoint state() will change to IsInvalidated.
    WatchpointState state() const
    {
        WTF::loadLoadFence();
        WatchpointState result = static_cast<WatchpointState>(m_state);
        WTF::loadLoadFence();
        return result;
    }
74
    
75 76
    // It is safe to call this from another thread.  It may return true
    // even if the set actually had been invalidated, but that ought to happen
77 78 79 80 81 82
    // only in the case of races, and should be rare. Guarantees that if you
    // call this after observing something that must imply that the set is
    // invalidated, then you will see this return false. This is ensured by
    // issuing a load-load fence prior to querying the state.
    bool isStillValid() const
    {
83
        return state() != IsInvalidated;
84
    }
85
    // Like isStillValid(), may be called from another thread.
86
    bool hasBeenInvalidated() const { return !isStillValid(); }
87 88 89 90 91 92 93 94 95 96 97
    
    // As a convenience, this will ignore 0. That's because code paths in the DFG
    // that create speculation watchpoints may choose to bail out if speculation
    // had already been terminated.
    void add(Watchpoint*);
    
    // Force the watchpoint set to behave as if it was being watched even if no
    // watchpoints have been installed. This will result in invalidation if the
    // watchpoint would have fired. That's a pretty good indication that you
    // probably don't want to set watchpoints, since we typically don't want to
    // set watchpoints that we believe will actually be fired.
98 99 100 101 102
    void startWatching()
    {
        ASSERT(state() != IsInvalidated);
        m_state = IsWatched;
    }
103
    
104
    void fireAll()
105
    {
106
        if (state() != IsWatched)
107
            return;
108
        fireAllSlow();
109
    }
110
    
111
    void touch()
fpizlo@apple.com's avatar
fpizlo@apple.com committed
112 113 114 115 116 117
    {
        if (state() == ClearWatchpoint)
            startWatching();
        else
            fireAll();
    }
118 119 120 121 122 123 124
    
    void invalidate()
    {
        if (state() == IsWatched)
            fireAll();
        m_state = IsInvalidated;
    }
fpizlo@apple.com's avatar
fpizlo@apple.com committed
125

126
    int8_t* addressOfState() { return &m_state; }
127
    int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
128
    
129
    JS_EXPORT_PRIVATE void fireAllSlow(); // Call only if you've checked isWatched.
130 131 132 133
    
private:
    void fireAllWatchpoints();
    
134
    friend class InlineWatchpointSet;
akling@apple.com's avatar
akling@apple.com committed
135

136
    int8_t m_state;
137
    int8_t m_setIsNotEmpty;
akling@apple.com's avatar
akling@apple.com committed
138 139

    SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set;
140 141
};

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
// InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which
// it is not possible to quickly query whether it is being watched in a single
// branch. There is a fairly simple tradeoff between WatchpointSet and
// InlineWatchpointSet:
//
// Do you have to emit JIT code that rapidly tests whether the watchpoint set
// is being watched?  If so, use WatchpointSet.
//
// Do you need multiple parties to have pointers to the same WatchpointSet?
// If so, use WatchpointSet.
//
// Do you have to allocate a lot of watchpoint sets?  If so, use
// InlineWatchpointSet unless you answered "yes" to the previous questions.
//
// InlineWatchpointSet will use just one pointer-width word of memory unless
// you actually add watchpoints to it, in which case it internally inflates
// to a pointer to a WatchpointSet, and transfers its state to the
// WatchpointSet.

class InlineWatchpointSet {
    WTF_MAKE_NONCOPYABLE(InlineWatchpointSet);
public:
164 165
    InlineWatchpointSet(WatchpointState state)
        : m_data(encodeState(state))
166 167 168 169 170 171 172 173 174 175
    {
    }
    
    ~InlineWatchpointSet()
    {
        if (isThin())
            return;
        freeFat();
    }
    
176 177 178
    // It is safe to call this from another thread.  It may return false
    // even if the set actually had been invalidated, but that ought to happen
    // only in the case of races, and should be rare.
179 180
    bool hasBeenInvalidated() const
    {
181
        WTF::loadLoadFence();
182 183 184 185 186
        uintptr_t data = m_data;
        if (isFat(data)) {
            WTF::loadLoadFence();
            return fat(data)->hasBeenInvalidated();
        }
187
        return decodeState(data) == IsInvalidated;
188 189
    }
    
190
    // Like hasBeenInvalidated(), may be called from another thread.
191 192 193 194 195 196 197 198 199 200 201 202 203
    bool isStillValid() const
    {
        return !hasBeenInvalidated();
    }
    
    void add(Watchpoint*);
    
    void startWatching()
    {
        if (isFat()) {
            fat()->startWatching();
            return;
        }
204 205
        ASSERT(decodeState(m_data) != IsInvalidated);
        m_data = encodeState(IsWatched);
206 207
    }
    
208
    void fireAll()
209 210
    {
        if (isFat()) {
211
            fat()->fireAll();
212 213
            return;
        }
214
        if (decodeState(m_data) == ClearWatchpoint)
215
            return;
216
        m_data = encodeState(IsInvalidated);
217
        WTF::storeStoreFence();
218 219
    }
    
220
    void touch()
fpizlo@apple.com's avatar
fpizlo@apple.com committed
221 222
    {
        if (isFat()) {
223
            fat()->touch();
fpizlo@apple.com's avatar
fpizlo@apple.com committed
224 225 226 227 228 229 230 231 232
            return;
        }
        if (decodeState(m_data) == ClearWatchpoint)
            m_data = encodeState(IsWatched);
        else
            m_data = encodeState(IsInvalidated);
        WTF::storeStoreFence();
    }
    
233 234
private:
    static const uintptr_t IsThinFlag        = 1;
235 236
    static const uintptr_t StateMask         = 6;
    static const uintptr_t StateShift        = 1;
237
    
238 239 240
    static bool isThin(uintptr_t data) { return data & IsThinFlag; }
    static bool isFat(uintptr_t data) { return !isThin(data); }
    
241 242 243 244 245 246 247 248 249 250 251
    static WatchpointState decodeState(uintptr_t data)
    {
        ASSERT(isThin(data));
        return static_cast<WatchpointState>((data & StateMask) >> StateShift);
    }
    
    static uintptr_t encodeState(WatchpointState state)
    {
        return (state << StateShift) | IsThinFlag;
    }
    
252 253 254 255 256 257 258
    bool isThin() const { return isThin(m_data); }
    bool isFat() const { return isFat(m_data); };
    
    static WatchpointSet* fat(uintptr_t data)
    {
        return bitwise_cast<WatchpointSet*>(data);
    }
259 260 261 262
    
    WatchpointSet* fat()
    {
        ASSERT(isFat());
263
        return fat(m_data);
264 265 266 267 268
    }
    
    const WatchpointSet* fat() const
    {
        ASSERT(isFat());
269
        return fat(m_data);
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
    }
    
    WatchpointSet* inflate()
    {
        if (LIKELY(isFat()))
            return fat();
        return inflateSlow();
    }
    
    JS_EXPORT_PRIVATE WatchpointSet* inflateSlow();
    JS_EXPORT_PRIVATE void freeFat();
    
    uintptr_t m_data;
};

285 286 287 288
} // namespace JSC

#endif // Watchpoint_h