blob: 7f6ae661144686af89437277af5f163c365a89c8 [file] [log] [blame]
/*
* 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/StackTrace.h>
#include <wtf/text/WTFString.h>
namespace JSC {
// Map from CodeType enum to a corresponding name.
static const char* const s_codeTypeNames[CodeProfile::NumberOfCodeTypes] = {
"[[EngineCode]]",
"[[GlobalThunk]]",
"[[RegExpCode]]",
"[[DFGJIT]]",
"[[BaselineOnly]]",
"[[BaselineProfile]]",
"[[BaselineOSR]]",
"[[EngineFrame]]"
};
// 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->jitType() == JITType::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<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!
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("<CodeProfiling %s:%d>\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 = "<unknown>";
auto demangled = StackTrace::demangle(m_samples[lastEngineFrame].pc);
if (demangled)
name = demangled->demangledName() ? demangled->demangledName() : demangled->mangledName();
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_lineNumber);
}
}