]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - tools/CodeProfile.cpp
JavaScriptCore-1097.3.tar.gz
[apple/javascriptcore.git] / tools / CodeProfile.cpp
diff --git a/tools/CodeProfile.cpp b/tools/CodeProfile.cpp
new file mode 100644 (file)
index 0000000..beed4b2
--- /dev/null
@@ -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 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;
+
+#if PLATFORM(MAC) && 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!
+        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()
+{
+    dataLog("<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.
+    dataLog("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();
+
+    dataLog("</CodeProfiling %s:%d>\n", m_file.data(), m_lineNo);
+}
+
+}