summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/bytecode/Watchpoint.h
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/bytecode/Watchpoint.h')
-rw-r--r--Source/JavaScriptCore/bytecode/Watchpoint.h401
1 files changed, 401 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/bytecode/Watchpoint.h b/Source/JavaScriptCore/bytecode/Watchpoint.h
new file mode 100644
index 000000000..c8f628d33
--- /dev/null
+++ b/Source/JavaScriptCore/bytecode/Watchpoint.h
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 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.
+ *
+ * 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.
+ */
+
+#ifndef Watchpoint_h
+#define Watchpoint_h
+
+#include <wtf/Atomics.h>
+#include <wtf/FastMalloc.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/PrintStream.h>
+#include <wtf/SentinelLinkedList.h>
+#include <wtf/ThreadSafeRefCounted.h>
+
+namespace JSC {
+
+class FireDetail {
+ void* operator new(size_t) = delete;
+
+public:
+ FireDetail()
+ {
+ }
+
+ virtual ~FireDetail()
+ {
+ }
+
+ virtual void dump(PrintStream&) const = 0;
+};
+
+class StringFireDetail : public FireDetail {
+public:
+ StringFireDetail(const char* string)
+ : m_string(string)
+ {
+ }
+
+ virtual void dump(PrintStream& out) const override;
+
+private:
+ const char* m_string;
+};
+
+class WatchpointSet;
+
+class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
+ WTF_MAKE_NONCOPYABLE(Watchpoint);
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ Watchpoint()
+ {
+ }
+
+ virtual ~Watchpoint();
+
+protected:
+ virtual void fireInternal(const FireDetail&) = 0;
+
+private:
+ friend class WatchpointSet;
+ void fire(const FireDetail&);
+};
+
+enum WatchpointState {
+ ClearWatchpoint,
+ IsWatched,
+ IsInvalidated
+};
+
+class InlineWatchpointSet;
+
+class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> {
+ friend class LLIntOffsetsExtractor;
+public:
+ JS_EXPORT_PRIVATE WatchpointSet(WatchpointState);
+ 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.
+
+ // Fast way of getting the state, which only works from the main thread.
+ WatchpointState stateOnJSThread() const
+ {
+ return static_cast<WatchpointState>(m_state);
+ }
+
+ // 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;
+ }
+
+ // 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
+ // 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
+ {
+ return state() != IsInvalidated;
+ }
+ // Like isStillValid(), may be called from another thread.
+ bool hasBeenInvalidated() const { return !isStillValid(); }
+
+ // 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.
+ void startWatching()
+ {
+ ASSERT(m_state != IsInvalidated);
+ if (m_state == IsWatched)
+ return;
+ WTF::storeStoreFence();
+ m_state = IsWatched;
+ WTF::storeStoreFence();
+ }
+
+ void fireAll(const FireDetail& detail)
+ {
+ if (LIKELY(m_state != IsWatched))
+ return;
+ fireAllSlow(detail);
+ }
+
+ void fireAll(const char* reason)
+ {
+ if (LIKELY(m_state != IsWatched))
+ return;
+ fireAllSlow(reason);
+ }
+
+ void touch(const FireDetail& detail)
+ {
+ if (state() == ClearWatchpoint)
+ startWatching();
+ else
+ fireAll(detail);
+ }
+
+ void touch(const char* reason)
+ {
+ touch(StringFireDetail(reason));
+ }
+
+ void invalidate(const FireDetail& detail)
+ {
+ if (state() == IsWatched)
+ fireAll(detail);
+ m_state = IsInvalidated;
+ }
+
+ void invalidate(const char* reason)
+ {
+ invalidate(StringFireDetail(reason));
+ }
+
+ bool isBeingWatched() const
+ {
+ return m_setIsNotEmpty;
+ }
+
+ int8_t* addressOfState() { return &m_state; }
+ int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
+
+ JS_EXPORT_PRIVATE void fireAllSlow(const FireDetail&); // Call only if you've checked isWatched.
+ JS_EXPORT_PRIVATE void fireAllSlow(const char* reason); // Ditto.
+
+private:
+ void fireAllWatchpoints(const FireDetail&);
+
+ friend class InlineWatchpointSet;
+
+ int8_t m_state;
+ int8_t m_setIsNotEmpty;
+
+ SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set;
+};
+
+// 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:
+ InlineWatchpointSet(WatchpointState state)
+ : m_data(encodeState(state))
+ {
+ }
+
+ ~InlineWatchpointSet()
+ {
+ if (isThin())
+ return;
+ freeFat();
+ }
+
+ // Fast way of getting the state, which only works from the main thread.
+ WatchpointState stateOnJSThread() const
+ {
+ uintptr_t data = m_data;
+ if (isFat(data))
+ return fat(data)->stateOnJSThread();
+ return decodeState(data);
+ }
+
+ // It is safe to call this from another thread. It may return a prior state,
+ // but that should be fine since you should only perform actions based on the
+ // state if you also add a watchpoint.
+ WatchpointState state() const
+ {
+ WTF::loadLoadFence();
+ uintptr_t data = m_data;
+ WTF::loadLoadFence();
+ if (isFat(data))
+ return fat(data)->state();
+ return decodeState(data);
+ }
+
+ // 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.
+ bool hasBeenInvalidated() const
+ {
+ return state() == IsInvalidated;
+ }
+
+ // Like hasBeenInvalidated(), may be called from another thread.
+ bool isStillValid() const
+ {
+ return !hasBeenInvalidated();
+ }
+
+ void add(Watchpoint*);
+
+ void startWatching()
+ {
+ if (isFat()) {
+ fat()->startWatching();
+ return;
+ }
+ ASSERT(decodeState(m_data) != IsInvalidated);
+ m_data = encodeState(IsWatched);
+ }
+
+ void fireAll(const FireDetail& detail)
+ {
+ if (isFat()) {
+ fat()->fireAll(detail);
+ return;
+ }
+ if (decodeState(m_data) == ClearWatchpoint)
+ return;
+ m_data = encodeState(IsInvalidated);
+ WTF::storeStoreFence();
+ }
+
+ void invalidate(const FireDetail& detail)
+ {
+ if (isFat())
+ fat()->invalidate(detail);
+ else
+ m_data = encodeState(IsInvalidated);
+ }
+
+ JS_EXPORT_PRIVATE void fireAll(const char* reason);
+
+ void touch(const FireDetail& detail)
+ {
+ if (isFat()) {
+ fat()->touch(detail);
+ return;
+ }
+ uintptr_t data = m_data;
+ if (decodeState(data) == IsInvalidated)
+ return;
+ WTF::storeStoreFence();
+ if (decodeState(data) == ClearWatchpoint)
+ m_data = encodeState(IsWatched);
+ else
+ m_data = encodeState(IsInvalidated);
+ WTF::storeStoreFence();
+ }
+
+ void touch(const char* reason)
+ {
+ touch(StringFireDetail(reason));
+ }
+
+ bool isBeingWatched() const
+ {
+ if (isFat())
+ return fat()->isBeingWatched();
+ return false;
+ }
+
+private:
+ static const uintptr_t IsThinFlag = 1;
+ static const uintptr_t StateMask = 6;
+ static const uintptr_t StateShift = 1;
+
+ static bool isThin(uintptr_t data) { return data & IsThinFlag; }
+ static bool isFat(uintptr_t data) { return !isThin(data); }
+
+ static WatchpointState decodeState(uintptr_t data)
+ {
+ ASSERT(isThin(data));
+ return static_cast<WatchpointState>((data & StateMask) >> StateShift);
+ }
+
+ static uintptr_t encodeState(WatchpointState state)
+ {
+ return (static_cast<uintptr_t>(state) << StateShift) | IsThinFlag;
+ }
+
+ 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);
+ }
+
+ WatchpointSet* fat()
+ {
+ ASSERT(isFat());
+ return fat(m_data);
+ }
+
+ const WatchpointSet* fat() const
+ {
+ ASSERT(isFat());
+ return fat(m_data);
+ }
+
+ WatchpointSet* inflate()
+ {
+ if (LIKELY(isFat()))
+ return fat();
+ return inflateSlow();
+ }
+
+ JS_EXPORT_PRIVATE WatchpointSet* inflateSlow();
+ JS_EXPORT_PRIVATE void freeFat();
+
+ uintptr_t m_data;
+};
+
+} // namespace JSC
+
+#endif // Watchpoint_h
+