/ bytecode / Watchpoint.cpp
Watchpoint.cpp
  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  #include "config.h"
 27  #include "Watchpoint.h"
 28  
 29  #include "AdaptiveInferredPropertyValueWatchpointBase.h"
 30  #include "CachedSpecialPropertyAdaptiveStructureWatchpoint.h"
 31  #include "CodeBlockJettisoningWatchpoint.h"
 32  #include "DFGAdaptiveStructureWatchpoint.h"
 33  #include "FunctionRareData.h"
 34  #include "HeapInlines.h"
 35  #include "LLIntPrototypeLoadAdaptiveStructureWatchpoint.h"
 36  #include "StructureStubClearingWatchpoint.h"
 37  #include "VM.h"
 38  
 39  namespace JSC {
 40  
 41  DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(Watchpoint);
 42  DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(WatchpointSet);
 43  
 44  void StringFireDetail::dump(PrintStream& out) const
 45  {
 46      out.print(m_string);
 47  }
 48  
 49  Watchpoint::~Watchpoint()
 50  {
 51      if (isOnList()) {
 52          // This will happen if we get destroyed before the set fires. That's totally a valid
 53          // possibility. For example:
 54          //
 55          // CodeBlock has a Watchpoint on transition from structure S1. The transition never
 56          // happens, but the CodeBlock gets destroyed because of GC.
 57          remove();
 58      }
 59  }
 60  
 61  void Watchpoint::fire(VM& vm, const FireDetail& detail)
 62  {
 63      RELEASE_ASSERT(!isOnList());
 64      switch (m_type) {
 65  #define JSC_DEFINE_WATCHPOINT_DISPATCH(type, cast) \
 66      case Type::type: \
 67          static_cast<cast*>(this)->fireInternal(vm, detail); \
 68          break;
 69      JSC_WATCHPOINT_TYPES(JSC_DEFINE_WATCHPOINT_DISPATCH)
 70  #undef JSC_DEFINE_WATCHPOINT_DISPATCH
 71      }
 72  }
 73  
 74  WatchpointSet::WatchpointSet(WatchpointState state)
 75      : m_state(state)
 76      , m_setIsNotEmpty(false)
 77  {
 78  }
 79  
 80  WatchpointSet::~WatchpointSet()
 81  {
 82      // Remove all watchpoints, so that they don't try to remove themselves. Note that we
 83      // don't fire watchpoints on deletion. We assume that any code that is interested in
 84      // watchpoints already also separately has a mechanism to make sure that the code is
 85      // either keeping the watchpoint set's owner alive, or does some weak reference thing.
 86      while (!m_set.isEmpty())
 87          m_set.begin()->remove();
 88  }
 89  
 90  void WatchpointSet::add(Watchpoint* watchpoint)
 91  {
 92      ASSERT(!isCompilationThread());
 93      ASSERT(state() != IsInvalidated);
 94      if (!watchpoint)
 95          return;
 96      m_set.push(watchpoint);
 97      m_setIsNotEmpty = true;
 98      m_state = IsWatched;
 99  }
100  
101  void WatchpointSet::fireAllSlow(VM& vm, const FireDetail& detail)
102  {
103      ASSERT(state() == IsWatched);
104      
105      WTF::storeStoreFence();
106      m_state = IsInvalidated; // Do this first. Needed for adaptive watchpoints.
107      fireAllWatchpoints(vm, detail);
108      WTF::storeStoreFence();
109  }
110  
111  void WatchpointSet::fireAllSlow(VM&, DeferredWatchpointFire* deferredWatchpoints)
112  {
113      ASSERT(state() == IsWatched);
114  
115      WTF::storeStoreFence();
116      deferredWatchpoints->takeWatchpointsToFire(this);
117      m_state = IsInvalidated; // Do after moving watchpoints to deferredWatchpoints so deferredWatchpoints gets our current state.
118      WTF::storeStoreFence();
119  }
120  
121  void WatchpointSet::fireAllSlow(VM& vm, const char* reason)
122  {
123      fireAllSlow(vm, StringFireDetail(reason));
124  }
125  
126  void WatchpointSet::fireAllWatchpoints(VM& vm, const FireDetail& detail)
127  {
128      // In case there are any adaptive watchpoints, we need to make sure that they see that this
129      // watchpoint has been already invalidated.
130      RELEASE_ASSERT(hasBeenInvalidated());
131  
132      // Firing a watchpoint may cause a GC to happen. This GC could destroy various
133      // Watchpoints themselves while they're in the process of firing. It's not safe
134      // for most Watchpoints to be destructed while they're in the middle of firing.
135      // This GC could also destroy us, and we're not in a safe state to be destroyed.
136      // The safest thing to do is to DeferGCForAWhile to prevent this GC from happening.
137      DeferGCForAWhile deferGC(vm.heap);
138      
139      while (!m_set.isEmpty()) {
140          Watchpoint* watchpoint = m_set.begin();
141          ASSERT(watchpoint->isOnList());
142          
143          // Removing the Watchpoint before firing it makes it possible to implement watchpoints
144          // that add themselves to a different set when they fire. This kind of "adaptive"
145          // watchpoint can be used to track some semantic property that is more fine-graiend than
146          // what the set can convey. For example, we might care if a singleton object ever has a
147          // property called "foo". We can watch for this by checking if its Structure has "foo" and
148          // then watching its transitions. But then the watchpoint fires if any property is added.
149          // So, before the watchpoint decides to invalidate any code, it can check if it is
150          // possible to add itself to the transition watchpoint set of the singleton object's new
151          // Structure.
152          watchpoint->remove();
153          ASSERT(m_set.begin() != watchpoint);
154          ASSERT(!watchpoint->isOnList());
155          
156          watchpoint->fire(vm, detail);
157          // After we fire the watchpoint, the watchpoint pointer may be a dangling pointer. That's
158          // fine, because we have no use for the pointer anymore.
159      }
160  }
161  
162  void WatchpointSet::take(WatchpointSet* other)
163  {
164      ASSERT(state() == ClearWatchpoint);
165      m_set.takeFrom(other->m_set);
166      m_setIsNotEmpty = other->m_setIsNotEmpty;
167      m_state = other->m_state;
168      other->m_setIsNotEmpty = false;
169  }
170  
171  void InlineWatchpointSet::add(Watchpoint* watchpoint)
172  {
173      inflate()->add(watchpoint);
174  }
175  
176  void InlineWatchpointSet::fireAll(VM& vm, const char* reason)
177  {
178      fireAll(vm, StringFireDetail(reason));
179  }
180  
181  WatchpointSet* InlineWatchpointSet::inflateSlow()
182  {
183      ASSERT(isThin());
184      ASSERT(!isCompilationThread());
185      WatchpointSet* fat = &WatchpointSet::create(decodeState(m_data)).leakRef();
186      WTF::storeStoreFence();
187      m_data = bitwise_cast<uintptr_t>(fat);
188      return fat;
189  }
190  
191  void InlineWatchpointSet::freeFat()
192  {
193      ASSERT(isFat());
194      fat()->deref();
195  }
196  
197  DeferredWatchpointFire::DeferredWatchpointFire(VM& vm)
198      : m_vm(vm)
199      , m_watchpointsToFire(ClearWatchpoint)
200  {
201  }
202  
203  DeferredWatchpointFire::~DeferredWatchpointFire()
204  {
205  }
206  
207  void DeferredWatchpointFire::fireAll()
208  {
209      if (m_watchpointsToFire.state() == IsWatched)
210          m_watchpointsToFire.fireAll(m_vm, *this);
211  }
212  
213  void DeferredWatchpointFire::takeWatchpointsToFire(WatchpointSet* watchpointsToFire)
214  {
215      ASSERT(m_watchpointsToFire.state() == ClearWatchpoint);
216      ASSERT(watchpointsToFire->state() == IsWatched);
217      m_watchpointsToFire.take(watchpointsToFire);
218  }
219  
220  } // namespace JSC
221  
222  namespace WTF {
223  
224  void printInternal(PrintStream& out, JSC::WatchpointState state)
225  {
226      switch (state) {
227      case JSC::ClearWatchpoint:
228          out.print("ClearWatchpoint");
229          return;
230      case JSC::IsWatched:
231          out.print("IsWatched");
232          return;
233      case JSC::IsInvalidated:
234          out.print("IsInvalidated");
235          return;
236      }
237      RELEASE_ASSERT_NOT_REACHED();
238  }
239  
240  } // namespace WTF
241