summaryrefslogtreecommitdiffstats
path: root/src/testlib
diff options
context:
space:
mode:
Diffstat (limited to 'src/testlib')
-rw-r--r--src/testlib/qbenchmarkperfevents.cpp105
-rw-r--r--src/testlib/qbenchmarkperfevents_p.h3
-rw-r--r--src/testlib/qtestcase.cpp3
3 files changed, 109 insertions, 2 deletions
diff --git a/src/testlib/qbenchmarkperfevents.cpp b/src/testlib/qbenchmarkperfevents.cpp
index 8c2a4852b4..178d4cc7e8 100644
--- a/src/testlib/qbenchmarkperfevents.cpp
+++ b/src/testlib/qbenchmarkperfevents.cpp
@@ -44,15 +44,41 @@
#ifdef QTESTLIB_USE_PERF_EVENTS
+// include the qcore_unix_p.h without core-private
+// we only use inline functions anyway
+#include "../corelib/kernel/qcore_unix_p.h"
+
#include <sys/types.h>
#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
#include <sys/syscall.h>
+#include <sys/ioctl.h>
#include "3rdparty/linux_perf_event_p.h"
QT_BEGIN_NAMESPACE
+/*!
+ \class QBenchmarkPerfEvents
+ \brief The Linux perf events benchmark backend
+
+ This benchmark backend uses the Linux Performance Counters interface,
+ introduced with the Linux kernel v2.6.31. The interface is done by one
+ system call (perf_event_open) which takes an attribute structure and
+ returns a file descriptor.
+
+ More information:
+ \li design docs: tools/perf/design.txt <http://lxr.linux.no/linux/tools/perf/design.txt>
+ \li sample tool: tools/perf/builtin-stat.c <http://lxr.linux.no/linux/tools/perf/builtin-stat.c>
+ (note: as of v3.3.1, the documentation is out-of-date with the kernel
+ interface, so reading the source code of existing tools is necessary)
+
+ This benchlib backend monitors the current process as well as child process
+ launched. We do not try to benchmark in kernel or hypervisor mode, as that
+ usually requires elevated privileges.
+ */
static int perf_event_open(perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags)
{
@@ -67,27 +93,69 @@ bool QBenchmarkPerfEventsMeasurer::isAvailable()
}
QBenchmarkPerfEventsMeasurer::QBenchmarkPerfEventsMeasurer()
+ : fd(-1)
{
}
QBenchmarkPerfEventsMeasurer::~QBenchmarkPerfEventsMeasurer()
{
+ qt_safe_close(fd);
}
void QBenchmarkPerfEventsMeasurer::init()
{
+ perf_event_attr attr;
+ memset(&attr, 0, sizeof attr);
+
+ // common init
+ attr.size = sizeof attr;
+ attr.sample_period = 0;
+ attr.sample_type = 0;
+ attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING;
+ attr.disabled = true; // start disabled, we'll enable later
+ attr.inherit = true; // let children inherit, if the benchmark has child processes
+ attr.pinned = true; // keep it running on the PMU
+ attr.inherit_stat = true; // collapse all the info from child processes
+ attr.task = true; // trace fork and exit
+
+ // our event type
+ // ### FIXME hardcoded for now
+ attr.type = PERF_TYPE_HARDWARE;
+ attr.config = PERF_COUNT_HW_CPU_CYCLES;
+
+ // pid == 0 -> attach to the current process
+ // cpu == -1 -> monitor on all CPUs
+ // group_fd == -1 -> this is the group leader
+ // flags == 0 -> reserved, must be zero
+ fd = perf_event_open(&attr, 0, -1, -1, 0);
+ if (fd == -1) {
+ perror("QBenchmarkPerfEventsMeasurer::start: perf_event_open");
+ exit(1);
+ } else {
+ ::fcntl(fd, F_SETFD, FD_CLOEXEC);
+ }
}
void QBenchmarkPerfEventsMeasurer::start()
{
+ // enable the counter
+ ::ioctl(fd, PERF_EVENT_IOC_RESET);
+ ::ioctl(fd, PERF_EVENT_IOC_ENABLE);
}
qint64 QBenchmarkPerfEventsMeasurer::checkpoint()
{
+ ::ioctl(fd, PERF_EVENT_IOC_DISABLE);
+ qint64 value = readValue();
+ ::ioctl(fd, PERF_EVENT_IOC_ENABLE);
+ return value;
}
qint64 QBenchmarkPerfEventsMeasurer::stop()
{
+ // disable the counter
+ ::ioctl(fd, PERF_EVENT_IOC_DISABLE);
+ return readValue();
}
bool QBenchmarkPerfEventsMeasurer::isMeasurementAccepted(qint64)
@@ -110,6 +178,41 @@ QTest::QBenchmarkMetric QBenchmarkPerfEventsMeasurer::metricType()
return QTest::Events;
}
-#endif
+qint64 QBenchmarkPerfEventsMeasurer::readValue()
+{
+ /* from the kernel docs:
+ * struct read_format {
+ * { u64 value;
+ * { u64 time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
+ * { u64 time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
+ * { u64 id; } && PERF_FORMAT_ID
+ * } && !PERF_FORMAT_GROUP
+ */
+
+ struct read_format {
+ quint64 value;
+ quint64 time_enabled;
+ quint64 time_running;
+ } results;
+
+ size_t nread = 0;
+ while (nread < sizeof results) {
+ char *ptr = reinterpret_cast<char *>(&results);
+ qint64 r = qt_safe_read(fd, ptr + nread, sizeof results - nread);
+ if (r == -1) {
+ perror("QBenchmarkPerfEventsMeasurer::readValue: reading the results");
+ exit(1);
+ }
+ nread += quint64(r);
+ }
+
+ if (results.time_running == results.time_enabled)
+ return results.value;
+
+ // scale the results, though this shouldn't happen!
+ return results.value * (double(results.time_running) / double(results.time_enabled));
+}
QT_END_NAMESPACE
+
+#endif
diff --git a/src/testlib/qbenchmarkperfevents_p.h b/src/testlib/qbenchmarkperfevents_p.h
index 19e68aebc3..74966e1699 100644
--- a/src/testlib/qbenchmarkperfevents_p.h
+++ b/src/testlib/qbenchmarkperfevents_p.h
@@ -75,6 +75,9 @@ public:
static bool isAvailable();
private:
+ int fd;
+
+ qint64 readValue();
};
QT_END_NAMESPACE
diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp
index 20d06f6be2..87d32da26a 100644
--- a/src/testlib/qtestcase.cpp
+++ b/src/testlib/qtestcase.cpp
@@ -1487,7 +1487,8 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml)
#ifdef QTESTLIB_USE_PERF_EVENTS
} else if (strcmp(argv[i], "-perf") == 0) {
if (QBenchmarkPerfEventsMeasurer::isAvailable()) {
- printf("perf available\n");
+ // perf available
+ QBenchmarkGlobalData::current->setMode(QBenchmarkGlobalData::PerfCounter);
} else {
fprintf(stderr, "WARNING: Linux perf events not available. Using the walltime measurer.\n");
}