[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/WebKit/WebKit/main/Source/JavaScriptCore/jit/ICStats.cpp [Back]  [Original]

/*
 * Copyright (C) 2016-2023 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 "ICStats.h"

#include 
#include 
#include 
#include 

namespace JSC {

WTF_MAKE_TZONE_ALLOCATED_IMPL(ICStats);

class ICHandlerChain {
public:
    ICHandlerChain() = default;

    ICHandlerChain(WTF::HashTableDeletedValueType)
        : m_isDeleted(true)
    {
    }

    void append(ICEvent::Kind kind) { m_chain.append(kind); }
    bool NODELETE isEmpty() const { return m_chain.isEmpty(); }
    void NODELETE clear()
    {
        m_chain.shrink(0);
        m_totalNumberOfHandlersInChain = 0;
    }
    unsigned NODELETE size() const { return m_chain.size(); }
    ICEvent::Kind operator[](unsigned i) const { return m_chain[i]; }

    void NODELETE setTotalNumberOfHandlersInChain(unsigned length) { m_totalNumberOfHandlersInChain = length; }
    unsigned NODELETE totalNumberOfHandlersInChain() const { return m_totalNumberOfHandlersInChain; }

    bool operator==(const ICHandlerChain& other) const
    {
        return m_chain == other.m_chain && m_totalNumberOfHandlersInChain == other.m_totalNumberOfHandlersInChain && m_isDeleted == other.m_isDeleted;
    }

    bool operator>(const ICHandlerChain& other) const
    {
        for (unsigned i = 0; i < std::min(m_chain.size(), other.m_chain.size()); i++) {
            if (m_chain[i] != other.m_chain[i])
                return m_chain[i] > other.m_chain[i];
        }
        if (m_chain.size() != other.m_chain.size())
            return m_chain.size() > other.m_chain.size();
        return m_totalNumberOfHandlersInChain > other.m_totalNumberOfHandlersInChain;
    }

    unsigned hash() const
    {
        return pairIntHash(computeHash(m_chain), WTF::IntHash::hash(m_totalNumberOfHandlersInChain));
    }

    bool isHashTableDeletedValue() const { return m_isDeleted; }

    static constexpr bool safeToCompareToHashTableEmptyOrDeletedValue = true;

private:
    Vector m_chain;
    unsigned m_totalNumberOfHandlersInChain { 0 };
    bool m_isDeleted { false };
};

} // namespace JSC

namespace WTF {

template struct HashTraits : SimpleClassHashTraits {
    static constexpr bool emptyValueIsZero = true;
};

} // namespace WTF

namespace JSC {

static LazyNeverDestroyed s_currentChain;
static LazyNeverDestroyed s_chainSpectrum;

bool ICEvent::operatorclassName, other.m_classInfo->className) < 0;
WTF_ALLOW_UNSAFE_BUFFER_USAGE_END
    }

    if (m_kind != other.m_kind)
        return m_kind < other.m_kind;

    return m_propertyLocation < other.m_propertyLocation;
}

void ICEvent::dump(PrintStream& out) const
{
    out.print(m_kind, "(", m_classInfo ? m_classInfo->className : "", ")");
    if (m_propertyLocation != Unknown)
        out.print(m_propertyLocation == BaseObject ? " self" : " proto lookup");
}

void ICEvent::log() const
{
    ICStats::singleton().add(*this);
}

Atomic ICStats::s_instance;

ICStats::ICStats()
{
    ASSERT(Options::useICStats());

    s_currentChain.construct();
    s_chainSpectrum.construct();

    std::atexit([] {
        ICStats& stats = singleton();
        dataLog("ICStats at exit:\n");
        Locker spectrumLocker { stats.m_spectrum.getLock() };
        auto list = stats.m_spectrum.buildList(spectrumLocker);
        uint64_t totalCount = 0;
        for (auto& entry : list)
            totalCount += entry.count;
        for (unsigned i = list.size(); i--;) {
            dataLog("    ", *list[i].key, ": ", list[i].count);
            if (totalCount)
                dataLogF(" (%.1f%%)", 100.0 * list[i].count / totalCount);
            dataLog("\n");
        }
        stats.dumpChains();
    });

    m_thread = Thread::create(
        "JSC ICStats"_s,
        [this] () {
            Locker locker { m_lock };
            for (;;) {
                m_condition.waitFor(
                    m_lock, Seconds(1), [this] () -> bool { return m_shouldStop; });
                if (m_shouldStop)
                    break;
                
                dataLog("ICStats:\n");
                {
                    Locker spectrumLocker { m_spectrum.getLock() };
                    auto list = m_spectrum.buildList(spectrumLocker);
                    uint64_t totalCount = 0;
                    for (auto& entry : list)
                        totalCount += entry.count;
                    for (unsigned i = list.size(); i--;) {
                        dataLog("    ", *list[i].key, ": ", list[i].count);
                        if (totalCount)
                            dataLogF(" (%.1f%%)", 100.0 * list[i].count / totalCount);
                        dataLog("\n");
                    }
                }
                dumpChains();
            }
        });
}

ICStats::~ICStats()
{
    {
        Locker locker { m_lock };
        m_shouldStop = true;
        m_condition.notifyAll();
    }
    
    m_thread->waitForCompletion();
}

static bool NODELETE shouldRecord()
{
    constexpr bool shouldMeasureOutsideSignpost = false;
    if (shouldMeasureOutsideSignpost)
        return true;
    return activeJSGlobalObjectSignpostIntervalCount.load();
}

void ICStats::add(const ICEvent& event)
{
    if (shouldRecord())
        m_spectrum.add(event);
}

ICStats& ICStats::singleton()
{
    static std::once_flag onceFlag;
    std::call_once(onceFlag, [] {
        s_instance.store(new ICStats());
    });
    return *s_instance.load();
}

void ICStats::startNewChain(unsigned newChainLength)
{
    Locker locker { s_chainSpectrum->getLock() };
    if (!s_currentChain->isEmpty() && shouldRecord())
        s_chainSpectrum->add(locker, s_currentChain.get());
    s_currentChain->clear();
    s_currentChain->setTotalNumberOfHandlersInChain(newChainLength);
}

void ICStats::appendToCurrentChain(ICEvent::Kind kind)
{
    if (shouldRecord())
        s_currentChain->append(kind);
}

void ICStats::dumpChains()
{
    startNewChain(0);
    Locker locker { s_chainSpectrum->getLock() };
    auto list = s_chainSpectrum->buildList(locker);
    if (list.isEmpty())
        return;
    dataLog("IC Handler Chains:\n");
    for (unsigned i = list.size(); i--;) {
        auto& chain = *list[i].key;
        dataLog("    ", list[i].count, "x [");
        for (unsigned j = 0; j < chain.size(); j++) {
            if (j)
                dataLog(", ");
            printInternal(WTF::dataFile(), chain[j]);
        }
        dataLog("] (chain length: ", chain.totalNumberOfHandlersInChain(), ")\n");
    }

    auto dumpHistogram = [&](const char* title, auto keyFn) {
        UncheckedKeyHashMap histogram;
        uint64_t total = 0;
        for (unsigned i = 0; i < list.size(); i++) {
            histogram.add(keyFn(i), 0).iterator->value += list[i].count;
            total += list[i].count;
        }
        Vector entries;
        for (auto& [len, count] : histogram)
            entries.append({ len, count });
        std::sort(entries.begin(), entries.end());
        dataLog("\n", title, "\n");
        for (auto& [len, count] : entries) {
            dataLog("    ", len, ": ", count);
            if (total)
                dataLogF(" (%.1f%%)", 100.0 * count / total);
            dataLog("\n");
        }
    };
    dumpHistogram("IC Chain Length Histogram (total handlers in chain): ",
        [&](unsigned i) { return list[i].key->totalNumberOfHandlersInChain(); });
    dumpHistogram("IC Chain Length Histogram (executed handlers): ",
        [&](unsigned i) { return list[i].key->size(); });
}

} // namespace JSC

namespace WTF {

using namespace JSC;

void printInternal(PrintStream& out, ICEvent::Kind kind)
{
    switch (kind) {
#define ICEVENT_KIND_DUMP(name) case ICEvent::name: out.print(#name); return;
        FOR_EACH_ICEVENT_KIND(ICEVENT_KIND_DUMP);
#undef ICEVENT_KIND_DUMP
    }
    RELEASE_ASSERT_NOT_REACHED();
}

} // namespace WTF



Web Proxy Viewer  |  New URL  |  Original Page