#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 OS(DARWIN)
#include <cxxabi.h>
#include <dlfcn.h>
#include <execinfo.h>
#endif
namespace JSC {
const char* CodeProfile::s_codeTypeNames[CodeProfile::NumberOfCodeTypes] = {
"[[EngineCode]]",
"[[GlobalThunk]]",
"[[RegExpCode]]",
"[[DFGJIT]]",
"[[BaselineOnly]]",
"[[BaselineProfile]]",
"[[BaselineOSR]]",
"[[EngineFrame]]"
};
static const char* symbolName(void* address)
{
#if OS(DARWIN)
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
}
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()");
}
void CodeProfile::sample(void* pc, void** framePointer)
{
if (!framePointer)
return;
while (framePointer) {
CodeType type;
#if ENABLE(JIT)
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() == JITCode::DFGJIT)
type = DFGJIT;
else if (!canCompile(codeBlock->capabilityLevelState()))
type = BaselineOnly;
else if (codeBlock->replacement())
type = BaselineOSR;
else
type = BaselineProfile;
}
#else
type = EngineFrame;
#endif
m_samples.append(CodeRecord(pc, type));
if (type != EngineFrame)
return;
#if OS(DARWIN) && CPU(X86_64)
pc = framePointer[1];
framePointer = reinterpret_cast<void**>(*framePointer);
#elif OS(LINUX) && CPU(X86)
framePointer = 0;
#else
RELEASE_ASSERT_NOT_REACHED();
#endif
}
m_samples.append(CodeRecord(0, EngineCode));
}
void CodeProfile::report()
{
dataLogF("<CodeProfiling %s:%d>\n", m_file.data(), m_lineNo);
unsigned recursionLimit = CodeProfiling::beVeryVerbose() ? 1024 : CodeProfiling::beVerbose();
ProfileTreeNode profile;
size_t trace = 0;
while (trace < m_samples.size()) {
size_t lastInTrace = trace;
while (m_samples[lastInTrace].type == EngineFrame)
++lastInTrace;
ProfileTreeNode* callbacks = profile.sampleChild(s_codeTypeNames[m_samples[lastInTrace].type]);
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;
}
trace = lastInTrace + 1;
ASSERT(trace <= m_samples.size());
}
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);
}
}