1/* 2 * Copyright (C) 2012 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "CodeProfile.h" 28 29#include "CodeBlock.h" 30#include "CodeProfiling.h" 31#include "LinkBuffer.h" 32#include "ProfileTreeNode.h" 33#include <wtf/Vector.h> 34#include <wtf/text/WTFString.h> 35 36#if OS(DARWIN) 37#include <cxxabi.h> 38#include <dlfcn.h> 39#include <execinfo.h> 40#endif 41 42namespace JSC { 43 44// Map from CodeType enum to a corresponding name. 45const char* CodeProfile::s_codeTypeNames[CodeProfile::NumberOfCodeTypes] = { 46 "[[EngineCode]]", 47 "[[GlobalThunk]]", 48 "[[RegExpCode]]", 49 "[[DFGJIT]]", 50 "[[BaselineOnly]]", 51 "[[BaselineProfile]]", 52 "[[BaselineOSR]]", 53 "[[EngineFrame]]" 54}; 55 56// Helper function, find the symbol name for a pc in JSC. 57static const char* symbolName(void* address) 58{ 59#if OS(DARWIN) 60 Dl_info info; 61 if (!dladdr(address, &info) || !info.dli_sname) 62 return "<unknown>"; 63 64 const char* mangledName = info.dli_sname; 65 const char* cxaDemangled = abi::__cxa_demangle(mangledName, 0, 0, 0); 66 return cxaDemangled ? cxaDemangled : mangledName; 67#else 68 UNUSED_PARAM(address); 69 return "<unknown>"; 70#endif 71} 72 73// Helper function, truncate traces to prune the output & make very verbose mode a little more readable. 74static bool truncateTrace(const char* symbolName) 75{ 76 return !strcmp(symbolName, "JSC::BytecodeGenerator::generate()") 77 || !strcmp(symbolName, "JSC::Parser<JSC::Lexer<unsigned char>>::parseInner()") 78 || !strcmp(symbolName, "WTF::fastMalloc(unsigned long)") 79 || !strcmp(symbolName, "WTF::calculateUTCOffset()") 80 || !strcmp(symbolName, "JSC::DFG::ByteCodeParser::parseCodeBlock()"); 81 82} 83 84// Each trace consists of a sequence of zero or more 'EngineFrame' entries, 85// followed by a sample in JIT code, or one or more 'EngineFrame' entries, 86// followed by a 'EngineCode' terminator. 87void CodeProfile::sample(void* pc, void** framePointer) 88{ 89 // Disallow traces containing only a 'EngineCode' terminator, without any 'EngineFrame' frames. 90 if (!framePointer) 91 return; 92 93 while (framePointer) { 94 CodeType type; 95 96#if ENABLE(JIT) 97 // Determine if this sample fell in JIT code, and if so, from which JIT & why. 98 void* ownerUID = CodeProfiling::getOwnerUIDForPC(pc); 99 100 if (!ownerUID) 101 type = EngineFrame; 102 else if (ownerUID == GLOBAL_THUNK_ID) 103 type = GlobalThunk; 104 else if (ownerUID == REGEXP_CODE_ID) 105 type = RegExpCode; 106 else { 107 CodeBlock* codeBlock = static_cast<CodeBlock*>(ownerUID); 108 if (codeBlock->jitType() == JITCode::DFGJIT) 109 type = DFGJIT; 110 else if (!canCompile(codeBlock->capabilityLevelState())) 111 type = BaselineOnly; 112 else if (codeBlock->replacement()) 113 type = BaselineOSR; 114 else 115 type = BaselineProfile; 116 } 117#else 118 type = EngineFrame; 119#endif 120 121 // A sample in JIT code terminates the trace. 122 m_samples.append(CodeRecord(pc, type)); 123 if (type != EngineFrame) 124 return; 125 126#if OS(DARWIN) && CPU(X86_64) 127 // Walk up the stack. 128 pc = framePointer[1]; 129 framePointer = reinterpret_cast<void**>(*framePointer); 130#elif OS(LINUX) && CPU(X86) 131 // Don't unwind the stack as some dependent third party libraries 132 // may be compiled with -fomit-frame-pointer. 133 framePointer = 0; 134#else 135 // This platform is not yet supported! 136 RELEASE_ASSERT_NOT_REACHED(); 137#endif 138 } 139 140 // If we get here, we walked the entire stack without finding any frames of JIT code. 141 m_samples.append(CodeRecord(0, EngineCode)); 142} 143 144void CodeProfile::report() 145{ 146 dataLogF("<CodeProfiling %s:%d>\n", m_file.data(), m_lineNo); 147 148 // How many frames of C-code to print - 0, if not verbose, 1 if verbose, up to 1024 if very verbose. 149 unsigned recursionLimit = CodeProfiling::beVeryVerbose() ? 1024 : CodeProfiling::beVerbose(); 150 151 ProfileTreeNode profile; 152 153 // Walk through the sample buffer. 154 size_t trace = 0; 155 while (trace < m_samples.size()) { 156 157 // All traces are zero or more 'EngineFrame's, followed by a non-'EngineFrame'. 158 // Scan to find the last sample in the trace. 159 size_t lastInTrace = trace; 160 while (m_samples[lastInTrace].type == EngineFrame) 161 ++lastInTrace; 162 163 // We use the last sample type to look up a name (used as a bucket in the profiler). 164 ProfileTreeNode* callbacks = profile.sampleChild(s_codeTypeNames[m_samples[lastInTrace].type]); 165 166 // If there are any samples in C-code, add up to recursionLimit of them into the profile tree. 167 size_t lastEngineFrame = lastInTrace; 168 for (unsigned count = 0; lastEngineFrame > trace && count < recursionLimit; ++count) { 169 --lastEngineFrame; 170 ASSERT(m_samples[lastEngineFrame].type == EngineFrame); 171 const char* name = symbolName(m_samples[lastEngineFrame].pc); 172 callbacks = callbacks->sampleChild(name); 173 if (truncateTrace(name)) 174 break; 175 } 176 177 // Move on to the next trace. 178 trace = lastInTrace + 1; 179 ASSERT(trace <= m_samples.size()); 180 } 181 182 // Output the profile tree. 183 dataLogF("Total samples: %lld\n", static_cast<long long>(profile.childCount())); 184 profile.dump(); 185 186 for (size_t i = 0 ; i < m_children.size(); ++i) 187 m_children[i]->report(); 188 189 dataLogF("</CodeProfiling %s:%d>\n", m_file.data(), m_lineNo); 190} 191 192} 193