Revision control

++ // Copyright (C) 2024 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++ // Qt-Security score:significant
++
++ #include "qqmljscompilerstatsreporter_p.h"
++
++ #include <QFileInfo>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QQmlJS {
++
++ using namespace Qt::StringLiterals;
++
++ AotStatsReporter::AotStatsReporter(const AotStats &aotstats, const QStringList &emptyModules,
++ const QStringList &onlyBytecodeModules)
++ : m_aotstats(aotstats), m_emptyModules(emptyModules), m_onlyBytecodeModules(onlyBytecodeModules)
++ {
++ for (const auto &[moduleUri, fileEntries] : aotstats.entries().asKeyValueRange()) {
++ for (const auto &[filepath, statsEntries] : fileEntries.asKeyValueRange()) {
++ for (const auto &entry : statsEntries) {
++ m_fileCounters[moduleUri][filepath].codegens += 1;
++ if (entry.codegenResult == CodegenResult::Success) {
++ m_fileCounters[moduleUri][filepath].successes += 1;
++ m_successDurations.append(entry.codegenDuration);
++ } else if (entry.codegenResult == CodegenResult::Skip) {
++ m_fileCounters[moduleUri][filepath].skips += 1;
++ }
++ }
++ m_moduleCounters[moduleUri].codegens += m_fileCounters[moduleUri][filepath].codegens;
++ m_moduleCounters[moduleUri].successes += m_fileCounters[moduleUri][filepath].successes;
++ m_moduleCounters[moduleUri].skips += m_fileCounters[moduleUri][filepath].skips;
++ }
++ m_totalCounters.codegens += m_moduleCounters[moduleUri].codegens;
++ m_totalCounters.successes += m_moduleCounters[moduleUri].successes;
++ m_totalCounters.skips += m_moduleCounters[moduleUri].skips;
++ }
++ }
++
++ void AotStatsReporter::formatDetailedStats(QTextStream &s) const
++ {
++ s << "############ AOT COMPILATION STATS ############\n";
++ QStringList sortedModuleKeys = m_aotstats.entries().keys();
++ sortedModuleKeys.sort();
++ for (const auto &moduleUri : std::as_const(sortedModuleKeys)) {
++ const auto &fileStats = m_aotstats.entries()[moduleUri];
++ s << u"Module %1:\n"_s.arg(moduleUri);
++ if (fileStats.empty()) {
++ s << "No attempts at compiling a binding or function\n";
++ continue;
++ }
++
++ QStringList sortedFileKeys = fileStats.keys();
++ sortedFileKeys.sort();
++ for (const auto &filename : std::as_const(sortedFileKeys)) {
++ const auto &entries = fileStats[filename];
++ s << u"--File %1\n"_s.arg(filename);
++ if (entries.empty()) {
++ s << " No attempts at compiling a binding or function\n";
++ continue;
++ }
++
++ int successes = m_fileCounters[moduleUri][filename].successes;
++ int skips = m_fileCounters[moduleUri][filename].skips;
++ s << " " << formatSuccessRate(entries.size(), successes, skips) << "\n";
++
++ for (const auto &stat : std::as_const(entries)) {
++ s << u" %1: [%2:%3:%4]\n"_s.arg(stat.functionName)
++ .arg(QFileInfo(filename).fileName())
++ .arg(stat.line)
++ .arg(stat.column);
++ s << u" result: "_s;
++ switch (stat.codegenResult) {
++ case QQmlJS::CodegenResult::Success: s << u"Success\n"_s;
++ break;
++ case QQmlJS::CodegenResult::Skip: s << u"Skip: %1\n"_s.arg(stat.message);
++ break;
++ case QQmlJS::CodegenResult::Failure: s << u"Error: %1\n"_s.arg(stat.message);
++ break;
++ }
++ s << u" duration: %1us\n"_s.arg(stat.codegenDuration.count());
++ }
++ s << "\n";
++ }
++ }
++ }
++
++ void AotStatsReporter::formatSummary(QTextStream &s) const
++ {
++ s << "############ AOT COMPILATION STATS SUMMARY ############\n";
++ if (m_totalCounters.codegens == 0 && m_emptyModules.empty() && m_onlyBytecodeModules.empty()) {
++ s << "No attempted compilations to Cpp for bindings or functions.\n";
++ return;
++ }
++
++ QStringList sortedKeys = m_aotstats.entries().keys();
++ sortedKeys.sort();
++ for (const auto &moduleUri : std::as_const(sortedKeys)) {
++ const auto &counters = m_moduleCounters[moduleUri];
++ s << u"Module %1: "_s.arg(moduleUri)
++ << formatSuccessRate(counters.codegens, counters.successes, counters.skips) << "\n";
++ }
++
++ for (const auto &module : std::as_const(m_emptyModules))
++ s << u"Module %1: No .qml files to compile.\n"_s.arg(module);
++
++ for (const auto &module : std::as_const(m_onlyBytecodeModules))
++ s << u"Module %1: No .qml files compiled (--only-bytecode).\n"_s.arg(module);
++
++ s << "Total results: " << formatSuccessRate(m_totalCounters.codegens,
++ m_totalCounters.successes,
++ m_totalCounters.skips);
++ s << "\n";
++
++ if (m_totalCounters.successes != 0) {
++ auto totalDuration = std::accumulate(m_successDurations.cbegin(), m_successDurations.cend(),
++ std::chrono::microseconds(0));
++ const auto averageDuration = totalDuration.count() / m_totalCounters.successes;
++ s << u"Successful codegens took an average of %1us\n"_s.arg(averageDuration);
++ }
++ }
++
++ QString AotStatsReporter::format() const
++ {
++ QString output;
++ QTextStream s(&output);
++
++ formatDetailedStats(s);
++ formatSummary(s);
++
++ return output;
++ }
++
++ QString AotStatsReporter::formatSuccessRate(int codegens, int successes, int skips) const
++ {
++ if (codegens == 0)
++ return u"No attempted compilations"_s;
++
++ return u"%1 of %2 (%3%4) %5bindings or functions compiled to Cpp successfully"_s
++ .arg(successes)
++ .arg(codegens)
++ .arg(double(successes) / codegens * 100, 0, 'g', 4)
++ .arg(u"%"_s)
++ .arg(skips ? u"(%1 skipped) "_s.arg(skips) : u""_s);
++ }
++
++ } // namespace QQmlJS
++
++ QT_END_NAMESPACE