CodeBlocks should be in IsoSubspaces
[WebKit-https.git] / Source / JavaScriptCore / tools / VMInspector.cpp
1 /*
2  * Copyright (C) 2017 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 "VMInspector.h"
28
29 #include "CodeBlock.h"
30 #include "CodeBlockSet.h"
31 #include "HeapInlines.h"
32 #include "HeapIterationScope.h"
33 #include "MachineContext.h"
34 #include "MarkedSpaceInlines.h"
35 #include "StackVisitor.h"
36 #include <mutex>
37 #include <wtf/Expected.h>
38
39 #if !OS(WINDOWS)
40 #include <unistd.h>
41 #endif
42
43 namespace JSC {
44
45 VMInspector& VMInspector::instance()
46 {
47     static VMInspector* manager;
48     static std::once_flag once;
49     std::call_once(once, [] {
50         manager = new VMInspector();
51     });
52     return *manager;
53 }
54
55 void VMInspector::add(VM* vm)
56 {
57     auto locker = holdLock(m_lock);
58     m_list.append(vm);
59 }
60
61 void VMInspector::remove(VM* vm)
62 {
63     auto locker = holdLock(m_lock);
64     m_list.remove(vm);
65 }
66
67 auto VMInspector::lock(Seconds timeout) -> Expected<Locker, Error>
68 {
69     // This function may be called from a signal handler (e.g. via visit()). Hence,
70     // it should only use APIs that are safe to call from signal handlers. This is
71     // why we use unistd.h's sleep() instead of its alternatives.
72
73     // We'll be doing sleep(1) between tries below. Hence, sleepPerRetry is 1.
74     unsigned maxRetries = (timeout < Seconds::infinity()) ? timeout.value() : UINT_MAX;
75
76     Expected<Locker, Error> locker = Locker::tryLock(m_lock);
77     unsigned tryCount = 0;
78     while (!locker && tryCount < maxRetries) {
79         // We want the version of sleep from unistd.h. Cast to disambiguate.
80 #if !OS(WINDOWS)
81         (static_cast<unsigned (*)(unsigned)>(sleep))(1);
82 #endif
83         locker = Locker::tryLock(m_lock);
84     }
85
86     if (!locker)
87         return makeUnexpected(Error::TimedOut);
88     return locker;
89 }
90
91 #if ENABLE(JIT)
92 static bool ensureIsSafeToLock(Lock& lock)
93 {
94     unsigned maxRetries = 2;
95     unsigned tryCount = 0;
96     while (tryCount <= maxRetries) {
97         bool success = lock.tryLock();
98         if (success) {
99             lock.unlock();
100             return true;
101         }
102         tryCount++;
103     }
104     return false;
105 };
106 #endif // ENABLE(JIT)
107
108 auto VMInspector::isValidExecutableMemory(const VMInspector::Locker&, void* machinePC) -> Expected<bool, Error>
109 {
110 #if ENABLE(JIT)
111     bool found = false;
112     bool hasTimeout = false;
113     iterate([&] (VM&) -> FunctorStatus {
114         auto& allocator = ExecutableAllocator::singleton();
115         auto& lock = allocator.getLock();
116
117         bool isSafeToLock = ensureIsSafeToLock(lock);
118         if (!isSafeToLock) {
119             hasTimeout = true;
120             return FunctorStatus::Continue; // Skip this VM.
121         }
122
123         LockHolder executableAllocatorLocker(lock);
124         if (allocator.isValidExecutableMemory(executableAllocatorLocker, machinePC)) {
125             found = true;
126             return FunctorStatus::Done;
127         }
128         return FunctorStatus::Continue;
129     });
130
131     if (!found && hasTimeout)
132         return makeUnexpected(Error::TimedOut);
133     return found;
134 #else
135     UNUSED_PARAM(machinePC);
136     return false;
137 #endif
138 }
139
140 auto VMInspector::codeBlockForMachinePC(const VMInspector::Locker&, void* machinePC) -> Expected<CodeBlock*, Error>
141 {
142 #if ENABLE(JIT)
143     CodeBlock* codeBlock = nullptr;
144     bool hasTimeout = false;
145     iterate([&] (VM& vm) {
146         if (!vm.currentThreadIsHoldingAPILock())
147             return FunctorStatus::Continue;
148
149         // It is safe to call Heap::forEachCodeBlockIgnoringJITPlans here because:
150         // 1. CodeBlocks are added to the CodeBlockSet from the main thread before
151         //    they are handed to the JIT plans. Those codeBlocks will have a null jitCode,
152         //    but we check for that in our lambda functor.
153         // 2. We will acquire the CodeBlockSet lock before iterating.
154         //    This ensures that a CodeBlock won't be GCed while we're iterating.
155         // 3. We do a tryLock on the CodeBlockSet's lock first to ensure that it is
156         //    safe for the current thread to lock it before calling
157         //    Heap::forEachCodeBlockIgnoringJITPlans(). Hence, there's no risk of
158         //    re-entering the lock and deadlocking on it.
159
160         auto& codeBlockSetLock = vm.heap.codeBlockSet().getLock();
161         bool isSafeToLock = ensureIsSafeToLock(codeBlockSetLock);
162         if (!isSafeToLock) {
163             hasTimeout = true;
164             return FunctorStatus::Continue; // Skip this VM.
165         }
166
167         auto locker = holdLock(codeBlockSetLock);
168         vm.heap.forEachCodeBlockIgnoringJITPlans(locker, [&] (CodeBlock* cb) {
169             JITCode* jitCode = cb->jitCode().get();
170             if (!jitCode) {
171                 // If the codeBlock is a replacement codeBlock which is in the process of being
172                 // compiled, its jitCode will be null, and we can disregard it as a match for
173                 // the machinePC we're searching for.
174                 return;
175             }
176
177             if (!JITCode::isJIT(jitCode->jitType()))
178                 return;
179
180             if (jitCode->contains(machinePC)) {
181                 codeBlock = cb;
182                 return;
183             }
184         });
185         if (codeBlock)
186             return FunctorStatus::Done;
187         return FunctorStatus::Continue;
188     });
189
190     if (!codeBlock && hasTimeout)
191         return makeUnexpected(Error::TimedOut);
192     return codeBlock;
193 #else
194     UNUSED_PARAM(machinePC);
195     return nullptr;
196 #endif
197 }
198
199 bool VMInspector::currentThreadOwnsJSLock(ExecState* exec)
200 {
201     return exec->vm().currentThreadIsHoldingAPILock();
202 }
203
204 static bool ensureCurrentThreadOwnsJSLock(ExecState* exec)
205 {
206     if (VMInspector::currentThreadOwnsJSLock(exec))
207         return true;
208     dataLog("ERROR: current thread does not own the JSLock\n");
209     return false;
210 }
211
212 void VMInspector::gc(ExecState* exec)
213 {
214     VM& vm = exec->vm();
215     if (!ensureCurrentThreadOwnsJSLock(exec))
216         return;
217     vm.heap.collectNow(Sync, CollectionScope::Full);
218 }
219
220 void VMInspector::edenGC(ExecState* exec)
221 {
222     VM& vm = exec->vm();
223     if (!ensureCurrentThreadOwnsJSLock(exec))
224         return;
225     vm.heap.collectSync(CollectionScope::Eden);
226 }
227
228 bool VMInspector::isInHeap(Heap* heap, void* ptr)
229 {
230     MarkedBlock* candidate = MarkedBlock::blockFor(ptr);
231     if (heap->objectSpace().blocks().set().contains(candidate))
232         return true;
233     for (LargeAllocation* allocation : heap->objectSpace().largeAllocations()) {
234         if (allocation->contains(ptr))
235             return true;
236     }
237     return false;
238 }
239
240 struct CellAddressCheckFunctor : MarkedBlock::CountFunctor {
241     CellAddressCheckFunctor(JSCell* candidate)
242         : candidate(candidate)
243     {
244     }
245
246     IterationStatus operator()(HeapCell* cell, HeapCell::Kind) const
247     {
248         if (cell == candidate) {
249             found = true;
250             return IterationStatus::Done;
251         }
252         return IterationStatus::Continue;
253     }
254
255     JSCell* candidate;
256     mutable bool found { false };
257 };
258
259 bool VMInspector::isValidCell(Heap* heap, JSCell* candidate)
260 {
261     HeapIterationScope iterationScope(*heap);
262     CellAddressCheckFunctor functor(candidate);
263     heap->objectSpace().forEachLiveCell(iterationScope, functor);
264     return functor.found;
265 }
266
267 bool VMInspector::isValidCodeBlock(ExecState* exec, CodeBlock* candidate)
268 {
269     if (!ensureCurrentThreadOwnsJSLock(exec))
270         return false;
271
272     struct CodeBlockValidationFunctor {
273         CodeBlockValidationFunctor(CodeBlock* candidate)
274             : candidate(candidate)
275         {
276         }
277
278         void operator()(CodeBlock* codeBlock) const
279         {
280             if (codeBlock == candidate)
281                 found = true;
282         }
283
284         CodeBlock* candidate;
285         mutable bool found { false };
286     };
287
288     VM& vm = exec->vm();
289     CodeBlockValidationFunctor functor(candidate);
290     vm.heap.forEachCodeBlock(functor);
291     return functor.found;
292 }
293
294 CodeBlock* VMInspector::codeBlockForFrame(CallFrame* topCallFrame, unsigned frameNumber)
295 {
296     if (!ensureCurrentThreadOwnsJSLock(topCallFrame))
297         return nullptr;
298
299     if (!topCallFrame)
300         return nullptr;
301
302     struct FetchCodeBlockFunctor {
303     public:
304         FetchCodeBlockFunctor(unsigned targetFrameNumber)
305             : targetFrame(targetFrameNumber)
306         {
307         }
308
309         StackVisitor::Status operator()(StackVisitor& visitor) const
310         {
311             auto currentFrame = nextFrame++;
312             if (currentFrame == targetFrame) {
313                 codeBlock = visitor->codeBlock();
314                 return StackVisitor::Done;
315             }
316             return StackVisitor::Continue;
317         }
318
319         unsigned targetFrame;
320         mutable unsigned nextFrame { 0 };
321         mutable CodeBlock* codeBlock { nullptr };
322     };
323
324     FetchCodeBlockFunctor functor(frameNumber);
325     topCallFrame->iterate(functor);
326     return functor.codeBlock;
327 }
328
329 class PrintFrameFunctor {
330 public:
331     enum Action {
332         PrintOne,
333         PrintAll
334     };
335
336     PrintFrameFunctor(Action action, unsigned framesToSkip)
337         : m_action(action)
338         , m_framesToSkip(framesToSkip)
339     {
340     }
341
342     StackVisitor::Status operator()(StackVisitor& visitor) const
343     {
344         m_currentFrame++;
345         if (m_currentFrame > m_framesToSkip) {
346             visitor->dump(WTF::dataFile(), Indenter(2), [&] (PrintStream& out) {
347                 out.print("[", (m_currentFrame - m_framesToSkip - 1), "] ");
348             });
349         }
350         if (m_action == PrintOne && m_currentFrame > m_framesToSkip)
351             return StackVisitor::Done;
352         return StackVisitor::Continue;
353     }
354
355 private:
356     Action m_action;
357     unsigned m_framesToSkip;
358     mutable unsigned m_currentFrame { 0 };
359 };
360
361 void VMInspector::printCallFrame(CallFrame* callFrame, unsigned framesToSkip)
362 {
363     if (!ensureCurrentThreadOwnsJSLock(callFrame))
364         return;
365     PrintFrameFunctor functor(PrintFrameFunctor::PrintOne, framesToSkip);
366     callFrame->iterate(functor);
367 }
368
369 void VMInspector::printStack(CallFrame* topCallFrame, unsigned framesToSkip)
370 {
371     if (!ensureCurrentThreadOwnsJSLock(topCallFrame))
372         return;
373     if (!topCallFrame)
374         return;
375     PrintFrameFunctor functor(PrintFrameFunctor::PrintAll, framesToSkip);
376     topCallFrame->iterate(functor);
377 }
378
379 void VMInspector::printValue(JSValue value)
380 {
381     dataLog(value);
382 }
383
384 } // namespace JSC