diff options
Diffstat (limited to 'src/testlib/qbenchmarkperfevents.cpp')
-rw-r--r-- | src/testlib/qbenchmarkperfevents.cpp | 216 |
1 files changed, 108 insertions, 108 deletions
diff --git a/src/testlib/qbenchmarkperfevents.cpp b/src/testlib/qbenchmarkperfevents.cpp index 9a06fdbff5..c161879a7d 100644 --- a/src/testlib/qbenchmarkperfevents.cpp +++ b/src/testlib/qbenchmarkperfevents.cpp @@ -17,8 +17,9 @@ #include <string.h> #include <stdio.h> -#include <sys/syscall.h> #include <sys/ioctl.h> +#include <sys/prctl.h> +#include <sys/syscall.h> #include "3rdparty/linux_perf_event_p.h" @@ -47,7 +48,13 @@ QT_BEGIN_NAMESPACE +struct PerfEvent +{ + quint32 type; + quint64 config; +}; static perf_event_attr attr; +Q_GLOBAL_STATIC(QList<PerfEvent>, eventTypes); static void initPerf() { @@ -62,14 +69,20 @@ static void initPerf() attr.inherit_stat = true; // aggregate all the info from child processes attr.task = true; // trace fork/exits - // set a default performance counter: CPU cycles - attr.type = PERF_TYPE_HARDWARE; - attr.config = PERF_COUNT_HW_CPU_CYCLES; // default - done = true; } } +static QList<PerfEvent> defaultCounters() +{ + return { + { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, + { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES }, + { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS }, + { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, + }; +} + // This class does not exist in the API so it's qdoc comment marker was removed. /* @@ -95,7 +108,8 @@ static void initPerf() static int perf_event_open(perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags) { #ifdef SYS_perf_event_open - return syscall(SYS_perf_event_open, attr, pid, cpu, group_fd, flags); + // syscall() returns long, but perf_event_open() is used to get a file descriptor + return int(syscall(SYS_perf_event_open, attr, pid, cpu, group_fd, flags)); #else Q_UNUSED(attr); Q_UNUSED(pid); @@ -128,8 +142,8 @@ bool QBenchmarkPerfEventsMeasurer::isAvailable() HARDWARE BUS_CYCLES BusCycles bus-cycles HARDWARE STALLED_CYCLES_FRONTEND StalledCycles stalled-cycles-frontend idle-cycles-frontend HARDWARE STALLED_CYCLES_BACKEND StalledCycles stalled-cycles-backend idle-cycles-backend - SOFTWARE CPU_CLOCK WalltimeMilliseconds cpu-clock - SOFTWARE TASK_CLOCK WalltimeMilliseconds task-clock + SOFTWARE CPU_CLOCK WalltimeNanoseconds cpu-clock + SOFTWARE TASK_CLOCK WalltimeNanoseconds task-clock SOFTWARE PAGE_FAULTS PageFaults page-faults faults SOFTWARE PAGE_FAULTS_MAJ MajorPageFaults major-faults SOFTWARE PAGE_FAULTS_MIN MinorPageFaults minor-faults @@ -195,7 +209,7 @@ for $entry (sort @strings) { $map{$entry}[2], $map{$entry}[3]; } -print " { 0, PERF_TYPE_MAX, 0, QTest::Events }\n};\n"; +print "};\n"; === cut perl === */ @@ -309,7 +323,7 @@ static const Events eventlist[] = { { 170, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES, QTest::CacheMisses }, { 183, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES, QTest::CacheReferences }, { 200, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES, QTest::ContextSwitches }, - { 217, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, QTest::WalltimeMilliseconds }, + { 217, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK, QTest::WalltimeNanoseconds }, { 227, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES, QTest::CPUCycles }, { 238, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS, QTest::CPUMigrations }, { 253, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES, QTest::ContextSwitches }, @@ -378,17 +392,15 @@ static const Events eventlist[] = { { 1292, PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES, QTest::RefCPUCycles }, { 1303, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND, QTest::StalledCycles }, { 1326, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND, QTest::StalledCycles }, - { 1350, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK, QTest::WalltimeMilliseconds }, - { 0, PERF_TYPE_MAX, 0, QTest::Events } + { 1350, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK, QTest::WalltimeNanoseconds }, }; /* -- END GENERATED CODE -- */ -QTest::QBenchmarkMetric QBenchmarkPerfEventsMeasurer::metricForEvent(quint32 type, quint64 event_id) +static QTest::QBenchmarkMetric metricForEvent(PerfEvent counter) { - const Events *ptr = eventlist; - for ( ; ptr->type != PERF_TYPE_MAX; ++ptr) { - if (ptr->type == type && ptr->event_id == event_id) - return ptr->metric; + for (const Events &ev : eventlist) { + if (ev.type == counter.type && ev.event_id == counter.config) + return ev.metric; } return QTest::Events; } @@ -396,47 +408,39 @@ QTest::QBenchmarkMetric QBenchmarkPerfEventsMeasurer::metricForEvent(quint32 typ void QBenchmarkPerfEventsMeasurer::setCounter(const char *name) { initPerf(); - const char *colon = strchr(name, ':'); - int n = colon ? colon - name : strlen(name); - const Events *ptr = eventlist; - for ( ; ptr->type != PERF_TYPE_MAX; ++ptr) { - int c = strncmp(name, eventlist_strings + ptr->offset, n); - if (c == 0) + eventTypes->clear(); + std::string_view input = name; + if (qsizetype idx = input.find(':'); idx >= 0) + input = input.substr(0, idx); + + while (!input.empty()) { + std::string_view countername = input; + if (qsizetype idx = countername.find(','); idx >= 0) + countername = countername.substr(0, idx); + + for (const Events &ev : eventlist) { + int c = countername.compare(eventlist_strings + ev.offset); + if (c > 0) + continue; + if (c < 0) { + fprintf(stderr, "ERROR: Performance counter type '%.*s' is unknown\n", + int(countername.size()), countername.data()); + exit(1); + } + eventTypes->append({ ev.type, ev.event_id }); break; - if (c < 0) { - fprintf(stderr, "ERROR: Performance counter type '%s' is unknown\n", name); - exit(1); } - } - - attr.type = ptr->type; - attr.config = ptr->event_id; - // now parse the attributes - if (!colon) - return; - while (*++colon) { - switch (*colon) { - case 'u': - attr.exclude_user = true; - break; - case 'k': - attr.exclude_kernel = true; - break; - case 'h': - attr.exclude_hv = true; - break; - case 'G': - attr.exclude_guest = true; - break; - case 'H': - attr.exclude_host = true; - break; - default: - fprintf(stderr, "ERROR: Unknown attribute '%c'\n", *colon); - exit(1); - } + if (countername.size() == input.size()) + input = {}; + else + input.remove_prefix(countername.size() + 1); } + + // We used to support attributes, but our code was the opposite of what + // perf(1) does, plus QBenchlib isn't exactly expected to be used to + // profile Linux kernel code or launch guest VMs as part of the workload. + // So we keep accepting the colon as a delimiter but ignore it. } void QBenchmarkPerfEventsMeasurer::listCounters() @@ -447,28 +451,20 @@ void QBenchmarkPerfEventsMeasurer::listCounters() } printf("The following performance counters are available:\n"); - const Events *ptr = eventlist; - for ( ; ptr->type != PERF_TYPE_MAX; ++ptr) { - printf(" %-30s [%s]\n", eventlist_strings + ptr->offset, - ptr->type == PERF_TYPE_HARDWARE ? "hardware" : - ptr->type == PERF_TYPE_SOFTWARE ? "software" : - ptr->type == PERF_TYPE_HW_CACHE ? "cache" : "other"); + for (const Events &ev : eventlist) { + printf(" %-30s [%s]\n", eventlist_strings + ev.offset, + ev.type == PERF_TYPE_HARDWARE ? "hardware" : + ev.type == PERF_TYPE_SOFTWARE ? "software" : + ev.type == PERF_TYPE_HW_CACHE ? "cache" : "other"); } - - printf("\nAttributes can be specified by adding a colon and the following:\n" - " u - exclude measuring in the userspace\n" - " k - exclude measuring in kernel mode\n" - " h - exclude measuring in the hypervisor\n" - " G - exclude measuring when running virtualized (guest VM)\n" - " H - exclude measuring when running non-virtualized (host system)\n" - "Attributes can be combined, for example: -perfcounter branch-mispredicts:kh\n"); } QBenchmarkPerfEventsMeasurer::QBenchmarkPerfEventsMeasurer() = default; QBenchmarkPerfEventsMeasurer::~QBenchmarkPerfEventsMeasurer() { - qt_safe_close(fd); + for (int fd : std::as_const(fds)) + qt_safe_close(fd); } void QBenchmarkPerfEventsMeasurer::init() @@ -477,43 +473,56 @@ void QBenchmarkPerfEventsMeasurer::init() void QBenchmarkPerfEventsMeasurer::start() { - initPerf(); - if (fd == -1) { - // 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); + QList<PerfEvent> &counters = *eventTypes; + if (counters.isEmpty()) + counters = defaultCounters(); + if (fds.isEmpty()) { + pid_t pid = 0; // attach to the current process only + int cpu = -1; // on any CPU + int group_fd = -1; + int flags = PERF_FLAG_FD_CLOEXEC; + + fds.reserve(counters.size()); + for (PerfEvent counter : std::as_const(counters)) { + attr.type = counter.type; + attr.config = counter.config; + int fd = perf_event_open(&attr, pid, cpu, group_fd, flags); + if (fd == -1) { + // probably a paranoid kernel (/proc/sys/kernel/perf_event_paranoid) + attr.exclude_kernel = true; + attr.exclude_hv = true; + fd = perf_event_open(&attr, pid, cpu, group_fd, flags); + } + if (fd == -1) { + perror("QBenchmarkPerfEventsMeasurer::start: perf_event_open"); + exit(1); + } + + fds.append(fd); } } - // enable the counter - ::ioctl(fd, PERF_EVENT_IOC_RESET); - ::ioctl(fd, PERF_EVENT_IOC_ENABLE); + // enable the counters + for (int fd : std::as_const(fds)) + ::ioctl(fd, PERF_EVENT_IOC_RESET); + prctl(PR_TASK_PERF_EVENTS_ENABLE); } -qint64 QBenchmarkPerfEventsMeasurer::checkpoint() +QList<QBenchmarkMeasurerBase::Measurement> QBenchmarkPerfEventsMeasurer::stop() { - ::ioctl(fd, PERF_EVENT_IOC_DISABLE); - qint64 value = readValue(); - ::ioctl(fd, PERF_EVENT_IOC_ENABLE); - return value; -} + // disable the counters + prctl(PR_TASK_PERF_EVENTS_DISABLE); -qint64 QBenchmarkPerfEventsMeasurer::stop() -{ - // disable the counter - ::ioctl(fd, PERF_EVENT_IOC_DISABLE); - return readValue(); + const QList<PerfEvent> &counters = *eventTypes; + QList<Measurement> result(counters.size(), {}); + for (qsizetype i = 0; i < counters.size(); ++i) { + result[i] = readValue(i); + } + return result; } -bool QBenchmarkPerfEventsMeasurer::isMeasurementAccepted(qint64) +bool QBenchmarkPerfEventsMeasurer::isMeasurementAccepted(Measurement) { return true; } @@ -528,11 +537,6 @@ int QBenchmarkPerfEventsMeasurer::adjustMedianCount(int) return 1; } -QTest::QBenchmarkMetric QBenchmarkPerfEventsMeasurer::metricType() -{ - return metricForEvent(attr.type, attr.config); -} - static quint64 rawReadValue(int fd) { /* from the kernel docs: @@ -568,14 +572,10 @@ static quint64 rawReadValue(int fd) return results.value * (double(results.time_running) / double(results.time_enabled)); } -qint64 QBenchmarkPerfEventsMeasurer::readValue() +QBenchmarkMeasurerBase::Measurement QBenchmarkPerfEventsMeasurer::readValue(qsizetype idx) { - quint64 raw = rawReadValue(fd); - if (metricType() == QTest::WalltimeMilliseconds) { - // perf returns nanoseconds - return raw / 1000000; - } - return raw; + quint64 raw = rawReadValue(fds.at(idx)); + return { qreal(qint64(raw)), metricForEvent(eventTypes->at(idx)) }; } QT_END_NAMESPACE |