/* * 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 #if OS(DARWIN) #include #include #include #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 ""; 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 ""; #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>::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(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(*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("\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(profile.childCount())); profile.dump(); for (size_t i = 0 ; i < m_children.size(); ++i) m_children[i]->report(); dataLogF("\n", m_file.data(), m_lineNumber); } }