Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qbenchmarkvalgrind.cpp
Go to the documentation of this file.
1
// Copyright (C) 2016 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
#
include
<
QtTest
/
private
/
qbenchmark_p
.
h
>
5
6
#
include
<
QtTest
/
private
/
qbenchmarkvalgrind_p
.
h
>
7
#
include
<
QtCore
/
qstringlist
.
h
>
8
#
include
<
QtCore
/
qcoreapplication
.
h
>
9
#
include
<
QtCore
/
qprocess
.
h
>
10
#
include
<
QtCore
/
qdir
.
h
>
11
#
include
<
QtCore
/
qregularexpression
.
h
>
12
#
include
<
QtCore
/
qset
.
h
>
13
#
include
"3rdparty/valgrind/callgrind_p.h"
14
15
#
include
<
charconv
>
16
#
include
<
optional
>
17
18
QT_BEGIN_NAMESPACE
19
20
using
namespace
Qt::StringLiterals;
21
22
// Returns \c true if valgrind is available.
23
bool
QBenchmarkValgrindUtils::haveValgrind()
24
{
25
#
ifdef
NVALGRIND
26
return
false
;
27
#
else
28
QProcess process;
29
process.start(u"valgrind"_s, QStringList(u"--version"_s));
30
return
process.waitForStarted() && process.waitForFinished(-1);
31
#
endif
32
}
33
34
// Reruns this program through callgrind.
35
// Returns \c true upon success, otherwise false.
36
bool
QBenchmarkValgrindUtils::rerunThroughCallgrind(
const
QStringList &origAppArgs,
int
&exitCode)
37
{
38
if
(!QBenchmarkValgrindUtils::runCallgrindSubProcess(origAppArgs, exitCode)) {
39
qWarning(
"failed to run callgrind subprocess"
);
40
return
false
;
41
}
42
return
true
;
43
}
44
45
static
void
dumpOutput
(
const
QByteArray &data, FILE *fh)
46
{
47
QFile file;
48
if
(!file.open(fh, QIODevice::WriteOnly)) {
49
qFatal(
"Could not open filehandle for dumping output: %s"
,
50
qPrintable(file.errorString()));
51
}
52
file.write(data);
53
}
54
55
qint64 QBenchmarkValgrindUtils::extractResult(
const
QString &fileName)
56
{
57
QFile file(fileName);
58
const
bool
openOk = file.open(QIODevice::ReadOnly | QIODevice::Text);
59
Q_ASSERT(openOk);
60
Q_UNUSED(openOk);
61
62
std::optional<qint64> val = std::nullopt;
63
QByteArray line;
64
while
(file.readLineInto(&line)) {
65
constexpr
QByteArrayView tag =
"summary: "
;
66
if
(line.startsWith(tag)) {
67
const
auto
maybeNumber = line.data() + tag.size();
68
const
auto
end = line.data() + line.size();
69
qint64 v;
70
const
auto
r = std::from_chars(maybeNumber, end, v);
71
if
(r.ec == std::errc{}) {
72
val = v;
73
break
;
74
}
75
}
76
}
77
if
(Q_UNLIKELY(!val))
78
qFatal(
"Failed to extract result"
);
79
return
*val;
80
}
81
82
// Gets the newest file name (i.e. the one with the highest integer suffix).
83
QString QBenchmarkValgrindUtils::getNewestFileName()
84
{
85
QStringList nameFilters;
86
QString base = QBenchmarkGlobalData::current->callgrindOutFileBase;
87
Q_ASSERT(!base.isEmpty());
88
89
nameFilters << QString::fromLatin1(
"%1.*"
).arg(base);
90
const
QFileInfoList fiList = QDir().entryInfoList(nameFilters, QDir::Files | QDir::Readable);
91
Q_ASSERT(!fiList.empty());
92
int
hiSuffix = -1;
93
QFileInfo lastFileInfo;
94
const
QString pattern = QString::fromLatin1(
"%1.(\\d+)"
).arg(base);
95
QRegularExpression rx(pattern);
96
for
(
const
QFileInfo &fileInfo : fiList) {
97
QRegularExpressionMatch match = rx.match(fileInfo.fileName());
98
Q_ASSERT(match.hasMatch());
99
bool
ok;
100
const
int
suffix = match.captured(1).toInt(&ok);
101
Q_ASSERT(ok);
102
Q_ASSERT(suffix >= 0);
103
if
(suffix > hiSuffix) {
104
lastFileInfo = fileInfo;
105
hiSuffix = suffix;
106
}
107
}
108
109
return
lastFileInfo.fileName();
110
}
111
112
qint64 QBenchmarkValgrindUtils::extractLastResult()
113
{
114
return
extractResult(getNewestFileName());
115
}
116
117
void
QBenchmarkValgrindUtils::cleanup()
118
{
119
QStringList nameFilters;
120
QString base = QBenchmarkGlobalData::current->callgrindOutFileBase;
121
Q_ASSERT(!base.isEmpty());
122
nameFilters
123
<< base
// overall summary
124
<< QString::fromLatin1(
"%1.*"
).arg(base);
// individual dumps
125
const
QFileInfoList fiList = QDir().entryInfoList(nameFilters, QDir::Files | QDir::Readable);
126
for
(
const
QFileInfo &fileInfo : fiList) {
127
const
bool
removeOk = QFile::remove(fileInfo.fileName());
128
Q_ASSERT(removeOk);
129
Q_UNUSED(removeOk);
130
}
131
}
132
133
QString QBenchmarkValgrindUtils::outFileBase(qint64 pid)
134
{
135
return
QString::fromLatin1(
"callgrind.out.%1"
).arg(
136
pid != -1 ? pid : QCoreApplication::applicationPid());
137
}
138
139
// Reruns this program through callgrind, storing callgrind result files in the
140
// current directory.
141
// Returns \c true upon success, otherwise false.
142
bool
QBenchmarkValgrindUtils::runCallgrindSubProcess(
const
QStringList &origAppArgs,
int
&exitCode)
143
{
144
const
QString &execFile = origAppArgs.at(0);
145
QStringList args{ u"--tool=callgrind"_s, u"--instr-atstart=yes"_s,
146
u"--quiet"_s, execFile, u"-callgrindchild"_s };
147
148
// pass on original arguments that make sense (e.g. avoid wasting time producing output
149
// that will be ignored anyway) ...
150
for
(
int
i = 1; i < origAppArgs.size(); ++i) {
151
const
QString &arg = origAppArgs.at(i);
152
if
(arg ==
"-callgrind"_L1
)
153
continue
;
154
args << arg;
// ok to pass on
155
}
156
157
QProcess process;
158
process.start(u"valgrind"_s, args);
159
process.waitForStarted(-1);
160
QBenchmarkGlobalData::current->callgrindOutFileBase =
161
QBenchmarkValgrindUtils::outFileBase(process.processId());
162
const
bool
finishedOk = process.waitForFinished(-1);
163
exitCode = process.exitCode();
164
165
dumpOutput(process.readAllStandardOutput(), stdout);
166
dumpOutput(process.readAllStandardError(), stderr);
167
168
return
finishedOk;
169
}
170
171
void
QBenchmarkCallgrindMeasurer::start()
172
{
173
CALLGRIND_ZERO_STATS;
174
}
175
176
void
QBenchmarkCallgrindMeasurer::updateMeasurement()
177
{
178
CALLGRIND_DUMP_STATS;
179
const
qint64 result = QBenchmarkValgrindUtils::extractLastResult();
180
accumulator.update(qreal(result));
181
CALLGRIND_ZERO_STATS;
// Zero cost centers again to ignore operations in previous lines
182
}
183
184
QList<QBenchmarkMeasurerBase::Measurement> QBenchmarkCallgrindMeasurer::stop()
185
{
186
return
{ { accumulator.total(),
187
accumulator.variance() / QBenchmarkTestMethodData::current->measurementBlockSize,
188
QTest::InstructionReads } };
189
}
190
191
bool
QBenchmarkCallgrindMeasurer::isMeasurementAccepted(Measurement measurement)
192
{
193
Q_UNUSED(measurement);
194
return
true
;
195
}
196
197
int
QBenchmarkCallgrindMeasurer::adjustIterationCount(
int
)
198
{
199
return
1;
200
}
201
202
int
QBenchmarkCallgrindMeasurer::adjustMedianCount(
int
)
203
{
204
return
1;
205
}
206
207
bool
QBenchmarkCallgrindMeasurer::needsWarmupIteration()
208
{
209
return
true
;
210
}
211
212
QT_END_NAMESPACE
dumpOutput
static void dumpOutput(const QByteArray &data, FILE *fh)
Definition
qbenchmarkvalgrind.cpp:45
qtbase
src
testlib
qbenchmarkvalgrind.cpp
Generated on
for Qt by
1.18.0