diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-02-03 09:55:33 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-02-03 09:55:33 +0100 |
commit | cd44dc59cdfc39534aef4d417e9f3c412e3be139 (patch) | |
tree | 8d89889ba95ed6ec9322e733846cc9cce9d7dff1 /Source/JavaScriptCore/tools | |
parent | d11f84f5b5cdc0d92a08af01b13472fdd5f9acb9 (diff) | |
download | qtwebkit-cd44dc59cdfc39534aef4d417e9f3c412e3be139.tar.gz |
Imported WebKit commit fce473cb4d55aa9fe9d0b0322a2fffecb731b961 (http://svn.webkit.org/repository/webkit/trunk@106560)
Diffstat (limited to 'Source/JavaScriptCore/tools')
-rw-r--r-- | Source/JavaScriptCore/tools/CodeProfile.cpp | 188 | ||||
-rw-r--r-- | Source/JavaScriptCore/tools/CodeProfile.h | 93 | ||||
-rw-r--r-- | Source/JavaScriptCore/tools/CodeProfiling.cpp | 163 | ||||
-rw-r--r-- | Source/JavaScriptCore/tools/CodeProfiling.h | 85 | ||||
-rw-r--r-- | Source/JavaScriptCore/tools/ProfileTreeNode.h | 123 | ||||
-rw-r--r-- | Source/JavaScriptCore/tools/TieredMMapArray.h | 117 |
6 files changed, 769 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/tools/CodeProfile.cpp b/Source/JavaScriptCore/tools/CodeProfile.cpp new file mode 100644 index 000000000..7794f58d3 --- /dev/null +++ b/Source/JavaScriptCore/tools/CodeProfile.cpp @@ -0,0 +1,188 @@ +/* + * 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 "Vector.h" +#include <wtf/text/WTFString.h> + +#if PLATFORM(MAC) +#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 PLATFORM(MAC) + 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->getJITType() == JITCode::DFGJIT) + type = DFGJIT; + else if (codeBlock->canCompileWithDFGState() == CodeBlock::CompileWithDFGFalse) + 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; + + // Walk up the stack. +#if PLATFORM(MAC) && CPU(X86_64) + pc = framePointer[1]; + framePointer = reinterpret_cast<void**>(*framePointer); +#else + // This platform is not yet supported! + 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() +{ + fprintf(stdout, "<CodeProfiling %s:%d>\n", m_file.data(), m_lineNo); + + // 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. + fprintf(stdout, "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(); + + fprintf(stdout, "</CodeProfiling %s:%d>\n", m_file.data(), m_lineNo); +} + +} diff --git a/Source/JavaScriptCore/tools/CodeProfile.h b/Source/JavaScriptCore/tools/CodeProfile.h new file mode 100644 index 000000000..ea360bcbc --- /dev/null +++ b/Source/JavaScriptCore/tools/CodeProfile.h @@ -0,0 +1,93 @@ +/* + * 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 { +public: + CodeProfile(const SourceCode& source, CodeProfile* parent) + : m_file(source.provider()->url().utf8()) + , m_lineNo(source.firstLine()) + , m_parent(parent) + { + if (parent) + parent->addChild(this); + } + + void sample(void* pc, void** framePointer); + void report(); + + CodeProfile* parent() + { + return m_parent; + } + + void addChild(CodeProfile* child) + { + m_children.append(adoptPtr(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_lineNo; + CodeProfile* m_parent; + Vector< OwnPtr<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..f236484c9 --- /dev/null +++ b/Source/JavaScriptCore/tools/CodeProfiling.cpp @@ -0,0 +1,163 @@ +/* + * 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 "MetaAllocator.h" +#include "signal.h" + +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 PLATFORM(MAC) && CPU(X86_64) +// 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 PLATFORM(MAC) && 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)); +} +#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 PLATFORM(MAC) && CPU(X86_64) + // Regsiter a signal handler & itimer. + struct sigaction action; + action.sa_sigaction = reinterpret_cast<void (*)(int, struct __siginfo *, 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 PLATFORM(MAC) && CPU(X86_64) + // 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/ProfileTreeNode.h b/Source/JavaScriptCore/tools/ProfileTreeNode.h new file mode 100644 index 000000000..6c5fdc185 --- /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 std::pair<String, ProfileTreeNode> 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; + pair<Map::iterator, bool> result = m_children->add(String(name), newEntry); + ProfileTreeNode* childInMap = &result.first->second; + ++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->second.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) + fprintf(stdout, " "); + fprintf(stdout, "% 8lld: %s (%lld stack top)\n", + static_cast<long long>(entry->second.count()), + entry->first.utf8().data(), + static_cast<long long>(entry->second.count() - entry->second.childCount())); + + // Recursively dump the child nodes. + entry->second.dumpInternal(indent + 1); + } + } + + static int compareEntries(const void* a, const void* b) + { + uint64_t da = (*static_cast<MapEntry* const *>(a))->second.count(); + uint64_t db = (*static_cast<MapEntry* const *>(b))->second.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..fa6e5ae1f --- /dev/null +++ b/Source/JavaScriptCore/tools/TieredMMapArray.h @@ -0,0 +1,117 @@ +/* + * 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 "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; + if (newDirectorySize < oldDirectorySize) + CRASH(); + 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 + |