| /* |
| * 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() != DFG::CanCompile) |
| 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() |
| { |
| dataLogF("<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. |
| 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_lineNo); |
| } |
| |
| } |