summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/tools/VMInspector.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/tools/VMInspector.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/tools/VMInspector.cpp')
-rw-r--r--Source/JavaScriptCore/tools/VMInspector.cpp195
1 files changed, 195 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/tools/VMInspector.cpp b/Source/JavaScriptCore/tools/VMInspector.cpp
new file mode 100644
index 000000000..ef44ecca7
--- /dev/null
+++ b/Source/JavaScriptCore/tools/VMInspector.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "config.h"
+#include "VMInspector.h"
+
+#include "CodeBlock.h"
+#include "CodeBlockSet.h"
+#include "HeapInlines.h"
+#include <mutex>
+#include <wtf/Expected.h>
+
+#if !OS(WINDOWS)
+#include <unistd.h>
+#endif
+
+namespace JSC {
+
+VMInspector& VMInspector::instance()
+{
+ static VMInspector* manager;
+ static std::once_flag once;
+ std::call_once(once, [] {
+ manager = new VMInspector();
+ });
+ return *manager;
+}
+
+void VMInspector::add(VM* vm)
+{
+ auto locker = holdLock(m_lock);
+ m_list.append(vm);
+}
+
+void VMInspector::remove(VM* vm)
+{
+ auto locker = holdLock(m_lock);
+ m_list.remove(vm);
+}
+
+auto VMInspector::lock(Seconds timeout) -> Expected<Locker, Error>
+{
+ // This function may be called from a signal handler (e.g. via visit()). Hence,
+ // it should only use APIs that are safe to call from signal handlers. This is
+ // why we use unistd.h's sleep() instead of its alternatives.
+
+ // We'll be doing sleep(1) between tries below. Hence, sleepPerRetry is 1.
+ unsigned maxRetries = (timeout < Seconds::infinity()) ? timeout.value() : UINT_MAX;
+
+ Expected<Locker, Error> locker = Locker::tryLock(m_lock);
+ unsigned tryCount = 0;
+ while (!locker && tryCount < maxRetries) {
+ // We want the version of sleep from unistd.h. Cast to disambiguate.
+#if !OS(WINDOWS)
+ (static_cast<unsigned (*)(unsigned)>(sleep))(1);
+#endif
+ locker = Locker::tryLock(m_lock);
+ }
+
+ if (!locker)
+ return makeUnexpected(Error::TimedOut);
+ return locker;
+}
+
+#if ENABLE(JIT)
+static bool ensureIsSafeToLock(Lock& lock)
+{
+ unsigned maxRetries = 2;
+ unsigned tryCount = 0;
+ while (tryCount <= maxRetries) {
+ bool success = lock.tryLock();
+ if (success) {
+ lock.unlock();
+ return true;
+ }
+ tryCount++;
+ }
+ return false;
+};
+#endif // ENABLE(JIT)
+
+auto VMInspector::isValidExecutableMemory(const VMInspector::Locker&, void* machinePC) -> Expected<bool, Error>
+{
+#if ENABLE(JIT)
+ bool found = false;
+ bool hasTimeout = false;
+ iterate([&] (VM& vm) -> FunctorStatus {
+ auto allocator = vm.executableAllocator;
+ auto& lock = allocator.getLock();
+
+ bool isSafeToLock = ensureIsSafeToLock(lock);
+ if (!isSafeToLock) {
+ hasTimeout = true;
+ return FunctorStatus::Continue; // Skip this VM.
+ }
+
+ LockHolder executableAllocatorLocker(lock);
+ if (allocator.isValidExecutableMemory(executableAllocatorLocker, machinePC)) {
+ found = true;
+ return FunctorStatus::Done;
+ }
+ return FunctorStatus::Continue;
+ });
+
+ if (!found && hasTimeout)
+ return makeUnexpected(Error::TimedOut);
+ return found;
+#else
+ UNUSED_PARAM(machinePC);
+ return false;
+#endif
+}
+
+auto VMInspector::codeBlockForMachinePC(const VMInspector::Locker&, void* machinePC) -> Expected<CodeBlock*, Error>
+{
+#if ENABLE(JIT)
+ CodeBlock* codeBlock = nullptr;
+ bool hasTimeout = false;
+ iterate([&] (VM& vm) {
+ if (!vm.apiLock().currentThreadIsHoldingLock())
+ return FunctorStatus::Continue;
+
+ // It is safe to call Heap::forEachCodeBlockIgnoringJITPlans here because:
+ // 1. CodeBlocks are added to the CodeBlockSet from the main thread before
+ // they are handed to the JIT plans. Those codeBlocks will have a null jitCode,
+ // but we check for that in our lambda functor.
+ // 2. CodeBlockSet::iterate() will acquire the CodeBlockSet lock before iterating.
+ // This ensures that a CodeBlock won't be GCed while we're iterating.
+ // 3. We do a tryLock on the CodeBlockSet's lock first to ensure that it is
+ // safe for the current thread to lock it before calling
+ // Heap::forEachCodeBlockIgnoringJITPlans(). Hence, there's no risk of
+ // re-entering the lock and deadlocking on it.
+
+ auto& lock = vm.heap.codeBlockSet().getLock();
+ bool isSafeToLock = ensureIsSafeToLock(lock);
+ if (!isSafeToLock) {
+ hasTimeout = true;
+ return FunctorStatus::Continue; // Skip this VM.
+ }
+
+ vm.heap.forEachCodeBlockIgnoringJITPlans([&] (CodeBlock* cb) {
+ JITCode* jitCode = cb->jitCode().get();
+ if (!jitCode) {
+ // If the codeBlock is a replacement codeBlock which is in the process of being
+ // compiled, its jitCode will be null, and we can disregard it as a match for
+ // the machinePC we're searching for.
+ return false;
+ }
+
+ if (!JITCode::isJIT(jitCode->jitType()))
+ return false;
+
+ if (jitCode->contains(machinePC)) {
+ codeBlock = cb;
+ return true;
+ }
+ return false;
+ });
+ if (codeBlock)
+ return FunctorStatus::Done;
+ return FunctorStatus::Continue;
+ });
+
+ if (!codeBlock && hasTimeout)
+ return makeUnexpected(Error::TimedOut);
+ return codeBlock;
+#else
+ UNUSED_PARAM(machinePC);
+ return nullptr;
+#endif
+}
+
+} // namespace JSC