summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/tools
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-02-03 09:55:33 +0100
committerSimon Hausmann <simon.hausmann@nokia.com>2012-02-03 09:55:33 +0100
commitcd44dc59cdfc39534aef4d417e9f3c412e3be139 (patch)
tree8d89889ba95ed6ec9322e733846cc9cce9d7dff1 /Source/JavaScriptCore/tools
parentd11f84f5b5cdc0d92a08af01b13472fdd5f9acb9 (diff)
downloadqtwebkit-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.cpp188
-rw-r--r--Source/JavaScriptCore/tools/CodeProfile.h93
-rw-r--r--Source/JavaScriptCore/tools/CodeProfiling.cpp163
-rw-r--r--Source/JavaScriptCore/tools/CodeProfiling.h85
-rw-r--r--Source/JavaScriptCore/tools/ProfileTreeNode.h123
-rw-r--r--Source/JavaScriptCore/tools/TieredMMapArray.h117
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
+