/*
 * 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 "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() == CodeBlock::CompileWithDFGFalse)
                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()
{
    dataLog("<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.
    dataLog("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();

    dataLog("</CodeProfiling %s:%d>\n", m_file.data(), m_lineNo);
}

}
