blob: 3d70df42aa33cb369364d93fdb4a2468f33cc30c [file] [log] [blame]
fpizlo@apple.comb75911b2012-06-13 20:53:52 +00001/*
fpizlo@apple.comd70351b2015-07-13 02:16:17 +00002 * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
fpizlo@apple.comb75911b2012-06-13 20:53:52 +00003 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
fpizlo@apple.com3a264a12012-07-11 23:33:20 +00007 * 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.
fpizlo@apple.comb75911b2012-06-13 20:53:52 +000012 *
fpizlo@apple.com3a264a12012-07-11 23:33:20 +000013 * 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.
fpizlo@apple.comb75911b2012-06-13 20:53:52 +000024 */
25
26#include "config.h"
27#include "Watchpoint.h"
28
ysuzuki@apple.comdafef972019-05-12 22:50:21 +000029#include "AdaptiveInferredPropertyValueWatchpointBase.h"
30#include "CodeBlockJettisoningWatchpoint.h"
31#include "DFGAdaptiveStructureWatchpoint.h"
32#include "FunctionRareData.h"
sbarati@apple.com0c3609d2016-06-28 21:30:20 +000033#include "HeapInlines.h"
ysuzuki@apple.comdafef972019-05-12 22:50:21 +000034#include "LLIntPrototypeLoadAdaptiveStructureWatchpoint.h"
35#include "ObjectToStringAdaptiveStructureWatchpoint.h"
36#include "StructureStubClearingWatchpoint.h"
sbarati@apple.com0c3609d2016-06-28 21:30:20 +000037#include "VM.h"
oliver@apple.com634a76a2013-07-25 03:59:09 +000038#include <wtf/CompilationThread.h>
fpizlo@apple.comb75911b2012-06-13 20:53:52 +000039
40namespace JSC {
41
fpizlo@apple.com2c4a7e92014-08-06 05:27:46 +000042void StringFireDetail::dump(PrintStream& out) const
43{
44 out.print(m_string);
45}
46
fpizlo@apple.comb75911b2012-06-13 20:53:52 +000047Watchpoint::~Watchpoint()
48{
fpizlo@apple.comd70351b2015-07-13 02:16:17 +000049 if (isOnList()) {
50 // This will happen if we get destroyed before the set fires. That's totally a valid
51 // possibility. For example:
52 //
53 // CodeBlock has a Watchpoint on transition from structure S1. The transition never
54 // happens, but the CodeBlock gets destroyed because of GC.
fpizlo@apple.comb75911b2012-06-13 20:53:52 +000055 remove();
fpizlo@apple.comd70351b2015-07-13 02:16:17 +000056 }
57}
58
utatane.tea@gmail.com7cb46142018-06-27 05:01:29 +000059void Watchpoint::fire(VM& vm, const FireDetail& detail)
fpizlo@apple.comd70351b2015-07-13 02:16:17 +000060{
61 RELEASE_ASSERT(!isOnList());
ysuzuki@apple.comdafef972019-05-12 22:50:21 +000062 switch (m_type) {
63#define JSC_DEFINE_WATCHPOINT_DISPATCH(type, cast) \
64 case Type::type: \
65 static_cast<cast*>(this)->fireInternal(vm, detail); \
66 break;
67 JSC_WATCHPOINT_TYPES(JSC_DEFINE_WATCHPOINT_DISPATCH)
68#undef JSC_DEFINE_WATCHPOINT_DISPATCH
69 }
fpizlo@apple.comb75911b2012-06-13 20:53:52 +000070}
71
fpizlo@apple.com09a6af02013-11-18 02:10:42 +000072WatchpointSet::WatchpointSet(WatchpointState state)
73 : m_state(state)
fpizlo@apple.com33961712013-11-20 05:49:05 +000074 , m_setIsNotEmpty(false)
fpizlo@apple.comb75911b2012-06-13 20:53:52 +000075{
76}
77
78WatchpointSet::~WatchpointSet()
79{
fpizlo@apple.com4917df22013-10-31 03:52:23 +000080 // Remove all watchpoints, so that they don't try to remove themselves. Note that we
81 // don't fire watchpoints on deletion. We assume that any code that is interested in
82 // watchpoints already also separately has a mechanism to make sure that the code is
83 // either keeping the watchpoint set's owner alive, or does some weak reference thing.
84 while (!m_set.isEmpty())
85 m_set.begin()->remove();
fpizlo@apple.comb75911b2012-06-13 20:53:52 +000086}
87
88void WatchpointSet::add(Watchpoint* watchpoint)
89{
oliver@apple.com634a76a2013-07-25 03:59:09 +000090 ASSERT(!isCompilationThread());
fpizlo@apple.com09a6af02013-11-18 02:10:42 +000091 ASSERT(state() != IsInvalidated);
fpizlo@apple.comb75911b2012-06-13 20:53:52 +000092 if (!watchpoint)
93 return;
94 m_set.push(watchpoint);
fpizlo@apple.com33961712013-11-20 05:49:05 +000095 m_setIsNotEmpty = true;
fpizlo@apple.com09a6af02013-11-18 02:10:42 +000096 m_state = IsWatched;
fpizlo@apple.comb75911b2012-06-13 20:53:52 +000097}
98
sbarati@apple.com0c3609d2016-06-28 21:30:20 +000099void WatchpointSet::fireAllSlow(VM& vm, const FireDetail& detail)
fpizlo@apple.comb75911b2012-06-13 20:53:52 +0000100{
fpizlo@apple.com09a6af02013-11-18 02:10:42 +0000101 ASSERT(state() == IsWatched);
fpizlo@apple.comb75911b2012-06-13 20:53:52 +0000102
fpizlo@apple.com33961712013-11-20 05:49:05 +0000103 WTF::storeStoreFence();
fpizlo@apple.comf3ea68482015-07-13 20:10:02 +0000104 m_state = IsInvalidated; // Do this first. Needed for adaptive watchpoints.
sbarati@apple.com0c3609d2016-06-28 21:30:20 +0000105 fireAllWatchpoints(vm, detail);
oliver@apple.com9055d142013-07-25 03:59:02 +0000106 WTF::storeStoreFence();
fpizlo@apple.comb75911b2012-06-13 20:53:52 +0000107}
108
msaboff@apple.comfbf2bf52018-05-08 23:20:33 +0000109void WatchpointSet::fireAllSlow(VM&, DeferredWatchpointFire* deferredWatchpoints)
110{
111 ASSERT(state() == IsWatched);
112
113 WTF::storeStoreFence();
114 deferredWatchpoints->takeWatchpointsToFire(this);
115 m_state = IsInvalidated; // Do after moving watchpoints to deferredWatchpoints so deferredWatchpoints gets our current state.
116 WTF::storeStoreFence();
117}
118
sbarati@apple.com0c3609d2016-06-28 21:30:20 +0000119void WatchpointSet::fireAllSlow(VM& vm, const char* reason)
fpizlo@apple.com2c4a7e92014-08-06 05:27:46 +0000120{
sbarati@apple.com0c3609d2016-06-28 21:30:20 +0000121 fireAllSlow(vm, StringFireDetail(reason));
fpizlo@apple.com2c4a7e92014-08-06 05:27:46 +0000122}
123
sbarati@apple.com0c3609d2016-06-28 21:30:20 +0000124void WatchpointSet::fireAllWatchpoints(VM& vm, const FireDetail& detail)
fpizlo@apple.comb75911b2012-06-13 20:53:52 +0000125{
fpizlo@apple.comf3ea68482015-07-13 20:10:02 +0000126 // In case there are any adaptive watchpoints, we need to make sure that they see that this
127 // watchpoint has been already invalidated.
128 RELEASE_ASSERT(hasBeenInvalidated());
sbarati@apple.com0c3609d2016-06-28 21:30:20 +0000129
130 // Firing a watchpoint may cause a GC to happen. This GC could destroy various
131 // Watchpoints themselves while they're in the process of firing. It's not safe
132 // for most Watchpoints to be destructed while they're in the middle of firing.
133 // This GC could also destroy us, and we're not in a safe state to be destroyed.
134 // The safest thing to do is to DeferGCForAWhile to prevent this GC from happening.
135 DeferGCForAWhile deferGC(vm.heap);
fpizlo@apple.comf3ea68482015-07-13 20:10:02 +0000136
fpizlo@apple.comd70351b2015-07-13 02:16:17 +0000137 while (!m_set.isEmpty()) {
138 Watchpoint* watchpoint = m_set.begin();
139 ASSERT(watchpoint->isOnList());
140
141 // Removing the Watchpoint before firing it makes it possible to implement watchpoints
142 // that add themselves to a different set when they fire. This kind of "adaptive"
143 // watchpoint can be used to track some semantic property that is more fine-graiend than
144 // what the set can convey. For example, we might care if a singleton object ever has a
145 // property called "foo". We can watch for this by checking if its Structure has "foo" and
146 // then watching its transitions. But then the watchpoint fires if any property is added.
147 // So, before the watchpoint decides to invalidate any code, it can check if it is
148 // possible to add itself to the transition watchpoint set of the singleton object's new
149 // Structure.
150 watchpoint->remove();
151 ASSERT(m_set.begin() != watchpoint);
152 ASSERT(!watchpoint->isOnList());
153
utatane.tea@gmail.com7cb46142018-06-27 05:01:29 +0000154 watchpoint->fire(vm, detail);
fpizlo@apple.comd70351b2015-07-13 02:16:17 +0000155 // After we fire the watchpoint, the watchpoint pointer may be a dangling pointer. That's
156 // fine, because we have no use for the pointer anymore.
157 }
fpizlo@apple.comb75911b2012-06-13 20:53:52 +0000158}
159
msaboff@apple.comfbf2bf52018-05-08 23:20:33 +0000160void WatchpointSet::take(WatchpointSet* other)
161{
162 ASSERT(state() == ClearWatchpoint);
163 m_set.takeFrom(other->m_set);
164 m_setIsNotEmpty = other->m_setIsNotEmpty;
165 m_state = other->m_state;
166 other->m_setIsNotEmpty = false;
167}
168
fpizlo@apple.com04e41152012-06-15 22:14:53 +0000169void InlineWatchpointSet::add(Watchpoint* watchpoint)
170{
171 inflate()->add(watchpoint);
172}
173
sbarati@apple.com0c3609d2016-06-28 21:30:20 +0000174void InlineWatchpointSet::fireAll(VM& vm, const char* reason)
fpizlo@apple.com2c4a7e92014-08-06 05:27:46 +0000175{
sbarati@apple.com0c3609d2016-06-28 21:30:20 +0000176 fireAll(vm, StringFireDetail(reason));
fpizlo@apple.com2c4a7e92014-08-06 05:27:46 +0000177}
178
fpizlo@apple.com04e41152012-06-15 22:14:53 +0000179WatchpointSet* InlineWatchpointSet::inflateSlow()
180{
181 ASSERT(isThin());
oliver@apple.com634a76a2013-07-25 03:59:09 +0000182 ASSERT(!isCompilationThread());
fpizlo@apple.com09a6af02013-11-18 02:10:42 +0000183 WatchpointSet* fat = adoptRef(new WatchpointSet(decodeState(m_data))).leakRef();
oliver@apple.com9397e002013-07-25 03:58:49 +0000184 WTF::storeStoreFence();
fpizlo@apple.com04e41152012-06-15 22:14:53 +0000185 m_data = bitwise_cast<uintptr_t>(fat);
186 return fat;
187}
188
189void InlineWatchpointSet::freeFat()
190{
191 ASSERT(isFat());
192 fat()->deref();
193}
194
msaboff@apple.comfbf2bf52018-05-08 23:20:33 +0000195DeferredWatchpointFire::DeferredWatchpointFire(VM& vm)
196 : m_vm(vm)
197 , m_watchpointsToFire(ClearWatchpoint)
198{
199}
200
201DeferredWatchpointFire::~DeferredWatchpointFire()
202{
203}
204
205void DeferredWatchpointFire::fireAll()
206{
207 if (m_watchpointsToFire.state() == IsWatched)
208 m_watchpointsToFire.fireAll(m_vm, *this);
209}
210
211void DeferredWatchpointFire::takeWatchpointsToFire(WatchpointSet* watchpointsToFire)
212{
213 ASSERT(m_watchpointsToFire.state() == ClearWatchpoint);
214 ASSERT(watchpointsToFire->state() == IsWatched);
215 m_watchpointsToFire.take(watchpointsToFire);
216}
217
fpizlo@apple.comb75911b2012-06-13 20:53:52 +0000218} // namespace JSC
219
fpizlo@apple.comed2da802018-07-22 02:48:16 +0000220namespace WTF {
221
222void printInternal(PrintStream& out, JSC::WatchpointState state)
223{
224 switch (state) {
225 case JSC::ClearWatchpoint:
226 out.print("ClearWatchpoint");
227 return;
228 case JSC::IsWatched:
229 out.print("IsWatched");
230 return;
231 case JSC::IsInvalidated:
232 out.print("IsInvalidated");
233 return;
234 }
235 RELEASE_ASSERT_NOT_REACHED();
236}
237
238} // namespace WTF
239