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

source code of qtdeclarative/src/qmlcompiler/qqmljscompilerstatsreporter.cpp