| 1 | // Copyright (C) 2024 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
| 3 | |
| 4 | #include "qqmljscompilerstatsreporter_p.h" |
| 5 | |
| 6 | #include <QFileInfo> |
| 7 | |
| 8 | QT_BEGIN_NAMESPACE |
| 9 | |
| 10 | namespace QQmlJS { |
| 11 | |
| 12 | using namespace Qt::StringLiterals; |
| 13 | |
| 14 | AotStatsReporter::AotStatsReporter(const AotStats &aotstats, const QStringList &emptyModules, |
| 15 | const QStringList &onlyBytecodeModules) |
| 16 | : m_aotstats(aotstats), m_emptyModules(emptyModules), m_onlyBytecodeModules(onlyBytecodeModules) |
| 17 | { |
| 18 | for (const auto &[moduleUri, fileEntries] : aotstats.entries().asKeyValueRange()) { |
| 19 | for (const auto &[filepath, statsEntries] : fileEntries.asKeyValueRange()) { |
| 20 | for (const auto &entry : statsEntries) { |
| 21 | m_fileCounters[moduleUri][filepath].codegens += 1; |
| 22 | if (entry.codegenSuccessful) { |
| 23 | m_fileCounters[moduleUri][filepath].successes += 1; |
| 24 | m_successDurations.append(t: entry.codegenDuration); |
| 25 | } |
| 26 | } |
| 27 | m_moduleCounters[moduleUri].codegens += m_fileCounters[moduleUri][filepath].codegens; |
| 28 | m_moduleCounters[moduleUri].successes += m_fileCounters[moduleUri][filepath].successes; |
| 29 | } |
| 30 | m_totalCounters.codegens += m_moduleCounters[moduleUri].codegens; |
| 31 | m_totalCounters.successes += m_moduleCounters[moduleUri].successes; |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | void AotStatsReporter::formatDetailedStats(QTextStream &s) const |
| 36 | { |
| 37 | s << "############ AOT COMPILATION STATS ############\n" ; |
| 38 | QStringList sortedModuleKeys = m_aotstats.entries().keys(); |
| 39 | sortedModuleKeys.sort(); |
| 40 | for (const auto &moduleUri : std::as_const(t&: sortedModuleKeys)) { |
| 41 | const auto &fileStats = m_aotstats.entries()[moduleUri]; |
| 42 | s << u"Module %1:\n"_s .arg(a: moduleUri); |
| 43 | if (fileStats.empty()) { |
| 44 | s << "No attempts at compiling a binding or function\n" ; |
| 45 | continue; |
| 46 | } |
| 47 | |
| 48 | QStringList sortedFileKeys = fileStats.keys(); |
| 49 | sortedFileKeys.sort(); |
| 50 | for (const auto &filename : std::as_const(t&: sortedFileKeys)) { |
| 51 | const auto &entries = fileStats[filename]; |
| 52 | s << u"--File %1\n"_s .arg(a: filename); |
| 53 | if (entries.empty()) { |
| 54 | s << " No attempts at compiling a binding or function\n" ; |
| 55 | continue; |
| 56 | } |
| 57 | |
| 58 | int successes = m_fileCounters[moduleUri][filename].successes; |
| 59 | s << " " << formatSuccessRate(codegens: entries.size(), successes) << "\n" ; |
| 60 | |
| 61 | for (const auto &stat : std::as_const(t: entries)) { |
| 62 | s << u" %1: [%2:%3:%4]\n"_s .arg(a: stat.functionName) |
| 63 | .arg(a: QFileInfo(filename).fileName()) |
| 64 | .arg(a: stat.line) |
| 65 | .arg(a: stat.column); |
| 66 | s << u" result: "_s << (stat.codegenSuccessful |
| 67 | ? u"Success\n"_s |
| 68 | : u"Error: "_s + stat.errorMessage + u'\n'); |
| 69 | s << u" duration: %1us\n"_s .arg(a: stat.codegenDuration.count()); |
| 70 | } |
| 71 | s << "\n" ; |
| 72 | } |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | void AotStatsReporter::formatSummary(QTextStream &s) const |
| 77 | { |
| 78 | s << "############ AOT COMPILATION STATS SUMMARY ############\n" ; |
| 79 | if (m_totalCounters.codegens == 0 && m_emptyModules.empty() && m_onlyBytecodeModules.empty()) { |
| 80 | s << "No attempted compilations to Cpp for bindings or functions.\n" ; |
| 81 | return; |
| 82 | } |
| 83 | |
| 84 | QStringList sortedKeys = m_aotstats.entries().keys(); |
| 85 | sortedKeys.sort(); |
| 86 | for (const auto &moduleUri : std::as_const(t&: sortedKeys)) { |
| 87 | const auto &counters = m_moduleCounters[moduleUri]; |
| 88 | s << u"Module %1: "_s .arg(a: moduleUri) |
| 89 | << formatSuccessRate(codegens: counters.codegens, successes: counters.successes) << "\n" ; |
| 90 | } |
| 91 | |
| 92 | for (const auto &module : std::as_const(t: m_emptyModules)) |
| 93 | s << u"Module %1: No .qml files to compile.\n"_s .arg(a: module); |
| 94 | |
| 95 | for (const auto &module : std::as_const(t: m_onlyBytecodeModules)) |
| 96 | s << u"Module %1: No .qml files compiled (--only-bytecode).\n"_s .arg(a: module); |
| 97 | |
| 98 | s << "Total results: " << formatSuccessRate(codegens: m_totalCounters.codegens, successes: m_totalCounters.successes); |
| 99 | s << "\n" ; |
| 100 | |
| 101 | if (m_totalCounters.successes != 0) { |
| 102 | auto totalDuration = std::accumulate(first: m_successDurations.cbegin(), last: m_successDurations.cend(), |
| 103 | init: std::chrono::microseconds(0)); |
| 104 | const auto averageDuration = totalDuration.count() / m_totalCounters.successes; |
| 105 | s << u"Successful codegens took an average of %1us\n"_s .arg(a: averageDuration); |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | QString AotStatsReporter::format() const |
| 110 | { |
| 111 | QString output; |
| 112 | QTextStream s(&output); |
| 113 | |
| 114 | formatDetailedStats(s); |
| 115 | formatSummary(s); |
| 116 | |
| 117 | return output; |
| 118 | } |
| 119 | |
| 120 | QString AotStatsReporter::formatSuccessRate(int codegens, int successes) const |
| 121 | { |
| 122 | if (codegens == 0) |
| 123 | return u"No attempted compilations"_s ; |
| 124 | |
| 125 | return u"%1 of %2 (%3%4) bindings or functions compiled to Cpp successfully"_s |
| 126 | .arg(a: successes) |
| 127 | .arg(a: codegens) |
| 128 | .arg(a: double(successes) / codegens * 100, fieldWidth: 0, format: 'g', precision: 4) |
| 129 | .arg(a: u"%"_s ); |
| 130 | } |
| 131 | |
| 132 | } // namespace QQmlJS |
| 133 | |
| 134 | QT_END_NAMESPACE |
| 135 | |