diff options
| author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-10-15 09:45:50 +0000 |
|---|---|---|
| committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-10-15 09:45:50 +0000 |
| commit | e15dd966d523731101f70ccf768bba12435a0208 (patch) | |
| tree | ae9cb828a24ded2585a41af3f21411523b47897d /Source/JavaScriptCore/tools | |
| download | WebKitGtk-tarball-e15dd966d523731101f70ccf768bba12435a0208.tar.gz | |
webkitgtk-2.10.2webkitgtk-2.10.2
Diffstat (limited to 'Source/JavaScriptCore/tools')
| -rw-r--r-- | Source/JavaScriptCore/tools/CodeProfile.cpp | 192 | ||||
| -rw-r--r-- | Source/JavaScriptCore/tools/CodeProfile.h | 94 | ||||
| -rw-r--r-- | Source/JavaScriptCore/tools/CodeProfiling.cpp | 177 | ||||
| -rw-r--r-- | Source/JavaScriptCore/tools/CodeProfiling.h | 85 | ||||
| -rw-r--r-- | Source/JavaScriptCore/tools/FunctionOverrides.cpp | 250 | ||||
| -rw-r--r-- | Source/JavaScriptCore/tools/FunctionOverrides.h | 64 | ||||
| -rw-r--r-- | Source/JavaScriptCore/tools/JSDollarVM.cpp | 36 | ||||
| -rw-r--r-- | Source/JavaScriptCore/tools/JSDollarVM.h | 60 | ||||
| -rw-r--r-- | Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp | 424 | ||||
| -rw-r--r-- | Source/JavaScriptCore/tools/JSDollarVMPrototype.h | 81 | ||||
| -rw-r--r-- | Source/JavaScriptCore/tools/ProfileTreeNode.h | 123 | ||||
| -rw-r--r-- | Source/JavaScriptCore/tools/TieredMMapArray.h | 116 |
12 files changed, 1702 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/tools/CodeProfile.cpp b/Source/JavaScriptCore/tools/CodeProfile.cpp new file mode 100644 index 000000000..c64f1d5a6 --- /dev/null +++ b/Source/JavaScriptCore/tools/CodeProfile.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2012 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 "CodeProfile.h" + +#include "CodeBlock.h" +#include "CodeProfiling.h" +#include "LinkBuffer.h" +#include "ProfileTreeNode.h" +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +#if OS(DARWIN) +#include <cxxabi.h> +#include <dlfcn.h> +#include <execinfo.h> +#endif + +namespace JSC { + +// Map from CodeType enum to a corresponding name. +const char* CodeProfile::s_codeTypeNames[CodeProfile::NumberOfCodeTypes] = { + "[[EngineCode]]", + "[[GlobalThunk]]", + "[[RegExpCode]]", + "[[DFGJIT]]", + "[[BaselineOnly]]", + "[[BaselineProfile]]", + "[[BaselineOSR]]", + "[[EngineFrame]]" +}; + +// Helper function, find the symbol name for a pc in JSC. +static const char* symbolName(void* address) +{ +#if OS(DARWIN) + Dl_info info; + if (!dladdr(address, &info) || !info.dli_sname) + return "<unknown>"; + + const char* mangledName = info.dli_sname; + const char* cxaDemangled = abi::__cxa_demangle(mangledName, 0, 0, 0); + return cxaDemangled ? cxaDemangled : mangledName; +#else + UNUSED_PARAM(address); + return "<unknown>"; +#endif +} + +// Helper function, truncate traces to prune the output & make very verbose mode a little more readable. +static bool truncateTrace(const char* symbolName) +{ + return !strcmp(symbolName, "JSC::BytecodeGenerator::generate()") + || !strcmp(symbolName, "JSC::Parser<JSC::Lexer<unsigned char>>::parseInner()") + || !strcmp(symbolName, "WTF::fastMalloc(unsigned long)") + || !strcmp(symbolName, "WTF::calculateUTCOffset()") + || !strcmp(symbolName, "JSC::DFG::ByteCodeParser::parseCodeBlock()"); + +} + +// Each trace consists of a sequence of zero or more 'EngineFrame' entries, +// followed by a sample in JIT code, or one or more 'EngineFrame' entries, +// followed by a 'EngineCode' terminator. +void CodeProfile::sample(void* pc, void** framePointer) +{ + // Disallow traces containing only a 'EngineCode' terminator, without any 'EngineFrame' frames. + if (!framePointer) + return; + + while (framePointer) { + CodeType type; + +#if ENABLE(JIT) + // Determine if this sample fell in JIT code, and if so, from which JIT & why. + void* ownerUID = CodeProfiling::getOwnerUIDForPC(pc); + + if (!ownerUID) + type = EngineFrame; + else if (ownerUID == GLOBAL_THUNK_ID) + type = GlobalThunk; + else if (ownerUID == REGEXP_CODE_ID) + type = RegExpCode; + else { + CodeBlock* codeBlock = static_cast<CodeBlock*>(ownerUID); + if (codeBlock->jitType() == JITCode::DFGJIT) + type = DFGJIT; + else if (!canCompile(codeBlock->capabilityLevelState())) + type = BaselineOnly; + else if (codeBlock->replacement()) + type = BaselineOSR; + else + type = BaselineProfile; + } +#else + type = EngineFrame; +#endif + + // A sample in JIT code terminates the trace. + m_samples.append(CodeRecord(pc, type)); + if (type != EngineFrame) + return; + +#if OS(DARWIN) && CPU(X86_64) + // Walk up the stack. + pc = framePointer[1]; + framePointer = reinterpret_cast<void**>(*framePointer); +#elif OS(LINUX) && CPU(X86) + // Don't unwind the stack as some dependent third party libraries + // may be compiled with -fomit-frame-pointer. + framePointer = 0; +#else + // This platform is not yet supported! + RELEASE_ASSERT_NOT_REACHED(); +#endif + } + + // If we get here, we walked the entire stack without finding any frames of JIT code. + m_samples.append(CodeRecord(0, EngineCode)); +} + +void CodeProfile::report() +{ + 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(); + + ProfileTreeNode profile; + + // Walk through the sample buffer. + size_t trace = 0; + while (trace < m_samples.size()) { + + // All traces are zero or more 'EngineFrame's, followed by a non-'EngineFrame'. + // Scan to find the last sample in the trace. + size_t lastInTrace = trace; + while (m_samples[lastInTrace].type == EngineFrame) + ++lastInTrace; + + // We use the last sample type to look up a name (used as a bucket in the profiler). + ProfileTreeNode* callbacks = profile.sampleChild(s_codeTypeNames[m_samples[lastInTrace].type]); + + // If there are any samples in C-code, add up to recursionLimit of them into the profile tree. + size_t lastEngineFrame = lastInTrace; + for (unsigned count = 0; lastEngineFrame > trace && count < recursionLimit; ++count) { + --lastEngineFrame; + ASSERT(m_samples[lastEngineFrame].type == EngineFrame); + const char* name = symbolName(m_samples[lastEngineFrame].pc); + callbacks = callbacks->sampleChild(name); + if (truncateTrace(name)) + break; + } + + // Move on to the next trace. + trace = lastInTrace + 1; + ASSERT(trace <= m_samples.size()); + } + + // Output the profile tree. + dataLogF("Total samples: %lld\n", static_cast<long long>(profile.childCount())); + profile.dump(); + + for (size_t i = 0 ; i < m_children.size(); ++i) + m_children[i]->report(); + + dataLogF("</CodeProfiling %s:%d>\n", m_file.data(), m_lineNumber); +} + +} diff --git a/Source/JavaScriptCore/tools/CodeProfile.h b/Source/JavaScriptCore/tools/CodeProfile.h new file mode 100644 index 000000000..42f6de50c --- /dev/null +++ b/Source/JavaScriptCore/tools/CodeProfile.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2012 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. + */ + +#ifndef CodeProfile_h +#define CodeProfile_h + +#include "SourceCode.h" +#include "TieredMMapArray.h" +#include <wtf/text/CString.h> + +namespace JSC { + +class CodeProfile { + WTF_MAKE_FAST_ALLOCATED; +public: + CodeProfile(const SourceCode& source, CodeProfile* parent) + : m_file(source.provider()->url().utf8()) + , m_lineNumber(source.firstLine()) + , m_parent(parent) + { + if (parent) + parent->addChild(std::unique_ptr<CodeProfile>(this)); + } + + void sample(void* pc, void** framePointer); + void report(); + + CodeProfile* parent() + { + return m_parent; + } + + void addChild(std::unique_ptr<CodeProfile> child) + { + m_children.append(WTF::move(child)); + } + +private: + enum CodeType { + EngineCode, + GlobalThunk, + RegExpCode, + DFGJIT, + BaselineOnly, + BaselineProfile, + BaselineOSR, + EngineFrame, + NumberOfCodeTypes + }; + struct CodeRecord { + CodeRecord(void* pc, CodeType type) + : pc(pc) + , type(type) + { + } + void* pc; + CodeType type; + }; + + CString m_file; + unsigned m_lineNumber; + CodeProfile* m_parent; + Vector<std::unique_ptr<CodeProfile>> m_children; + TieredMMapArray<CodeRecord> m_samples; + + static const char* s_codeTypeNames[NumberOfCodeTypes]; +}; + +} + +#endif // CodeProfile_h + diff --git a/Source/JavaScriptCore/tools/CodeProfiling.cpp b/Source/JavaScriptCore/tools/CodeProfiling.cpp new file mode 100644 index 000000000..302b5f929 --- /dev/null +++ b/Source/JavaScriptCore/tools/CodeProfiling.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2012 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 "CodeProfiling.h" + +#include "CodeProfile.h" +#include <wtf/MetaAllocator.h> + +#if HAVE(SIGNAL_H) +#include <signal.h> +#endif + +#if OS(LINUX) || OS(DARWIN) +#include <sys/time.h> +#endif + +namespace JSC { + +volatile CodeProfile* CodeProfiling::s_profileStack = 0; +CodeProfiling::Mode CodeProfiling::s_mode = CodeProfiling::Disabled; +WTF::MetaAllocatorTracker* CodeProfiling::s_tracker = 0; + +#if COMPILER(CLANG) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-noreturn" +#endif + +#if (OS(DARWIN) && !PLATFORM(EFL) && !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) +{ + itimerval timer; + timer.it_value.tv_sec = 0; + timer.it_value.tv_usec = usec; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = usec; + setitimer(ITIMER_REAL, &timer, 0); +} +#endif + +#if COMPILER(CLANG) +#pragma clang diagnostic pop +#endif + +#if OS(DARWIN) && !PLATFORM(EFL) && !PLATFORM(GTK) && CPU(X86_64) +static void profilingTimer(int, siginfo_t*, void* uap) +{ + mcontext_t context = static_cast<ucontext_t*>(uap)->uc_mcontext; + CodeProfiling::sample(reinterpret_cast<void*>(context->__ss.__rip), + reinterpret_cast<void**>(context->__ss.__rbp)); +} +#elif OS(LINUX) && CPU(X86) +static void profilingTimer(int, siginfo_t*, void* uap) +{ + mcontext_t context = static_cast<ucontext_t*>(uap)->uc_mcontext; + CodeProfiling::sample(reinterpret_cast<void*>(context.gregs[REG_EIP]), + reinterpret_cast<void**>(context.gregs[REG_EBP])); +} +#endif + +// Callback triggered when the timer is fired. +void CodeProfiling::sample(void* pc, void** framePointer) +{ + CodeProfile* profileStack = const_cast<CodeProfile*>(s_profileStack); + if (profileStack) + profileStack->sample(pc, framePointer); +} + +void CodeProfiling::notifyAllocator(WTF::MetaAllocator* allocator) +{ + // Check for JSC_CODE_PROFILING. + const char* codeProfilingMode = getenv("JSC_CODE_PROFILING"); + if (!codeProfilingMode) + return; + + // Check for a valid mode, currently "1", "2", or "3". + if (!codeProfilingMode[0] || codeProfilingMode[1]) + return; + switch (*codeProfilingMode) { + case '1': + s_mode = Enabled; + break; + case '2': + s_mode = Verbose; + break; + case '3': + s_mode = VeryVerbose; + break; + default: + return; + } + + ASSERT(enabled()); + ASSERT(!s_tracker); + s_tracker = new WTF::MetaAllocatorTracker(); + allocator->trackAllocations(s_tracker); +} + +void* CodeProfiling::getOwnerUIDForPC(void* address) +{ + if (!s_tracker) + return 0; + WTF::MetaAllocatorHandle* handle = s_tracker->find(address); + if (!handle) + return 0; + return handle->ownerUID(); +} + +void CodeProfiling::begin(const SourceCode& source) +{ + // Push a new CodeProfile onto the stack for each script encountered. + CodeProfile* profileStack = const_cast<CodeProfile*>(s_profileStack); + bool alreadyProfiling = profileStack; + s_profileStack = profileStack = new CodeProfile(source, profileStack); + + // Is the profiler already running - if so, the timer will already be set up. + if (alreadyProfiling) + return; + +#if (OS(DARWIN) && !PLATFORM(EFL) && !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); + sigfillset(&action.sa_mask); + action.sa_flags = SA_SIGINFO; + sigaction(SIGALRM, &action, 0); + setProfileTimer(100); +#endif +} + +void CodeProfiling::end() +{ + // Pop the current profiler off the stack. + CodeProfile* current = const_cast<CodeProfile*>(s_profileStack); + ASSERT(current); + s_profileStack = current->parent(); + + // Is this the outermost script being profiled? - if not, just return. + // We perform all output of profiles recursively from the outermost script, + // to minimize profiling overhead from skewing results. + if (s_profileStack) + return; + +#if (OS(DARWIN) && !PLATFORM(EFL) && !PLATFORM(GTK) && CPU(X86_64)) || (OS(LINUX) && CPU(X86)) + // Stop profiling + setProfileTimer(0); +#endif + + current->report(); + delete current; +} + +} diff --git a/Source/JavaScriptCore/tools/CodeProfiling.h b/Source/JavaScriptCore/tools/CodeProfiling.h new file mode 100644 index 000000000..9b0f5daa3 --- /dev/null +++ b/Source/JavaScriptCore/tools/CodeProfiling.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2012 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. + */ + +#ifndef CodeProfiling_h +#define CodeProfiling_h + +namespace WTF { + +class MetaAllocator; +class MetaAllocatorTracker; + +} + +namespace JSC { + +class CodeProfile; +class SourceCode; + +class CodeProfiling { + enum Mode { + Disabled, + Enabled, + Verbose, + VeryVerbose + }; + +public: + CodeProfiling(const SourceCode& source) + : m_active(enabled()) + { + if (m_active) + begin(source); + } + + ~CodeProfiling() + { + if (m_active) + end(); + } + + static bool enabled() { return s_mode != Disabled; } + static bool beVerbose() { return s_mode >= Verbose; } + static bool beVeryVerbose() { return s_mode >= VeryVerbose; } + + static void notifyAllocator(WTF::MetaAllocator*); + static void* getOwnerUIDForPC(void*); + static void sample(void* pc, void** framePointer); + +private: + void begin(const SourceCode&); + void end(); + + bool m_active; + + static Mode s_mode; + static WTF::MetaAllocatorTracker* s_tracker; + static volatile CodeProfile* s_profileStack; +}; + +} + +#endif // CodeProfiling_h + diff --git a/Source/JavaScriptCore/tools/FunctionOverrides.cpp b/Source/JavaScriptCore/tools/FunctionOverrides.cpp new file mode 100644 index 000000000..2034d60f5 --- /dev/null +++ b/Source/JavaScriptCore/tools/FunctionOverrides.cpp @@ -0,0 +1,250 @@ +/* + * 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 "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); +} + +static void initializeOverrideInfo(const SourceCode& origCode, const String& newBody, FunctionOverrides::OverrideInfo& info) +{ + String origProviderStr = origCode.provider()->source(); + unsigned origBraceStart = origCode.startOffset(); + unsigned origFunctionStart = origProviderStr.reverseFind("function", origBraceStart); + unsigned headerLength = origBraceStart - origFunctionStart; + String origHeader = origProviderStr.substring(origFunctionStart, headerLength); + + String newProviderStr; + newProviderStr.append(origHeader); + newProviderStr.append(newBody); + + RefPtr<SourceProvider> newProvider = StringSourceProvider::create(newProviderStr, "<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(newProvider.release(), info.typeProfilingStartOffset, info.typeProfilingEndOffset + 1, 1, 1); +} + +bool FunctionOverrides::initializeOverrideFor(const SourceCode& origCode, FunctionOverrides::OverrideInfo& result) +{ + ASSERT(Options::functionOverrides()); + FunctionOverrides& overrides = FunctionOverrides::overrides(); + + auto it = overrides.m_entries.find(origCode.toString()); + 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); + + const char* terminator = terminatorString.ascii().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..a0d8ad47a --- /dev/null +++ b/Source/JavaScriptCore/tools/FunctionOverrides.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef FunctionOverrides_h +#define FunctionOverrides_h + +#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); + +private: + void parseOverridesInFile(const char* fileName); + + HashMap<String, String> m_entries; +}; + +} // namespace JSC + +#endif // FunctionOverrides_h diff --git a/Source/JavaScriptCore/tools/JSDollarVM.cpp b/Source/JavaScriptCore/tools/JSDollarVM.cpp new file mode 100644 index 000000000..ee5c3d18e --- /dev/null +++ b/Source/JavaScriptCore/tools/JSDollarVM.cpp @@ -0,0 +1,36 @@ +/* + * 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 "JSCJSValueInlines.h" +#include "StructureInlines.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..6b5be038f --- /dev/null +++ b/Source/JavaScriptCore/tools/JSDollarVM.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef JSDollarVM_h +#define JSDollarVM_h + +#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 + +#endif // JSDollarVM_h diff --git a/Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp b/Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp new file mode 100644 index 000000000..26a8e77b1 --- /dev/null +++ b/Source/JavaScriptCore/tools/JSDollarVMPrototype.cpp @@ -0,0 +1,424 @@ +/* + * 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 "JSDollarVMPrototype.h" + +#include "Heap.h" +#include "HeapIterationScope.h" +#include "JSCInlines.h" +#include "JSFunction.h" +#include "StackVisitor.h" +#include <wtf/DataLog.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) + { + if (m_currentFrame++ > 1) { + m_jitType = visitor->codeBlock()->jitType(); + return StackVisitor::Done; + } + return StackVisitor::Continue; + } + + JITCode::JITType jitType() { return m_jitType; } + +private: + unsigned m_currentFrame; + 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()->collectAndSweep(EdenCollection); +} + +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); + return heap->objectSpace().blocks().set().contains(candidate); +} + +bool JSDollarVMPrototype::isInStorageSpace(Heap* heap, void* ptr) +{ + CopiedBlock* candidate = CopiedSpace::blockFor(ptr); + return heap->storageSpace().contains(candidate); +} + +struct CellAddressCheckFunctor : MarkedBlock::CountFunctor { + CellAddressCheckFunctor(JSCell* candidate) + : candidate(candidate) + { + } + + IterationStatus operator()(JSCell* cell) + { + if (cell == candidate) { + found = true; + return IterationStatus::Done; + } + return IterationStatus::Continue; + } + + JSCell* candidate; + 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) + { + if (codeBlock == candidate) + found = true; + return found; + } + + CodeBlock* candidate; + 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) + { + currentFrame++; + if (currentFrame == targetFrame) { + codeBlock = visitor->codeBlock(); + return StackVisitor::Done; + } + return StackVisitor::Continue; + } + + unsigned targetFrame; + unsigned currentFrame { 0 }; + 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); + return JSValue::encode(JSValue(bitwise_cast<double>(reinterpret_cast<uint64_t>(codeBlock)))); +} + +static CodeBlock* codeBlockFromArg(ExecState* exec) +{ + if (exec->argumentCount() < 1) + return nullptr; + + JSValue value = exec->uncheckedArgument(0); + if (!value.isDouble()) { + dataLog("Invalid codeBlock: ", value, "\n"); + return nullptr; + } + + CodeBlock* codeBlock = reinterpret_cast<CodeBlock*>(bitwise_cast<uint64_t>(value.asDouble())); + if (JSDollarVMPrototype::isValidCodeBlock(exec, codeBlock)) + return codeBlock; + + dataLogF("Invalid codeBlock: %p ", codeBlock); + dataLog(value, "\n"); + return nullptr; + +} + +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) +{ + for (unsigned i = 0; i < exec->argumentCount(); ++i) { + if (i) + dataLog(" "); + dataLog(exec->uncheckedArgument(i).toString(exec)->value(exec)); + } + 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) + { + m_currentFrame++; + if (m_currentFrame > m_framesToSkip) + visitor->print(2); + + if (m_action == PrintOne && m_currentFrame > m_framesToSkip) + return StackVisitor::Done; + return StackVisitor::Continue; + } + +private: + Action m_action; + unsigned m_framesToSkip; + 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 functionPrintValue(ExecState* exec) +{ + for (unsigned i = 0; i < exec->argumentCount(); ++i) { + if (i) + dataLog(" "); + dataLog(exec->uncheckedArgument(i)); + } + return JSValue::encode(jsUndefined()); +} + +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 | JSC::Function); + + 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, "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, "printValue", functionPrintValue, 1); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/tools/JSDollarVMPrototype.h b/Source/JavaScriptCore/tools/JSDollarVMPrototype.h new file mode 100644 index 000000000..4c58be299 --- /dev/null +++ b/Source/JavaScriptCore/tools/JSDollarVMPrototype.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#ifndef JSDollarVMPrototype_h +#define JSDollarVMPrototype_h + +#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 + +#endif // JSDollarVMPrototype_h diff --git a/Source/JavaScriptCore/tools/ProfileTreeNode.h b/Source/JavaScriptCore/tools/ProfileTreeNode.h new file mode 100644 index 000000000..88d57f3dd --- /dev/null +++ b/Source/JavaScriptCore/tools/ProfileTreeNode.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2012 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. + */ + +#ifndef ProfileTreeNode_h +#define ProfileTreeNode_h + +namespace JSC { + +class ProfileTreeNode { + typedef HashMap<String, ProfileTreeNode> Map; + typedef Map::KeyValuePairType MapEntry; + +public: + ProfileTreeNode() + : m_count(0) + , m_children(0) + { + } + + ~ProfileTreeNode() + { + delete m_children; + } + + ProfileTreeNode* sampleChild(const char* name) + { + if (!m_children) + m_children = new Map(); + + ProfileTreeNode newEntry; + Map::AddResult result = m_children->add(String(name), newEntry); + ProfileTreeNode* childInMap = &result.iterator->value; + ++childInMap->m_count; + return childInMap; + } + + void dump() + { + dumpInternal(0); + } + + uint64_t count() + { + return m_count; + } + + uint64_t childCount() + { + if (!m_children) + return 0; + uint64_t childCount = 0; + for (Map::iterator it = m_children->begin(); it != m_children->end(); ++it) + childCount += it->value.count(); + return childCount; + } + +private: + void dumpInternal(unsigned indent) + { + if (!m_children) + return; + + // Copy pointers to all children into a vector, and sort the vector by sample count. + Vector<MapEntry*> entries; + for (Map::iterator it = m_children->begin(); it != m_children->end(); ++it) + entries.append(&*it); + 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]; + + // 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) + dataLogF(" "); + dataLogF("% 8lld: %s (%lld stack top)\n", + static_cast<long long>(entry->value.count()), + entry->key.utf8().data(), + static_cast<long long>(entry->value.count() - entry->value.childCount())); + + // Recursively dump the child nodes. + entry->value.dumpInternal(indent + 1); + } + } + + static int compareEntries(const void* a, const void* b) + { + uint64_t da = (*static_cast<MapEntry* const *>(a))->value.count(); + uint64_t db = (*static_cast<MapEntry* const *>(b))->value.count(); + return (da < db) - (da > db); + } + + uint64_t m_count; + Map* m_children; +}; + +} + +#endif // ProfileTreeNode_h + diff --git a/Source/JavaScriptCore/tools/TieredMMapArray.h b/Source/JavaScriptCore/tools/TieredMMapArray.h new file mode 100644 index 000000000..e0dfa3628 --- /dev/null +++ b/Source/JavaScriptCore/tools/TieredMMapArray.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2012 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. + */ + +#ifndef TieredMMapArray_h +#define TieredMMapArray_h + +#include <wtf/OSAllocator.h> + +namespace JSC { + +// This class implements a simple array class that can be grown by appending items to the end. +// This class is implemented purely in terms of system allocations, with no malloc/free, so that +// it can safely be used from a secondary thread whilst the main thrad is paused (potentially +// holding the fast malloc heap lock). +template<typename T> +class TieredMMapArray { + static const size_t entriesPerBlock = 4096; + +public: + TieredMMapArray() + : m_directoryCount(4096) + , m_directory(static_cast<T**>(OSAllocator::reserveAndCommit(m_directoryCount * sizeof(T*)))) + , m_size(0) + { + for (size_t block = 0; block < m_directoryCount; ++block) + m_directory[block] = 0; + } + + ~TieredMMapArray() + { + size_t usedCount = (m_size + (entriesPerBlock - 1)) / entriesPerBlock; + ASSERT(usedCount == m_directoryCount || !m_directory[usedCount]); + + for (size_t block = 0; block < usedCount; ++block) { + ASSERT(m_directory[block]); + OSAllocator::decommitAndRelease(m_directory[block], entriesPerBlock * sizeof(T)); + } + + OSAllocator::decommitAndRelease(m_directory, m_directoryCount * sizeof(T*)); + } + + T& operator[](size_t index) + { + ASSERT(index < m_size); + size_t block = index / entriesPerBlock; + size_t offset = index % entriesPerBlock; + + ASSERT(m_directory[block]); + return m_directory[block][offset]; + } + + void append(const T& value) + { + // Check if the array is completely full, if so create more capacity in the directory. + if (m_size == m_directoryCount * entriesPerBlock) { + // Reallocate the directory. + size_t oldDirectorySize = m_directoryCount * sizeof(T*); + size_t newDirectorySize = oldDirectorySize * 2; + RELEASE_ASSERT(newDirectorySize < oldDirectorySize); + m_directory = OSAllocator::reallocateCommitted(m_directory, oldDirectorySize, newDirectorySize); + + // + size_t newDirectoryCount = m_directoryCount * 2; + for (size_t block = m_directoryCount; block < newDirectoryCount; ++block) + m_directory[block] = 0; + m_directoryCount = newDirectoryCount; + } + + size_t index = m_size; + size_t block = index / entriesPerBlock; + size_t offset = index % entriesPerBlock; + + if (!offset) { + ASSERT(!m_directory[block]); + m_directory[block] = static_cast<T*>(OSAllocator::reserveAndCommit(entriesPerBlock * sizeof(T))); + } + + ASSERT(m_directory[block]); + ++m_size; + m_directory[block][offset] = value; + } + + size_t size() const { return m_size; } + +private: + size_t m_directoryCount; + T** m_directory; + size_t m_size; +}; + +} + +#endif // TieredMMapArray_h + |
