/* * virperf.c: methods for managing perf events * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include #include #ifndef WIN32 # include #endif #if defined WITH_SYS_SYSCALL_H # include #endif #include "virperf.h" #include "virerror.h" #include "virlog.h" #include "virfile.h" #include "virstring.h" #include "virtypedparam.h" #include "viralloc.h" VIR_LOG_INIT("util.perf"); #define VIR_FROM_THIS VIR_FROM_PERF VIR_ENUM_IMPL(virPerfEvent, VIR_PERF_EVENT_LAST, "cmt", "mbmt", "mbml", "cpu_cycles", "instructions", "cache_references", "cache_misses", "branch_instructions", "branch_misses", "bus_cycles", "stalled_cycles_frontend", "stalled_cycles_backend", "ref_cpu_cycles", "cpu_clock", "task_clock", "page_faults", "context_switches", "cpu_migrations", "page_faults_min", "page_faults_maj", "alignment_faults", "emulation_faults", ); struct virPerfEvent { int fd; bool enabled; union { /* cmt */ struct { int scale; } cmt; } efields; }; struct _virPerf { struct virPerfEvent events[VIR_PERF_EVENT_LAST]; }; #if defined(__linux__) && defined(WITH_SYS_SYSCALL_H) # include struct virPerfEventAttr { unsigned int attrType; unsigned long long attrConfig; }; static struct virPerfEventAttr attrs[] = { [VIR_PERF_EVENT_CMT] = { .attrType = 0, .attrConfig = 1 }, [VIR_PERF_EVENT_MBMT] = { .attrType = 0, .attrConfig = 2 }, [VIR_PERF_EVENT_MBML] = { .attrType = 0, .attrConfig = 3 }, [VIR_PERF_EVENT_CPU_CYCLES] = { .attrType = PERF_TYPE_HARDWARE, .attrConfig = PERF_COUNT_HW_CPU_CYCLES }, [VIR_PERF_EVENT_INSTRUCTIONS] = { .attrType = PERF_TYPE_HARDWARE, .attrConfig = PERF_COUNT_HW_INSTRUCTIONS }, [VIR_PERF_EVENT_CACHE_REFERENCES] = { .attrType = PERF_TYPE_HARDWARE, .attrConfig = PERF_COUNT_HW_CACHE_REFERENCES }, [VIR_PERF_EVENT_CACHE_MISSES] = { .attrType = PERF_TYPE_HARDWARE, .attrConfig = PERF_COUNT_HW_CACHE_MISSES }, [VIR_PERF_EVENT_BRANCH_INSTRUCTIONS] = { .attrType = PERF_TYPE_HARDWARE, .attrConfig = PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, [VIR_PERF_EVENT_BRANCH_MISSES] = { .attrType = PERF_TYPE_HARDWARE, .attrConfig = PERF_COUNT_HW_BRANCH_MISSES }, [VIR_PERF_EVENT_BUS_CYCLES] = { .attrType = PERF_TYPE_HARDWARE, .attrConfig = PERF_COUNT_HW_BUS_CYCLES }, [VIR_PERF_EVENT_STALLED_CYCLES_FRONTEND] = { .attrType = PERF_TYPE_HARDWARE, .attrConfig = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, [VIR_PERF_EVENT_STALLED_CYCLES_BACKEND] = { .attrType = PERF_TYPE_HARDWARE, .attrConfig = PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, [VIR_PERF_EVENT_REF_CPU_CYCLES] = { # ifdef PERF_COUNT_HW_REF_CPU_CYCLES .attrType = PERF_TYPE_HARDWARE, .attrConfig = PERF_COUNT_HW_REF_CPU_CYCLES # else .attrType = 0, .attrConfig = 0, # endif }, [VIR_PERF_EVENT_CPU_CLOCK] = { .attrType = PERF_TYPE_SOFTWARE, .attrConfig = PERF_COUNT_SW_CPU_CLOCK }, [VIR_PERF_EVENT_TASK_CLOCK] = { .attrType = PERF_TYPE_SOFTWARE, .attrConfig = PERF_COUNT_SW_TASK_CLOCK }, [VIR_PERF_EVENT_PAGE_FAULTS] = { .attrType = PERF_TYPE_SOFTWARE, .attrConfig = PERF_COUNT_SW_PAGE_FAULTS }, [VIR_PERF_EVENT_CONTEXT_SWITCHES] = { .attrType = PERF_TYPE_SOFTWARE, .attrConfig = PERF_COUNT_SW_CONTEXT_SWITCHES }, [VIR_PERF_EVENT_CPU_MIGRATIONS] = { .attrType = PERF_TYPE_SOFTWARE, .attrConfig = PERF_COUNT_SW_CPU_MIGRATIONS }, [VIR_PERF_EVENT_PAGE_FAULTS_MIN] = { .attrType = PERF_TYPE_SOFTWARE, .attrConfig = PERF_COUNT_SW_PAGE_FAULTS_MIN }, [VIR_PERF_EVENT_PAGE_FAULTS_MAJ] = { .attrType = PERF_TYPE_SOFTWARE, .attrConfig = PERF_COUNT_SW_PAGE_FAULTS_MAJ }, [VIR_PERF_EVENT_ALIGNMENT_FAULTS] = { .attrType = PERF_TYPE_SOFTWARE, .attrConfig = PERF_COUNT_SW_ALIGNMENT_FAULTS }, [VIR_PERF_EVENT_EMULATION_FAULTS] = { .attrType = PERF_TYPE_SOFTWARE, .attrConfig = PERF_COUNT_SW_EMULATION_FAULTS }, }; G_STATIC_ASSERT(G_N_ELEMENTS(attrs) == VIR_PERF_EVENT_LAST); static int virPerfRdtAttrInit(void) { char *tmp = NULL; unsigned int attr_type = 0; g_autofree char *buf = NULL; if (virFileReadAllQuiet("/sys/devices/intel_cqm/type", 10, &buf) < 0) return -1; if ((tmp = strchr(buf, '\n'))) *tmp = '\0'; if (virStrToLong_ui(buf, NULL, 10, &attr_type) < 0) { virReportSystemError(errno, "%s", _("failed to get rdt event type")); return -1; } attrs[VIR_PERF_EVENT_CMT].attrType = attr_type; attrs[VIR_PERF_EVENT_MBMT].attrType = attr_type; attrs[VIR_PERF_EVENT_MBML].attrType = attr_type; return 0; } int virPerfEventEnable(virPerf *perf, virPerfEventType type, pid_t pid) { struct perf_event_attr attr; struct virPerfEvent *event = &(perf->events[type]); struct virPerfEventAttr *event_attr = &attrs[type]; if (event->enabled) return 0; if (event_attr->attrType == 0 && (type == VIR_PERF_EVENT_CMT || type == VIR_PERF_EVENT_MBMT || type == VIR_PERF_EVENT_MBML)) { virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, _("unable to enable host cpu perf event for %s"), virPerfEventTypeToString(type)); return -1; } if (type == VIR_PERF_EVENT_CMT) { g_autofree char *buf = NULL; if (virFileReadAll("/sys/devices/intel_cqm/events/llc_occupancy.scale", 10, &buf) < 0) goto error; if (virStrToLong_i(buf, NULL, 10, &event->efields.cmt.scale) < 0) { virReportSystemError(errno, "%s", _("failed to get cmt scaling factor")); goto error; } } memset(&attr, 0, sizeof(attr)); attr.size = sizeof(attr); attr.inherit = 1; attr.disabled = 1; attr.enable_on_exec = 0; attr.type = event_attr->attrType; attr.config = event_attr->attrConfig; event->fd = syscall(__NR_perf_event_open, &attr, pid, -1, -1, 0); if (event->fd < 0) { virReportSystemError(errno, _("unable to open host cpu perf event for %s"), virPerfEventTypeToString(type)); goto error; } if (ioctl(event->fd, PERF_EVENT_IOC_ENABLE) < 0) { virReportSystemError(errno, _("unable to enable host cpu perf event for %s"), virPerfEventTypeToString(type)); goto error; } event->enabled = true; return 0; error: VIR_FORCE_CLOSE(event->fd); return -1; } int virPerfEventDisable(virPerf *perf, virPerfEventType type) { struct virPerfEvent *event = &(perf->events[type]); if (!event->enabled) return 0; if (ioctl(event->fd, PERF_EVENT_IOC_DISABLE) < 0) { virReportSystemError(errno, _("unable to disable host cpu perf event for %s"), virPerfEventTypeToString(type)); return -1; } event->enabled = false; VIR_FORCE_CLOSE(event->fd); return 0; } bool virPerfEventIsEnabled(virPerf *perf, virPerfEventType type) { return perf && perf->events[type].enabled; } int virPerfReadEvent(virPerf *perf, virPerfEventType type, uint64_t *value) { struct virPerfEvent *event = &perf->events[type]; if (!event->enabled) return -1; if (saferead(event->fd, value, sizeof(uint64_t)) < 0) { virReportSystemError(errno, "%s", _("Unable to read cache data")); return -1; } if (type == VIR_PERF_EVENT_CMT) *value *= event->efields.cmt.scale; return 0; } #else static int virPerfRdtAttrInit(void) { return 0; } int virPerfEventEnable(virPerf *perf G_GNUC_UNUSED, virPerfEventType type G_GNUC_UNUSED, pid_t pid G_GNUC_UNUSED) { virReportSystemError(ENXIO, "%s", _("Perf not supported on this platform")); return -1; } int virPerfEventDisable(virPerf *perf G_GNUC_UNUSED, virPerfEventType type G_GNUC_UNUSED) { virReportSystemError(ENXIO, "%s", _("Perf not supported on this platform")); return -1; } bool virPerfEventIsEnabled(virPerf *perf G_GNUC_UNUSED, virPerfEventType type G_GNUC_UNUSED) { return false; } int virPerfReadEvent(virPerf *perf G_GNUC_UNUSED, virPerfEventType type G_GNUC_UNUSED, uint64_t *value G_GNUC_UNUSED) { virReportSystemError(ENXIO, "%s", _("Perf not supported on this platform")); return -1; } #endif virPerf * virPerfNew(void) { size_t i; virPerf *perf; perf = g_new0(virPerf, 1); for (i = 0; i < VIR_PERF_EVENT_LAST; i++) { perf->events[i].fd = -1; perf->events[i].enabled = false; } if (virPerfRdtAttrInit() < 0) virResetLastError(); return perf; } void virPerfFree(virPerf *perf) { size_t i; if (perf == NULL) return; for (i = 0; i < VIR_PERF_EVENT_LAST; i++) { if (perf->events[i].enabled) virPerfEventDisable(perf, i); } g_free(perf); }