summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/tools
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
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/tools')
-rw-r--r--Source/JavaScriptCore/tools/CodeProfile.cpp5
-rw-r--r--Source/JavaScriptCore/tools/CodeProfile.h20
-rw-r--r--Source/JavaScriptCore/tools/CodeProfiling.cpp12
-rw-r--r--Source/JavaScriptCore/tools/CodeProfiling.h8
-rw-r--r--Source/JavaScriptCore/tools/FunctionOverrides.cpp266
-rw-r--r--Source/JavaScriptCore/tools/FunctionOverrides.h64
-rw-r--r--Source/JavaScriptCore/tools/FunctionWhitelist.cpp99
-rw-r--r--Source/JavaScriptCore/tools/FunctionWhitelist.h46
-rw-r--r--Source/JavaScriptCore/tools/JSDollarVM.cpp35
-rw-r--r--Source/JavaScriptCore/tools/JSDollarVM.h57
-rw-r--r--Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp477
-rw-r--r--Source/JavaScriptCore/tools/JSDollarVMPrototype.h78
-rw-r--r--Source/JavaScriptCore/tools/ProfileTreeNode.h12
-rw-r--r--Source/JavaScriptCore/tools/SigillCrashAnalyzer.cpp396
-rw-r--r--Source/JavaScriptCore/tools/SigillCrashAnalyzer.h33
-rw-r--r--Source/JavaScriptCore/tools/TieredMMapArray.h8
-rw-r--r--Source/JavaScriptCore/tools/VMInspector.cpp195
-rw-r--r--Source/JavaScriptCore/tools/VMInspector.h72
18 files changed, 1840 insertions, 43 deletions
diff --git a/Source/JavaScriptCore/tools/CodeProfile.cpp b/Source/JavaScriptCore/tools/CodeProfile.cpp
index b99df2c66..c8d580aa1 100644
--- a/Source/JavaScriptCore/tools/CodeProfile.cpp
+++ b/Source/JavaScriptCore/tools/CodeProfile.cpp
@@ -30,7 +30,6 @@
#include "CodeProfiling.h"
#include "LinkBuffer.h"
#include "ProfileTreeNode.h"
-#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
#if OS(DARWIN)
@@ -143,7 +142,7 @@ void CodeProfile::sample(void* pc, void** framePointer)
void CodeProfile::report()
{
- dataLogF("<CodeProfiling %s:%d>\n", m_file.data(), m_lineNo);
+ dataLogF("<CodeProfiling %s:%d>\n", m_file.data(), m_lineNumber);
// How many frames of C-code to print - 0, if not verbose, 1 if verbose, up to 1024 if very verbose.
unsigned recursionLimit = CodeProfiling::beVeryVerbose() ? 1024 : CodeProfiling::beVerbose();
@@ -186,7 +185,7 @@ void CodeProfile::report()
for (size_t i = 0 ; i < m_children.size(); ++i)
m_children[i]->report();
- dataLogF("</CodeProfiling %s:%d>\n", m_file.data(), m_lineNo);
+ dataLogF("</CodeProfiling %s:%d>\n", m_file.data(), m_lineNumber);
}
}
diff --git a/Source/JavaScriptCore/tools/CodeProfile.h b/Source/JavaScriptCore/tools/CodeProfile.h
index 6b0a30689..4fa357d50 100644
--- a/Source/JavaScriptCore/tools/CodeProfile.h
+++ b/Source/JavaScriptCore/tools/CodeProfile.h
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CodeProfile_h
-#define CodeProfile_h
+#pragma once
#include "SourceCode.h"
#include "TieredMMapArray.h"
@@ -37,11 +36,11 @@ class CodeProfile {
public:
CodeProfile(const SourceCode& source, CodeProfile* parent)
: m_file(source.provider()->url().utf8())
- , m_lineNo(source.firstLine())
+ , m_lineNumber(source.firstLine().oneBasedInt())
, m_parent(parent)
{
if (parent)
- parent->addChild(this);
+ parent->addChild(std::unique_ptr<CodeProfile>(this));
}
void sample(void* pc, void** framePointer);
@@ -52,9 +51,9 @@ public:
return m_parent;
}
- void addChild(CodeProfile* child)
+ void addChild(std::unique_ptr<CodeProfile> child)
{
- m_children.append(adoptPtr(child));
+ m_children.append(WTFMove(child));
}
private:
@@ -80,15 +79,12 @@ private:
};
CString m_file;
- unsigned m_lineNo;
+ unsigned m_lineNumber;
CodeProfile* m_parent;
- Vector< OwnPtr<CodeProfile>> m_children;
+ Vector<std::unique_ptr<CodeProfile>> m_children;
TieredMMapArray<CodeRecord> m_samples;
static const char* s_codeTypeNames[NumberOfCodeTypes];
};
-}
-
-#endif // CodeProfile_h
-
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/CodeProfiling.cpp b/Source/JavaScriptCore/tools/CodeProfiling.cpp
index 740595e3e..f916c203b 100644
--- a/Source/JavaScriptCore/tools/CodeProfiling.cpp
+++ b/Source/JavaScriptCore/tools/CodeProfiling.cpp
@@ -33,7 +33,7 @@
#include <signal.h>
#endif
-#if OS(LINUX)
+#if OS(LINUX) || OS(DARWIN)
#include <sys/time.h>
#endif
@@ -48,7 +48,7 @@ WTF::MetaAllocatorTracker* CodeProfiling::s_tracker = 0;
#pragma clang diagnostic ignored "-Wmissing-noreturn"
#endif
-#if (PLATFORM(MAC) && CPU(X86_64)) || (OS(LINUX) && CPU(X86))
+#if (OS(DARWIN) && !PLATFORM(GTK) && CPU(X86_64)) || (OS(LINUX) && CPU(X86))
// Helper function to start & stop the timer.
// Presently we're using the wall-clock timer, since this seems to give the best results.
static void setProfileTimer(unsigned usec)
@@ -66,7 +66,7 @@ static void setProfileTimer(unsigned usec)
#pragma clang diagnostic pop
#endif
-#if PLATFORM(MAC) && CPU(X86_64)
+#if OS(DARWIN) && !PLATFORM(GTK) && CPU(X86_64)
static void profilingTimer(int, siginfo_t*, void* uap)
{
mcontext_t context = static_cast<ucontext_t*>(uap)->uc_mcontext;
@@ -92,7 +92,6 @@ void CodeProfiling::sample(void* pc, void** framePointer)
void CodeProfiling::notifyAllocator(WTF::MetaAllocator* allocator)
{
-#if !OS(WINCE)
// Check for JSC_CODE_PROFILING.
const char* codeProfilingMode = getenv("JSC_CODE_PROFILING");
if (!codeProfilingMode)
@@ -119,7 +118,6 @@ void CodeProfiling::notifyAllocator(WTF::MetaAllocator* allocator)
ASSERT(!s_tracker);
s_tracker = new WTF::MetaAllocatorTracker();
allocator->trackAllocations(s_tracker);
-#endif
}
void* CodeProfiling::getOwnerUIDForPC(void* address)
@@ -143,7 +141,7 @@ void CodeProfiling::begin(const SourceCode& source)
if (alreadyProfiling)
return;
-#if (PLATFORM(MAC) && CPU(X86_64)) || (OS(LINUX) && CPU(X86))
+#if (OS(DARWIN) && !PLATFORM(GTK) && CPU(X86_64)) || (OS(LINUX) && CPU(X86))
// Regsiter a signal handler & itimer.
struct sigaction action;
action.sa_sigaction = reinterpret_cast<void (*)(int, siginfo_t *, void *)>(profilingTimer);
@@ -167,7 +165,7 @@ void CodeProfiling::end()
if (s_profileStack)
return;
-#if (PLATFORM(MAC) && CPU(X86_64)) || (OS(LINUX) && CPU(X86))
+#if (OS(DARWIN) && !PLATFORM(GTK) && CPU(X86_64)) || (OS(LINUX) && CPU(X86))
// Stop profiling
setProfileTimer(0);
#endif
diff --git a/Source/JavaScriptCore/tools/CodeProfiling.h b/Source/JavaScriptCore/tools/CodeProfiling.h
index 9b0f5daa3..311fcda68 100644
--- a/Source/JavaScriptCore/tools/CodeProfiling.h
+++ b/Source/JavaScriptCore/tools/CodeProfiling.h
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CodeProfiling_h
-#define CodeProfiling_h
+#pragma once
namespace WTF {
@@ -79,7 +78,4 @@ private:
static volatile CodeProfile* s_profileStack;
};
-}
-
-#endif // CodeProfiling_h
-
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/FunctionOverrides.cpp b/Source/JavaScriptCore/tools/FunctionOverrides.cpp
new file mode 100644
index 000000000..ef8943592
--- /dev/null
+++ b/Source/JavaScriptCore/tools/FunctionOverrides.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2015-2016 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 "FunctionOverrides.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <wtf/DataLog.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/StringHash.h>
+
+namespace JSC {
+
+/*
+ The overrides file defines function bodies that we will want to override with
+ a replacement for debugging purposes. The overrides file may contain
+ 'override' and 'with' clauses like these:
+
+ // Example 1: function foo1(a)
+ override !@#$%{ print("In foo1"); }!@#$%
+ with abc{
+ print("I am overridden");
+ }abc
+
+ // Example 2: function foo2(a)
+ override %%%{
+ print("foo2's body has a string with }%% in it.");
+ // Because }%% appears in the function body here, we cannot use
+ // %% or % as the delimiter. %%% is ok though.
+ }%%%
+ with %%%{
+ print("Overridden foo2");
+ }%%%
+
+ 1. Comments are lines starting with //. All comments will be ignored.
+
+ 2. An 'override' clause is used to specify the original function body we
+ want to override. The with clause is used to specify the overriding
+ function body.
+
+ An 'override' clause must be followed immediately by a 'with' clause.
+
+ 3. An 'override' clause must be of the form:
+ override <delimiter>{...function body...}<delimiter>
+
+ The override keyword must be at the start of the line.
+
+ <delimiter> may be any string of any ASCII characters (except for '{',
+ '}', and whitespace characters) as long as the pattern of "}<delimiter>"
+ does not appear in the function body e.g. the override clause of Example 2
+ above illustrates this.
+
+ The start and end <delimiter> must be identical.
+
+ The space between the override keyword and the start <delimiter> is
+ required.
+
+ All characters between the pair of delimiters will be considered to
+ be part of the function body string. This allows us to also work
+ with script source that are multi-lined i.e. newlines are allowed.
+
+ 4. A 'with' clause is identical in form to an 'override' clause except that
+ it uses the 'with' keyword instead of the 'override' keyword.
+ */
+
+FunctionOverrides& FunctionOverrides::overrides()
+{
+ static LazyNeverDestroyed<FunctionOverrides> overrides;
+ static std::once_flag initializeListFlag;
+ std::call_once(initializeListFlag, [] {
+ const char* overridesFileName = Options::functionOverrides();
+ overrides.construct(overridesFileName);
+ });
+ return overrides;
+}
+
+FunctionOverrides::FunctionOverrides(const char* overridesFileName)
+{
+ parseOverridesInFile(overridesFileName);
+}
+
+void FunctionOverrides::reinstallOverrides()
+{
+ FunctionOverrides& overrides = FunctionOverrides::overrides();
+ const char* overridesFileName = Options::functionOverrides();
+ overrides.clear();
+ overrides.parseOverridesInFile(overridesFileName);
+}
+
+static void initializeOverrideInfo(const SourceCode& origCode, const String& newBody, FunctionOverrides::OverrideInfo& info)
+{
+ String origProviderStr = origCode.provider()->source().toString();
+ unsigned origStart = origCode.startOffset();
+ unsigned origFunctionStart = origProviderStr.reverseFind("function", origStart);
+ unsigned origBraceStart = origProviderStr.find("{", origStart);
+ unsigned headerLength = origBraceStart - origFunctionStart;
+ String origHeader = origProviderStr.substring(origFunctionStart, headerLength);
+
+ String newProviderStr;
+ newProviderStr.append(origHeader);
+ newProviderStr.append(newBody);
+
+ Ref<SourceProvider> newProvider = StringSourceProvider::create(newProviderStr, SourceOrigin { "<overridden>" }, "<overridden>");
+
+ info.firstLine = 1;
+ info.lineCount = 1; // Faking it. This doesn't really matter for now.
+ info.startColumn = 1;
+ info.endColumn = 1; // Faking it. This doesn't really matter for now.
+ info.parametersStartOffset = newProviderStr.find("(");
+ info.typeProfilingStartOffset = newProviderStr.find("{");
+ info.typeProfilingEndOffset = newProviderStr.length() - 1;
+
+ info.sourceCode =
+ SourceCode(WTFMove(newProvider), info.parametersStartOffset, info.typeProfilingEndOffset + 1, 1, 1);
+}
+
+bool FunctionOverrides::initializeOverrideFor(const SourceCode& origCode, FunctionOverrides::OverrideInfo& result)
+{
+ ASSERT(Options::functionOverrides());
+ FunctionOverrides& overrides = FunctionOverrides::overrides();
+
+ String sourceString = origCode.view().toString();
+ size_t sourceBodyStart = sourceString.find('{');
+ if (sourceBodyStart == notFound)
+ return false;
+ String sourceBodyString = sourceString.substring(sourceBodyStart);
+
+ auto it = overrides.m_entries.find(sourceBodyString);
+ if (it == overrides.m_entries.end())
+ return false;
+
+ initializeOverrideInfo(origCode, it->value, result);
+ return true;
+}
+
+#define SYNTAX_ERROR "SYNTAX ERROR"
+#define IO_ERROR "IO ERROR"
+#define FAIL_WITH_ERROR(error, errorMessageInBrackets) \
+ do { \
+ dataLog("functionOverrides ", error, ": "); \
+ dataLog errorMessageInBrackets; \
+ exit(EXIT_FAILURE); \
+ } while (false)
+
+static bool hasDisallowedCharacters(const char* str, size_t length)
+{
+ while (length--) {
+ char c = *str++;
+ // '{' is also disallowed, but we don't need to check for it because
+ // parseClause() searches for '{' as the end of the start delimiter.
+ // As a result, the parsed delimiter string will never include '{'.
+ if (c == '}' || isASCIISpace(c))
+ return true;
+ }
+ return false;
+}
+
+static String parseClause(const char* keyword, size_t keywordLength, FILE* file, const char* line, char* buffer, size_t bufferSize)
+{
+ const char* keywordPos = strstr(line, keyword);
+ if (!keywordPos)
+ FAIL_WITH_ERROR(SYNTAX_ERROR, ("Expecting '", keyword, "' clause:\n", line, "\n"));
+ if (keywordPos != line)
+ FAIL_WITH_ERROR(SYNTAX_ERROR, ("Cannot have any characters before '", keyword, "':\n", line, "\n"));
+ if (line[keywordLength] != ' ')
+ FAIL_WITH_ERROR(SYNTAX_ERROR, ("'", keyword, "' must be followed by a ' ':\n", line, "\n"));
+
+ const char* delimiterStart = &line[keywordLength + 1];
+ const char* delimiterEnd = strstr(delimiterStart, "{");
+ if (!delimiterEnd)
+ FAIL_WITH_ERROR(SYNTAX_ERROR, ("Missing { after '", keyword, "' clause start delimiter:\n", line, "\n"));
+
+ size_t delimiterLength = delimiterEnd - delimiterStart;
+ String delimiter(delimiterStart, delimiterLength);
+
+ if (hasDisallowedCharacters(delimiterStart, delimiterLength))
+ FAIL_WITH_ERROR(SYNTAX_ERROR, ("Delimiter '", delimiter, "' cannot have '{', '}', or whitespace:\n", line, "\n"));
+
+ String terminatorString;
+ terminatorString.append("}");
+ terminatorString.append(delimiter);
+
+ CString terminatorCString = terminatorString.ascii();
+ const char* terminator = terminatorCString.data();
+ line = delimiterEnd; // Start from the {.
+
+ StringBuilder builder;
+ do {
+ const char* p = strstr(line, terminator);
+ if (p) {
+ if (p[strlen(terminator)] != '\n')
+ FAIL_WITH_ERROR(SYNTAX_ERROR, ("Unexpected characters after '", keyword, "' clause end delimiter '", delimiter, "':\n", line, "\n"));
+
+ builder.append(line, p - line + 1);
+ return builder.toString();
+ }
+ builder.append(line);
+
+ } while ((line = fgets(buffer, bufferSize, file)));
+
+ FAIL_WITH_ERROR(SYNTAX_ERROR, ("'", keyword, "' clause end delimiter '", delimiter, "' not found:\n", builder.toString(), "\n", "Are you missing a '}' before the delimiter?\n"));
+}
+
+void FunctionOverrides::parseOverridesInFile(const char* fileName)
+{
+ if (!fileName)
+ return;
+
+ FILE* file = fopen(fileName, "r");
+ if (!file)
+ FAIL_WITH_ERROR(IO_ERROR, ("Failed to open file ", fileName, ". Did you add the file-read-data entitlement to WebProcess.sb?\n"));
+
+ char* line;
+ char buffer[BUFSIZ];
+ while ((line = fgets(buffer, sizeof(buffer), file))) {
+ if (strstr(line, "//") == line)
+ continue;
+
+ if (line[0] == '\n' || line[0] == '\0')
+ continue;
+
+ size_t keywordLength;
+
+ keywordLength = sizeof("override") - 1;
+ String keyStr = parseClause("override", keywordLength, file, line, buffer, sizeof(buffer));
+
+ line = fgets(buffer, sizeof(buffer), file);
+
+ keywordLength = sizeof("with") - 1;
+ String valueStr = parseClause("with", keywordLength, file, line, buffer, sizeof(buffer));
+
+ m_entries.add(keyStr, valueStr);
+ }
+
+ int result = fclose(file);
+ if (result)
+ dataLogF("Failed to close file %s: %s\n", fileName, strerror(errno));
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/tools/FunctionOverrides.h b/Source/JavaScriptCore/tools/FunctionOverrides.h
new file mode 100644
index 000000000..3e5de199e
--- /dev/null
+++ b/Source/JavaScriptCore/tools/FunctionOverrides.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015-2016 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.
+ */
+
+#pragma once
+
+#include "Options.h"
+#include "SourceCode.h"
+#include <wtf/HashMap.h>
+#include <wtf/text/WTFString.h>
+
+namespace JSC {
+
+class ScriptExecutable;
+
+class FunctionOverrides {
+public:
+ struct OverrideInfo {
+ SourceCode sourceCode;
+ unsigned firstLine;
+ unsigned lineCount;
+ unsigned startColumn;
+ unsigned endColumn;
+ unsigned parametersStartOffset;
+ unsigned typeProfilingStartOffset;
+ unsigned typeProfilingEndOffset;
+ };
+
+ static FunctionOverrides& overrides();
+ FunctionOverrides(const char* functionOverridesFileName);
+
+ static bool initializeOverrideFor(const SourceCode& origCode, OverrideInfo& result);
+
+ JS_EXPORT_PRIVATE static void reinstallOverrides();
+
+private:
+ void parseOverridesInFile(const char* fileName);
+ void clear() { m_entries.clear(); }
+
+ HashMap<String, String> m_entries;
+};
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/FunctionWhitelist.cpp b/Source/JavaScriptCore/tools/FunctionWhitelist.cpp
new file mode 100644
index 000000000..c1ea36057
--- /dev/null
+++ b/Source/JavaScriptCore/tools/FunctionWhitelist.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014, 2016 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 "FunctionWhitelist.h"
+
+#if ENABLE(JIT)
+
+#include "CodeBlock.h"
+#include "Options.h"
+#include <stdio.h>
+#include <string.h>
+
+namespace JSC {
+
+FunctionWhitelist::FunctionWhitelist(const char* filename)
+{
+ if (!filename)
+ return;
+
+ FILE* f = fopen(filename, "r");
+ if (!f) {
+ dataLogF("Failed to open file %s. Did you add the file-read-data entitlement to WebProcess.sb?\n", filename);
+ return;
+ }
+
+ m_hasActiveWhitelist = true;
+
+ char* line;
+ char buffer[BUFSIZ];
+ while ((line = fgets(buffer, sizeof(buffer), f))) {
+ if (strstr(line, "//") == line)
+ continue;
+
+ // Get rid of newlines at the ends of the strings.
+ size_t length = strlen(line);
+ if (line[length - 1] == '\n') {
+ line[length - 1] = '\0';
+ length--;
+ }
+
+ // Skip empty lines.
+ if (!length)
+ continue;
+
+ m_entries.add(String(line, length));
+ }
+
+ int result = fclose(f);
+ if (result)
+ dataLogF("Failed to close file %s: %s\n", filename, strerror(errno));
+}
+
+bool FunctionWhitelist::contains(CodeBlock* codeBlock) const
+{
+ ASSERT(!isCompilationThread());
+ if (!m_hasActiveWhitelist)
+ return true;
+
+ if (m_entries.isEmpty())
+ return false;
+
+ String name = String::fromUTF8(codeBlock->inferredName());
+ if (m_entries.contains(name))
+ return true;
+
+ String hash = String::fromUTF8(codeBlock->hashAsStringIfPossible());
+ if (m_entries.contains(hash))
+ return true;
+
+ return m_entries.contains(name + '#' + hash);
+}
+
+} // namespace JSC
+
+#endif // ENABLE(JIT)
+
diff --git a/Source/JavaScriptCore/tools/FunctionWhitelist.h b/Source/JavaScriptCore/tools/FunctionWhitelist.h
new file mode 100644
index 000000000..b5e6e4f14
--- /dev/null
+++ b/Source/JavaScriptCore/tools/FunctionWhitelist.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, 2016 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.
+ */
+
+#pragma once
+
+#include <wtf/HashSet.h>
+#include <wtf/text/WTFString.h>
+
+namespace JSC {
+
+class CodeBlock;
+
+class FunctionWhitelist {
+public:
+ explicit FunctionWhitelist(const char*);
+
+ bool contains(CodeBlock*) const;
+
+private:
+ HashSet<String> m_entries;
+ bool m_hasActiveWhitelist { false };
+};
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/JSDollarVM.cpp b/Source/JavaScriptCore/tools/JSDollarVM.cpp
new file mode 100644
index 000000000..ae389731d
--- /dev/null
+++ b/Source/JavaScriptCore/tools/JSDollarVM.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 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 "JSDollarVM.h"
+
+#include "JSCInlines.h"
+
+namespace JSC {
+
+const ClassInfo JSDollarVM::s_info = { "DollarVM", &Base::s_info, 0, CREATE_METHOD_TABLE(JSDollarVM) };
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/JSDollarVM.h b/Source/JavaScriptCore/tools/JSDollarVM.h
new file mode 100644
index 000000000..22675772b
--- /dev/null
+++ b/Source/JavaScriptCore/tools/JSDollarVM.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#pragma once
+
+#include "JSObject.h"
+
+namespace JSC {
+
+class JSDollarVM : public JSNonFinalObject {
+public:
+ typedef JSNonFinalObject Base;
+
+ DECLARE_EXPORT_INFO;
+
+ static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+ {
+ return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+ }
+
+ static JSDollarVM* create(VM& vm, Structure* structure)
+ {
+ JSDollarVM* instance = new (NotNull, allocateCell<JSDollarVM>(vm.heap)) JSDollarVM(vm, structure);
+ instance->finishCreation(vm);
+ return instance;
+ }
+
+private:
+ JSDollarVM(VM& vm, Structure* structure)
+ : Base(vm, structure)
+ {
+ }
+};
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp b/Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp
new file mode 100644
index 000000000..95b422c4e
--- /dev/null
+++ b/Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2015-2016 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 "JSDollarVMPrototype.h"
+
+#include "CodeBlock.h"
+#include "FunctionCodeBlock.h"
+#include "Heap.h"
+#include "HeapIterationScope.h"
+#include "JSCInlines.h"
+#include "JSFunction.h"
+#include "MarkedSpaceInlines.h"
+#include "StackVisitor.h"
+#include <wtf/DataLog.h>
+#include <wtf/StringPrintStream.h>
+
+namespace JSC {
+
+const ClassInfo JSDollarVMPrototype::s_info = { "DollarVMPrototype", &Base::s_info, 0, CREATE_METHOD_TABLE(JSDollarVMPrototype) };
+
+
+bool JSDollarVMPrototype::currentThreadOwnsJSLock(ExecState* exec)
+{
+ return exec->vm().apiLock().currentThreadIsHoldingLock();
+}
+
+static bool ensureCurrentThreadOwnsJSLock(ExecState* exec)
+{
+ if (JSDollarVMPrototype::currentThreadOwnsJSLock(exec))
+ return true;
+ dataLog("ERROR: current thread does not own the JSLock\n");
+ return false;
+}
+
+void JSDollarVMPrototype::addFunction(VM& vm, JSGlobalObject* globalObject, const char* name, NativeFunction function, unsigned arguments)
+{
+ Identifier identifier = Identifier::fromString(&vm, name);
+ putDirect(vm, identifier, JSFunction::create(vm, globalObject, arguments, identifier.string(), function));
+}
+
+static EncodedJSValue JSC_HOST_CALL functionCrash(ExecState*)
+{
+ CRASH();
+ return JSValue::encode(jsUndefined());
+}
+
+static EncodedJSValue JSC_HOST_CALL functionDFGTrue(ExecState*)
+{
+ return JSValue::encode(jsBoolean(false));
+}
+
+class CallerFrameJITTypeFunctor {
+public:
+ CallerFrameJITTypeFunctor()
+ : m_currentFrame(0)
+ , m_jitType(JITCode::None)
+ {
+ }
+
+ StackVisitor::Status operator()(StackVisitor& visitor) const
+ {
+ if (m_currentFrame++ > 1) {
+ m_jitType = visitor->codeBlock()->jitType();
+ return StackVisitor::Done;
+ }
+ return StackVisitor::Continue;
+ }
+
+ JITCode::JITType jitType() { return m_jitType; }
+
+private:
+ mutable unsigned m_currentFrame;
+ mutable JITCode::JITType m_jitType;
+};
+
+static EncodedJSValue JSC_HOST_CALL functionLLintTrue(ExecState* exec)
+{
+ if (!exec)
+ return JSValue::encode(jsUndefined());
+ CallerFrameJITTypeFunctor functor;
+ exec->iterate(functor);
+ return JSValue::encode(jsBoolean(functor.jitType() == JITCode::InterpreterThunk));
+}
+
+static EncodedJSValue JSC_HOST_CALL functionJITTrue(ExecState* exec)
+{
+ if (!exec)
+ return JSValue::encode(jsUndefined());
+ CallerFrameJITTypeFunctor functor;
+ exec->iterate(functor);
+ return JSValue::encode(jsBoolean(functor.jitType() == JITCode::BaselineJIT));
+}
+
+void JSDollarVMPrototype::gc(ExecState* exec)
+{
+ if (!ensureCurrentThreadOwnsJSLock(exec))
+ return;
+ exec->heap()->collectAllGarbage();
+}
+
+static EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec)
+{
+ JSDollarVMPrototype::gc(exec);
+ return JSValue::encode(jsUndefined());
+}
+
+void JSDollarVMPrototype::edenGC(ExecState* exec)
+{
+ if (!ensureCurrentThreadOwnsJSLock(exec))
+ return;
+ exec->heap()->collectSync(CollectionScope::Eden);
+}
+
+static EncodedJSValue JSC_HOST_CALL functionEdenGC(ExecState* exec)
+{
+ JSDollarVMPrototype::edenGC(exec);
+ return JSValue::encode(jsUndefined());
+}
+
+bool JSDollarVMPrototype::isInHeap(Heap* heap, void* ptr)
+{
+ return isInObjectSpace(heap, ptr) || isInStorageSpace(heap, ptr);
+}
+
+bool JSDollarVMPrototype::isInObjectSpace(Heap* heap, void* ptr)
+{
+ MarkedBlock* candidate = MarkedBlock::blockFor(ptr);
+ if (heap->objectSpace().blocks().set().contains(candidate))
+ return true;
+ for (LargeAllocation* allocation : heap->objectSpace().largeAllocations()) {
+ if (allocation->contains(ptr))
+ return true;
+ }
+ return false;
+}
+
+bool JSDollarVMPrototype::isInStorageSpace(Heap*, void*)
+{
+ // FIXME: Do something with this.
+ // https://bugs.webkit.org/show_bug.cgi?id=161753
+ return false;
+}
+
+struct CellAddressCheckFunctor : MarkedBlock::CountFunctor {
+ CellAddressCheckFunctor(JSCell* candidate)
+ : candidate(candidate)
+ {
+ }
+
+ IterationStatus operator()(HeapCell* cell, HeapCell::Kind) const
+ {
+ if (cell == candidate) {
+ found = true;
+ return IterationStatus::Done;
+ }
+ return IterationStatus::Continue;
+ }
+
+ JSCell* candidate;
+ mutable bool found { false };
+};
+
+bool JSDollarVMPrototype::isValidCell(Heap* heap, JSCell* candidate)
+{
+ HeapIterationScope iterationScope(*heap);
+ CellAddressCheckFunctor functor(candidate);
+ heap->objectSpace().forEachLiveCell(iterationScope, functor);
+ return functor.found;
+}
+
+bool JSDollarVMPrototype::isValidCodeBlock(ExecState* exec, CodeBlock* candidate)
+{
+ if (!ensureCurrentThreadOwnsJSLock(exec))
+ return false;
+
+ struct CodeBlockValidationFunctor {
+ CodeBlockValidationFunctor(CodeBlock* candidate)
+ : candidate(candidate)
+ {
+ }
+
+ bool operator()(CodeBlock* codeBlock) const
+ {
+ if (codeBlock == candidate)
+ found = true;
+ return found;
+ }
+
+ CodeBlock* candidate;
+ mutable bool found { false };
+ };
+
+ VM& vm = exec->vm();
+ CodeBlockValidationFunctor functor(candidate);
+ vm.heap.forEachCodeBlock(functor);
+ return functor.found;
+}
+
+CodeBlock* JSDollarVMPrototype::codeBlockForFrame(CallFrame* topCallFrame, unsigned frameNumber)
+{
+ if (!ensureCurrentThreadOwnsJSLock(topCallFrame))
+ return nullptr;
+
+ if (!topCallFrame)
+ return nullptr;
+
+ struct FetchCodeBlockFunctor {
+ public:
+ FetchCodeBlockFunctor(unsigned targetFrameNumber)
+ : targetFrame(targetFrameNumber)
+ {
+ }
+
+ StackVisitor::Status operator()(StackVisitor& visitor) const
+ {
+ currentFrame++;
+ if (currentFrame == targetFrame) {
+ codeBlock = visitor->codeBlock();
+ return StackVisitor::Done;
+ }
+ return StackVisitor::Continue;
+ }
+
+ unsigned targetFrame;
+ mutable unsigned currentFrame { 0 };
+ mutable CodeBlock* codeBlock { nullptr };
+ };
+
+ FetchCodeBlockFunctor functor(frameNumber);
+ topCallFrame->iterate(functor);
+ return functor.codeBlock;
+}
+
+static EncodedJSValue JSC_HOST_CALL functionCodeBlockForFrame(ExecState* exec)
+{
+ if (exec->argumentCount() < 1)
+ return JSValue::encode(jsUndefined());
+
+ JSValue value = exec->uncheckedArgument(0);
+ if (!value.isUInt32())
+ return JSValue::encode(jsUndefined());
+
+ // We need to inc the frame number because the caller would consider
+ // its own frame as frame 0. Hence, we need discount the frame for this
+ // function.
+ unsigned frameNumber = value.asUInt32() + 1;
+ CodeBlock* codeBlock = JSDollarVMPrototype::codeBlockForFrame(exec, frameNumber);
+ // Though CodeBlock is a JSCell, it is not safe to return it directly back to JS code
+ // as it is an internal type that the JS code cannot handle. Hence, we first encode the
+ // CodeBlock* as a double token (which is safe for JS code to handle) before returning it.
+ return JSValue::encode(JSValue(bitwise_cast<double>(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(codeBlock)))));
+}
+
+static CodeBlock* codeBlockFromArg(ExecState* exec)
+{
+ VM& vm = exec->vm();
+ if (exec->argumentCount() < 1)
+ return nullptr;
+
+ JSValue value = exec->uncheckedArgument(0);
+ CodeBlock* candidateCodeBlock = nullptr;
+ if (value.isCell()) {
+ JSFunction* func = jsDynamicCast<JSFunction*>(vm, value.asCell());
+ if (func) {
+ if (func->isHostFunction())
+ candidateCodeBlock = nullptr;
+ else
+ candidateCodeBlock = func->jsExecutable()->eitherCodeBlock();
+ }
+ } else if (value.isDouble()) {
+ // If the value is a double, it may be an encoded CodeBlock* that came from
+ // $vm.codeBlockForFrame(). We'll treat it as a candidate codeBlock and check if it's
+ // valid below before using.
+ candidateCodeBlock = reinterpret_cast<CodeBlock*>(bitwise_cast<uint64_t>(value.asDouble()));
+ }
+
+ if (candidateCodeBlock && JSDollarVMPrototype::isValidCodeBlock(exec, candidateCodeBlock))
+ return candidateCodeBlock;
+
+ if (candidateCodeBlock)
+ dataLog("Invalid codeBlock: ", RawPointer(candidateCodeBlock), " ", value, "\n");
+ else
+ dataLog("Invalid codeBlock: ", value, "\n");
+ return nullptr;
+}
+
+static EncodedJSValue JSC_HOST_CALL functionCodeBlockFor(ExecState* exec)
+{
+ CodeBlock* codeBlock = codeBlockFromArg(exec);
+ WTF::StringPrintStream stream;
+ if (codeBlock) {
+ stream.print(*codeBlock);
+ return JSValue::encode(jsString(exec, stream.toString()));
+ }
+ return JSValue::encode(jsUndefined());
+}
+
+static EncodedJSValue JSC_HOST_CALL functionPrintSourceFor(ExecState* exec)
+{
+ CodeBlock* codeBlock = codeBlockFromArg(exec);
+ if (codeBlock)
+ codeBlock->dumpSource();
+ return JSValue::encode(jsUndefined());
+}
+
+static EncodedJSValue JSC_HOST_CALL functionPrintByteCodeFor(ExecState* exec)
+{
+ CodeBlock* codeBlock = codeBlockFromArg(exec);
+ if (codeBlock)
+ codeBlock->dumpBytecode();
+ return JSValue::encode(jsUndefined());
+}
+
+static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec)
+{
+ auto scope = DECLARE_THROW_SCOPE(exec->vm());
+ for (unsigned i = 0; i < exec->argumentCount(); ++i) {
+ String argStr = exec->uncheckedArgument(i).toWTFString(exec);
+ RETURN_IF_EXCEPTION(scope, encodedJSValue());
+ dataLog(argStr);
+ }
+ return JSValue::encode(jsUndefined());
+}
+
+class PrintFrameFunctor {
+public:
+ enum Action {
+ PrintOne,
+ PrintAll
+ };
+
+ PrintFrameFunctor(Action action, unsigned framesToSkip)
+ : m_action(action)
+ , m_framesToSkip(framesToSkip)
+ {
+ }
+
+ StackVisitor::Status operator()(StackVisitor& visitor) const
+ {
+ m_currentFrame++;
+ if (m_currentFrame > m_framesToSkip) {
+ visitor->dump(WTF::dataFile(), Indenter(2), [&] (PrintStream& out) {
+ out.print("[", (m_currentFrame - m_framesToSkip - 1), "] ");
+ });
+ }
+ if (m_action == PrintOne && m_currentFrame > m_framesToSkip)
+ return StackVisitor::Done;
+ return StackVisitor::Continue;
+ }
+
+private:
+ Action m_action;
+ unsigned m_framesToSkip;
+ mutable unsigned m_currentFrame { 0 };
+};
+
+static void printCallFrame(CallFrame* callFrame, unsigned framesToSkip)
+{
+ if (!ensureCurrentThreadOwnsJSLock(callFrame))
+ return;
+ PrintFrameFunctor functor(PrintFrameFunctor::PrintOne, framesToSkip);
+ callFrame->iterate(functor);
+}
+
+void JSDollarVMPrototype::printCallFrame(CallFrame* callFrame)
+{
+ JSC::printCallFrame(callFrame, 0);
+}
+
+static void printStack(CallFrame* topCallFrame, unsigned framesToSkip)
+{
+ if (!ensureCurrentThreadOwnsJSLock(topCallFrame))
+ return;
+ if (!topCallFrame)
+ return;
+ PrintFrameFunctor functor(PrintFrameFunctor::PrintAll, framesToSkip);
+ topCallFrame->iterate(functor);
+}
+
+void JSDollarVMPrototype::printStack(CallFrame* topCallFrame)
+{
+ JSC::printStack(topCallFrame, 0);
+}
+
+static EncodedJSValue JSC_HOST_CALL functionPrintCallFrame(ExecState* exec)
+{
+ // When the callers call this function, they are expecting to print their
+ // own frame. So skip 1 for this frame.
+ printCallFrame(exec, 1);
+ return JSValue::encode(jsUndefined());
+}
+
+static EncodedJSValue JSC_HOST_CALL functionPrintStack(ExecState* exec)
+{
+ // When the callers call this function, they are expecting to print the
+ // stack starting their own frame. So skip 1 for this frame.
+ printStack(exec, 1);
+ return JSValue::encode(jsUndefined());
+}
+
+void JSDollarVMPrototype::printValue(JSValue value)
+{
+ dataLog(value);
+}
+
+static EncodedJSValue JSC_HOST_CALL functionValue(ExecState* exec)
+{
+ WTF::StringPrintStream stream;
+ for (unsigned i = 0; i < exec->argumentCount(); ++i) {
+ if (i)
+ stream.print(", ");
+ stream.print(exec->uncheckedArgument(i));
+ }
+
+ return JSValue::encode(jsString(exec, stream.toString()));
+}
+
+#if !PLATFORM(WIN)
+static EncodedJSValue JSC_HOST_CALL functionGetPID(ExecState*)
+{
+ return JSValue::encode(jsNumber(getpid()));
+}
+#endif
+
+void JSDollarVMPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject)
+{
+ Base::finishCreation(vm);
+
+ addFunction(vm, globalObject, "crash", functionCrash, 0);
+
+ putDirectNativeFunction(vm, globalObject, Identifier::fromString(&vm, "dfgTrue"), 0, functionDFGTrue, DFGTrueIntrinsic, DontEnum);
+
+ addFunction(vm, globalObject, "llintTrue", functionLLintTrue, 0);
+ addFunction(vm, globalObject, "jitTrue", functionJITTrue, 0);
+
+ addFunction(vm, globalObject, "gc", functionGC, 0);
+ addFunction(vm, globalObject, "edenGC", functionEdenGC, 0);
+
+ addFunction(vm, globalObject, "codeBlockFor", functionCodeBlockFor, 1);
+ addFunction(vm, globalObject, "codeBlockForFrame", functionCodeBlockForFrame, 1);
+ addFunction(vm, globalObject, "printSourceFor", functionPrintSourceFor, 1);
+ addFunction(vm, globalObject, "printByteCodeFor", functionPrintByteCodeFor, 1);
+
+ addFunction(vm, globalObject, "print", functionPrint, 1);
+ addFunction(vm, globalObject, "printCallFrame", functionPrintCallFrame, 0);
+ addFunction(vm, globalObject, "printStack", functionPrintStack, 0);
+
+ addFunction(vm, globalObject, "value", functionValue, 1);
+#if !PLATFORM(WIN)
+ addFunction(vm, globalObject, "getpid", functionGetPID, 0);
+#endif
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/JSDollarVMPrototype.h b/Source/JavaScriptCore/tools/JSDollarVMPrototype.h
new file mode 100644
index 000000000..788e26790
--- /dev/null
+++ b/Source/JavaScriptCore/tools/JSDollarVMPrototype.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#pragma once
+
+#include "JSObject.h"
+
+namespace JSC {
+
+class Heap;
+
+class JSDollarVMPrototype : public JSNonFinalObject {
+public:
+ typedef JSNonFinalObject Base;
+
+ DECLARE_INFO;
+
+ static JSDollarVMPrototype* create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
+ {
+ JSDollarVMPrototype* prototype = new (NotNull, allocateCell<JSDollarVMPrototype>(vm.heap)) JSDollarVMPrototype(vm, structure);
+ prototype->finishCreation(vm, globalObject);
+ return prototype;
+ }
+
+ static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+ {
+ return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+ }
+
+ // The following are exported because they are designed to be callable from
+ // lldb. The JS versions are implemented on top of these.
+
+ JS_EXPORT_PRIVATE static bool currentThreadOwnsJSLock(ExecState*);
+ JS_EXPORT_PRIVATE static void gc(ExecState*);
+ JS_EXPORT_PRIVATE static void edenGC(ExecState*);
+ JS_EXPORT_PRIVATE static bool isInHeap(Heap*, void*);
+ JS_EXPORT_PRIVATE static bool isInObjectSpace(Heap*, void*);
+ JS_EXPORT_PRIVATE static bool isInStorageSpace(Heap*, void*);
+ JS_EXPORT_PRIVATE static bool isValidCell(Heap*, JSCell*);
+ JS_EXPORT_PRIVATE static bool isValidCodeBlock(ExecState*, CodeBlock*);
+ JS_EXPORT_PRIVATE static CodeBlock* codeBlockForFrame(CallFrame* topCallFrame, unsigned frameNumber);
+ JS_EXPORT_PRIVATE static void printCallFrame(CallFrame*);
+ JS_EXPORT_PRIVATE static void printStack(CallFrame* topCallFrame);
+ JS_EXPORT_PRIVATE static void printValue(JSValue);
+
+private:
+ JSDollarVMPrototype(VM& vm, Structure* structure)
+ : Base(vm, structure)
+ {
+ }
+
+ void finishCreation(VM&, JSGlobalObject*);
+ void addFunction(VM&, JSGlobalObject*, const char* name, NativeFunction, unsigned arguments);
+};
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/ProfileTreeNode.h b/Source/JavaScriptCore/tools/ProfileTreeNode.h
index 88d57f3dd..66f415489 100644
--- a/Source/JavaScriptCore/tools/ProfileTreeNode.h
+++ b/Source/JavaScriptCore/tools/ProfileTreeNode.h
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ProfileTreeNode_h
-#define ProfileTreeNode_h
+#pragma once
namespace JSC {
@@ -89,9 +88,7 @@ private:
qsort(entries.begin(), entries.size(), sizeof(MapEntry*), compareEntries);
// Iterate over the children in sample-frequency order.
- for (size_t e = 0; e < entries.size(); ++e) {
- MapEntry* entry = entries[e];
-
+ for (auto* entry : entries) {
// Print the number of samples, the name of this node, and the number of samples that are stack-top
// in this node (samples directly within this node, excluding samples in children.
for (unsigned i = 0; i < indent; ++i)
@@ -117,7 +114,4 @@ private:
Map* m_children;
};
-}
-
-#endif // ProfileTreeNode_h
-
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/SigillCrashAnalyzer.cpp b/Source/JavaScriptCore/tools/SigillCrashAnalyzer.cpp
new file mode 100644
index 000000000..1ac1af480
--- /dev/null
+++ b/Source/JavaScriptCore/tools/SigillCrashAnalyzer.cpp
@@ -0,0 +1,396 @@
+/*
+ * 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 "SigillCrashAnalyzer.h"
+
+#include "CallFrame.h"
+#include "CodeBlock.h"
+#include "VMInspector.h"
+#include <mutex>
+#include <wtf/StdLibExtras.h>
+
+#if USE(ARM64_DISASSEMBLER)
+#include "A64DOpcode.h"
+#endif
+
+#if HAVE(SIGNAL_H)
+#include <signal.h>
+#endif
+
+namespace JSC {
+
+struct SignalContext;
+
+class SigillCrashAnalyzer {
+public:
+ static SigillCrashAnalyzer& instance();
+
+ enum class CrashSource {
+ Unknown,
+ JavaScriptCore,
+ Other,
+ };
+ CrashSource analyze(SignalContext&);
+
+private:
+ SigillCrashAnalyzer() { }
+ void dumpCodeBlock(CodeBlock*, void* machinePC);
+
+#if USE(ARM64_DISASSEMBLER)
+ A64DOpcode m_arm64Opcode;
+#endif
+};
+
+#if OS(DARWIN)
+
+#if USE(OS_LOG)
+
+#define log(format, ...) \
+ os_log_info(OS_LOG_DEFAULT, format, ##__VA_ARGS__)
+
+#else // USE(OS_LOG)
+
+#define log(format, ...) \
+ dataLogF(format, ##__VA_ARGS__)
+
+#endif // USE(OS_LOG)
+
+#if CPU(X86_64)
+struct SignalContext {
+ SignalContext(mcontext_t& mcontext)
+ : mcontext(mcontext)
+ , machinePC(reinterpret_cast<void*>(mcontext->__ss.__rip))
+ , stackPointer(reinterpret_cast<void*>(mcontext->__ss.__rsp))
+ , framePointer(reinterpret_cast<CallFrame*>(mcontext->__ss.__rbp))
+ { }
+
+ void dump()
+ {
+#define FOR_EACH_REGISTER(v) \
+ v(rax) \
+ v(rbx) \
+ v(rcx) \
+ v(rdx) \
+ v(rdi) \
+ v(rsi) \
+ v(rbp) \
+ v(rsp) \
+ v(r8) \
+ v(r9) \
+ v(r10) \
+ v(r11) \
+ v(r12) \
+ v(r13) \
+ v(r14) \
+ v(r15) \
+ v(rip) \
+ v(rflags) \
+ v(cs) \
+ v(fs) \
+ v(gs)
+
+#define DUMP_REGISTER(__reg) \
+ log("Register " #__reg ": %p", reinterpret_cast<void*>(mcontext->__ss.__##__reg));
+ FOR_EACH_REGISTER(DUMP_REGISTER)
+#undef FOR_EACH_REGISTER
+ }
+
+ mcontext_t& mcontext;
+ void* machinePC;
+ void* stackPointer;
+ void* framePointer;
+};
+
+#elif CPU(ARM64)
+
+struct SignalContext {
+ SignalContext(mcontext_t& mcontext)
+ : mcontext(mcontext)
+ , machinePC(reinterpret_cast<void*>(mcontext->__ss.__pc))
+ , stackPointer(reinterpret_cast<void*>(mcontext->__ss.__sp))
+ , framePointer(reinterpret_cast<CallFrame*>(mcontext->__ss.__fp))
+ { }
+
+ void dump()
+ {
+ int i;
+ for (i = 0; i < 28; i += 4) {
+ log("x%d: %016llx x%d: %016llx x%d: %016llx x%d: %016llx",
+ i, mcontext->__ss.__x[i],
+ i+1, mcontext->__ss.__x[i+1],
+ i+2, mcontext->__ss.__x[i+2],
+ i+3, mcontext->__ss.__x[i+3]);
+ }
+ ASSERT(i < 29);
+ log("x%d: %016llx fp: %016llx lr: %016llx",
+ i, mcontext->__ss.__x[i], mcontext->__ss.__fp, mcontext->__ss.__lr);
+ log("sp: %016llx pc: %016llx cpsr: %08x",
+ mcontext->__ss.__sp, mcontext->__ss.__pc, mcontext->__ss.__cpsr);
+ }
+
+ mcontext_t& mcontext;
+ void* machinePC;
+ void* stackPointer;
+ void* framePointer;
+};
+
+#else
+
+struct SignalContext {
+ SignalContext(mcontext_t&) { }
+
+ void dump() { }
+
+ void* machinePC;
+ void* stackPointer;
+ void* framePointer;
+};
+
+#endif
+
+struct sigaction originalSigIllAction;
+
+static void handleCrash(int signalNumber, siginfo_t* info, void* uap)
+{
+ SignalContext context(static_cast<ucontext_t*>(uap)->uc_mcontext);
+ SigillCrashAnalyzer& analyzer = SigillCrashAnalyzer::instance();
+ auto crashSource = analyzer.analyze(context);
+
+ auto originalAction = originalSigIllAction.sa_sigaction;
+ if (originalAction) {
+ // It is always safe to just invoke the original handler using the sa_sigaction form
+ // without checking for the SA_SIGINFO flag. If the original handler is of the
+ // sa_handler form, it will just ignore the 2nd and 3rd arguments since sa_handler is a
+ // subset of sa_sigaction. This is what the man pages says the OS does anyway.
+ originalAction(signalNumber, info, uap);
+ }
+
+ if (crashSource == SigillCrashAnalyzer::CrashSource::JavaScriptCore) {
+ // Restore the default handler so that we can get a core dump.
+ struct sigaction defaultAction;
+ defaultAction.sa_handler = SIG_DFL;
+ sigfillset(&defaultAction.sa_mask);
+ defaultAction.sa_flags = 0;
+ sigaction(SIGILL, &defaultAction, nullptr);
+ } else if (!originalAction) {
+ // Pre-emptively restore the default handler but we may roll it back below.
+ struct sigaction currentAction;
+ struct sigaction defaultAction;
+ defaultAction.sa_handler = SIG_DFL;
+ sigfillset(&defaultAction.sa_mask);
+ defaultAction.sa_flags = 0;
+ sigaction(SIGILL, &defaultAction, &currentAction);
+
+ if (currentAction.sa_sigaction != handleCrash) {
+ // This means that there's a client handler installed after us. This also means
+ // that the client handler thinks it was able to recover from the SIGILL, and
+ // did not uninstall itself. We can't argue with this because the crash isn't
+ // known to be from a JavaScriptCore source. Hence, restore the client handler
+ // and keep going.
+ sigaction(SIGILL, &currentAction, nullptr);
+ }
+ }
+}
+
+static void installCrashHandler()
+{
+#if CPU(X86_64) || CPU(ARM64)
+ struct sigaction action;
+ action.sa_sigaction = reinterpret_cast<void (*)(int, siginfo_t *, void *)>(handleCrash);
+ sigfillset(&action.sa_mask);
+ action.sa_flags = SA_SIGINFO;
+ sigaction(SIGILL, &action, &originalSigIllAction);
+#else
+ UNUSED_PARAM(handleCrash);
+#endif
+}
+
+#else // OS(DARWIN)
+
+#define log(format, ...) do { } while (false)
+
+struct SignalContext {
+ SignalContext() { }
+
+ void dump() { }
+
+ void* machinePC;
+ void* stackPointer;
+ void* framePointer;
+};
+
+static void installCrashHandler()
+{
+ // Do nothing. Not supported for this platform.
+}
+
+#endif // OS(DARWIN)
+
+SigillCrashAnalyzer& SigillCrashAnalyzer::instance()
+{
+ static SigillCrashAnalyzer* analyzer;
+ static std::once_flag once;
+ std::call_once(once, [] {
+ installCrashHandler();
+ analyzer = new SigillCrashAnalyzer;
+ });
+ return *analyzer;
+}
+
+void enableSigillCrashAnalyzer()
+{
+ // Just instantiating the SigillCrashAnalyzer will enable it.
+ SigillCrashAnalyzer::instance();
+}
+
+auto SigillCrashAnalyzer::analyze(SignalContext& context) -> CrashSource
+{
+ CrashSource crashSource = CrashSource::Unknown;
+ log("BEGIN SIGILL analysis");
+
+ do {
+ // First, dump the signal context info so that we'll at least have the same info
+ // that the default crash handler would given us in case this crash analyzer
+ // itself crashes.
+ context.dump();
+
+ VMInspector& inspector = VMInspector::instance();
+
+ // Use a timeout period of 2 seconds. The client is about to crash, and we don't
+ // want to turn the crash into a hang by re-trying the lock for too long.
+ auto expectedLocker = inspector.lock(Seconds(2));
+ if (!expectedLocker) {
+ ASSERT(expectedLocker.error() == VMInspector::Error::TimedOut);
+ log("ERROR: Unable to analyze SIGILL. Timed out while waiting to iterate VMs.");
+ break;
+ }
+ auto& locker = expectedLocker.value();
+
+ void* pc = context.machinePC;
+ auto isInJITMemory = inspector.isValidExecutableMemory(locker, pc);
+ if (!isInJITMemory) {
+ log("ERROR: Timed out: not able to determine if pc %p is in valid JIT executable memory", pc);
+ break;
+ }
+ if (!isInJITMemory.value()) {
+ log("pc %p is NOT in valid JIT executable memory", pc);
+ crashSource = CrashSource::Other;
+ break;
+ }
+ log("pc %p is in valid JIT executable memory", pc);
+ crashSource = CrashSource::JavaScriptCore;
+
+#if CPU(ARM64)
+ size_t pcAsSize = reinterpret_cast<size_t>(pc);
+ if (pcAsSize != roundUpToMultipleOf<sizeof(uint32_t)>(pcAsSize)) {
+ log("pc %p is NOT properly aligned", pc);
+ break;
+ }
+
+ // We know it's safe to read the word at the PC because we're handling a SIGILL.
+ // Otherwise, we would have crashed with a SIGBUS instead.
+ uint32_t wordAtPC = *reinterpret_cast<uint32_t*>(pc);
+ log("instruction bits at pc %p is: 0x%08x", pc, wordAtPC);
+#endif
+
+ auto expectedCodeBlock = inspector.codeBlockForMachinePC(locker, pc);
+ if (!expectedCodeBlock) {
+ if (expectedCodeBlock.error() == VMInspector::Error::TimedOut)
+ log("ERROR: Timed out: not able to determine if pc %p is in a valid CodeBlock", pc);
+ else
+ log("The current thread does not own any VM JSLock");
+ break;
+ }
+ CodeBlock* codeBlock = expectedCodeBlock.value();
+ if (!codeBlock) {
+ log("machine PC %p does not belong to any CodeBlock in the currently entered VM", pc);
+ break;
+ }
+
+ log("pc %p belongs to CodeBlock %p of type %s", pc, codeBlock, JITCode::typeName(codeBlock->jitType()));
+
+ dumpCodeBlock(codeBlock, pc);
+ } while (false);
+
+ log("END SIGILL analysis");
+ return crashSource;
+}
+
+void SigillCrashAnalyzer::dumpCodeBlock(CodeBlock* codeBlock, void* machinePC)
+{
+#if CPU(ARM64)
+ JITCode* jitCode = codeBlock->jitCode().get();
+
+ // Dump the raw bits of the code.
+ uint32_t* start = reinterpret_cast<uint32_t*>(jitCode->start());
+ uint32_t* end = reinterpret_cast<uint32_t*>(jitCode->end());
+ log("JITCode %p [%p-%p]:", jitCode, start, end);
+ if (start < end) {
+ uint32_t* p = start;
+ while (p + 8 <= end) {
+ log("[%p-%p]: %08x %08x %08x %08x %08x %08x %08x %08x", p, p+7, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+ p += 8;
+ }
+ if (p + 7 <= end)
+ log("[%p-%p]: %08x %08x %08x %08x %08x %08x %08x", p, p+6, p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
+ else if (p + 6 <= end)
+ log("[%p-%p]: %08x %08x %08x %08x %08x %08x", p, p+5, p[0], p[1], p[2], p[3], p[4], p[5]);
+ else if (p + 5 <= end)
+ log("[%p-%p]: %08x %08x %08x %08x %08x", p, p+4, p[0], p[1], p[2], p[3], p[4]);
+ else if (p + 4 <= end)
+ log("[%p-%p]: %08x %08x %08x %08x", p, p+3, p[0], p[1], p[2], p[3]);
+ if (p + 3 <= end)
+ log("[%p-%p]: %08x %08x %08x", p, p+2, p[0], p[1], p[2]);
+ else if (p + 2 <= end)
+ log("[%p-%p]: %08x %08x", p, p+1, p[0], p[1]);
+ else if (p + 1 <= end)
+ log("[%p-%p]: %08x", p, p, p[0]);
+ }
+
+ // Dump the disassembly of the code.
+ log("Disassembly:");
+ uint32_t* currentPC = reinterpret_cast<uint32_t*>(jitCode->executableAddress());
+ size_t byteCount = jitCode->size();
+ while (byteCount) {
+ char pcString[24];
+ if (currentPC == machinePC) {
+ snprintf(pcString, sizeof(pcString), "* 0x%lx", reinterpret_cast<unsigned long>(currentPC));
+ log("%20s: %s <=========================", pcString, m_arm64Opcode.disassemble(currentPC));
+ } else {
+ snprintf(pcString, sizeof(pcString), "0x%lx", reinterpret_cast<unsigned long>(currentPC));
+ log("%20s: %s", pcString, m_arm64Opcode.disassemble(currentPC));
+ }
+ currentPC++;
+ byteCount -= sizeof(uint32_t);
+ }
+#else
+ UNUSED_PARAM(codeBlock);
+ UNUSED_PARAM(machinePC);
+ // Not implemented yet.
+#endif
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/SigillCrashAnalyzer.h b/Source/JavaScriptCore/tools/SigillCrashAnalyzer.h
new file mode 100644
index 000000000..a36b82f7d
--- /dev/null
+++ b/Source/JavaScriptCore/tools/SigillCrashAnalyzer.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+namespace JSC {
+
+// Enables the SIGILL crash analyzer. This is a one way trip. There's no going back.
+void enableSigillCrashAnalyzer();
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/tools/TieredMMapArray.h b/Source/JavaScriptCore/tools/TieredMMapArray.h
index e0dfa3628..1418292ce 100644
--- a/Source/JavaScriptCore/tools/TieredMMapArray.h
+++ b/Source/JavaScriptCore/tools/TieredMMapArray.h
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef TieredMMapArray_h
-#define TieredMMapArray_h
+#pragma once
#include <wtf/OSAllocator.h>
@@ -110,7 +109,4 @@ private:
size_t m_size;
};
-}
-
-#endif // TieredMMapArray_h
-
+} // namespace JSC
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
diff --git a/Source/JavaScriptCore/tools/VMInspector.h b/Source/JavaScriptCore/tools/VMInspector.h
new file mode 100644
index 000000000..b633b4bd7
--- /dev/null
+++ b/Source/JavaScriptCore/tools/VMInspector.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "VM.h"
+#include <wtf/DoublyLinkedList.h>
+#include <wtf/Expected.h>
+#include <wtf/Lock.h>
+
+namespace JSC {
+
+class VMInspector {
+public:
+ enum class Error {
+ None,
+ TimedOut
+ };
+
+ typedef WTF::Locker<Lock> Locker;
+
+ static VMInspector& instance();
+
+ void add(VM*);
+ void remove(VM*);
+
+ Expected<Locker, Error> lock(Seconds timeout = Seconds::infinity());
+
+ Expected<bool, Error> isValidExecutableMemory(const Locker&, void*);
+ Expected<CodeBlock*, Error> codeBlockForMachinePC(const Locker&, void*);
+
+private:
+ enum class FunctorStatus {
+ Continue,
+ Done
+ };
+ template <typename Functor> void iterate(const Functor& functor)
+ {
+ for (VM* vm = m_list.head(); vm; vm = vm->next()) {
+ FunctorStatus status = functor(*vm);
+ if (status == FunctorStatus::Done)
+ return;
+ }
+ }
+
+ Lock m_lock;
+ DoublyLinkedList<VM> m_list;
+};
+
+} // namespace JSC