summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/inspector/AsyncStackTrace.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/inspector/AsyncStackTrace.cpp')
-rw-r--r--Source/JavaScriptCore/inspector/AsyncStackTrace.cpp194
1 files changed, 194 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/inspector/AsyncStackTrace.cpp b/Source/JavaScriptCore/inspector/AsyncStackTrace.cpp
new file mode 100644
index 000000000..4002964ac
--- /dev/null
+++ b/Source/JavaScriptCore/inspector/AsyncStackTrace.cpp
@@ -0,0 +1,194 @@
+/*
+ * 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. AND ITS CONTRIBUTORS ``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 ITS 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 "AsyncStackTrace.h"
+
+#include "InspectorValues.h"
+#include "ScriptCallStack.h"
+
+namespace Inspector {
+
+RefPtr<AsyncStackTrace> AsyncStackTrace::create(Ref<ScriptCallStack>&& callStack, bool singleShot, RefPtr<AsyncStackTrace> parent)
+{
+ ASSERT(callStack->size());
+ return adoptRef(*new AsyncStackTrace(WTFMove(callStack), singleShot, WTFMove(parent)));
+}
+
+AsyncStackTrace::AsyncStackTrace(Ref<ScriptCallStack>&& callStack, bool singleShot, RefPtr<AsyncStackTrace> parent)
+ : m_callStack(WTFMove(callStack))
+ , m_parent(parent)
+ , m_singleShot(singleShot)
+{
+ if (m_parent)
+ m_parent->m_childCount++;
+}
+
+AsyncStackTrace::~AsyncStackTrace()
+{
+ if (m_parent)
+ remove();
+ ASSERT(!m_childCount);
+}
+
+bool AsyncStackTrace::isPending() const
+{
+ return m_state == State::Pending;
+}
+
+bool AsyncStackTrace::isLocked() const
+{
+ return m_state == State::Pending || m_state == State::Active || m_childCount > 1;
+}
+
+void AsyncStackTrace::willDispatchAsyncCall(size_t maxDepth)
+{
+ ASSERT(m_state == State::Pending);
+ m_state = State::Active;
+
+ truncate(maxDepth);
+}
+
+void AsyncStackTrace::didDispatchAsyncCall()
+{
+ ASSERT(m_state == State::Active || m_state == State::Canceled);
+
+ if (m_state == State::Active && !m_singleShot) {
+ m_state = State::Pending;
+ return;
+ }
+
+ m_state = State::Dispatched;
+
+ if (!m_childCount)
+ remove();
+}
+
+void AsyncStackTrace::didCancelAsyncCall()
+{
+ if (m_state == State::Canceled)
+ return;
+
+ if (m_state == State::Pending && !m_childCount)
+ remove();
+
+ m_state = State::Canceled;
+}
+
+RefPtr<Inspector::Protocol::Console::StackTrace> AsyncStackTrace::buildInspectorObject() const
+{
+ RefPtr<Inspector::Protocol::Console::StackTrace> topStackTrace;
+ RefPtr<Inspector::Protocol::Console::StackTrace> previousStackTrace;
+
+ auto* stackTrace = this;
+ while (stackTrace) {
+ auto& callStack = stackTrace->m_callStack;
+ ASSERT(callStack->size());
+
+ RefPtr<Inspector::Protocol::Console::StackTrace> protocolObject = Inspector::Protocol::Console::StackTrace::create()
+ .setCallFrames(callStack->buildInspectorArray())
+ .release();
+
+ if (stackTrace->m_truncated)
+ protocolObject->setTruncated(true);
+ if (callStack->at(0).isNative())
+ protocolObject->setTopCallFrameIsBoundary(true);
+
+ if (!topStackTrace)
+ topStackTrace = protocolObject;
+
+ if (previousStackTrace)
+ previousStackTrace->setParentStackTrace(protocolObject);
+
+ previousStackTrace = protocolObject;
+ stackTrace = stackTrace->m_parent.get();
+ }
+
+ return topStackTrace;
+}
+
+void AsyncStackTrace::truncate(size_t maxDepth)
+{
+ AsyncStackTrace* lastUnlockedAncestor = nullptr;
+ size_t depth = 0;
+
+ auto* newStackTraceRoot = this;
+ while (newStackTraceRoot) {
+ depth += newStackTraceRoot->m_callStack->size();
+ if (depth >= maxDepth)
+ break;
+
+ auto* parent = newStackTraceRoot->m_parent.get();
+ if (!lastUnlockedAncestor && parent && parent->isLocked())
+ lastUnlockedAncestor = newStackTraceRoot;
+
+ newStackTraceRoot = parent;
+ }
+
+ if (!newStackTraceRoot || !newStackTraceRoot->m_parent)
+ return;
+
+ if (!lastUnlockedAncestor) {
+ // No locked nodes belong to the trace. The subtree at the new root
+ // is moved to a new tree, and marked as truncated if necessary.
+ newStackTraceRoot->m_truncated = true;
+ newStackTraceRoot->remove();
+ return;
+ }
+
+ // The new root has a locked descendent. Since truncating a stack trace
+ // cannot mutate locked nodes or their ancestors, a new tree is created by
+ // cloning the locked portion of the trace (the path from the locked node
+ // to the new root). The subtree rooted at the last unlocked ancestor is
+ // then appended to the new tree.
+ auto* currentNode = lastUnlockedAncestor;
+ while (currentNode->m_parent) {
+ auto& parentNode = currentNode->m_parent;
+ currentNode->m_parent = AsyncStackTrace::create(parentNode->m_callStack.copyRef(), true, parentNode->m_parent);
+ currentNode = currentNode->m_parent.get();
+
+ if (parentNode.get() == newStackTraceRoot)
+ break;
+ }
+
+ currentNode->m_truncated = true;
+ currentNode->remove();
+
+ // Decrement the child count of the first locked ancestor after removing its subtree.
+ auto& firstLockedAncestor = lastUnlockedAncestor->m_parent;
+ firstLockedAncestor->m_childCount--;
+}
+
+void AsyncStackTrace::remove()
+{
+ if (!m_parent)
+ return;
+
+ ASSERT(m_parent->m_childCount);
+ m_parent->m_childCount--;
+ m_parent = nullptr;
+}
+
+} // namespace Inspector