[libvirt] [PATCH v4 3/8] perf: implement a set of util functions for perf event

Qiaowei Ren qiaowei.ren at intel.com
Mon Mar 28 13:30:28 UTC 2016


This patch implement a set of interfaces for perf event. Based on
these interfaces, we can implement internal driver API for perf,
and get the results of perf conuter you care about.

Signed-off-by: Qiaowei Ren <qiaowei.ren at intel.com>
---
 include/libvirt/virterror.h |   2 +
 src/Makefile.am             |   1 +
 src/libvirt_private.syms    |  12 ++
 src/util/virerror.c         |   2 +
 src/util/virperf.c          | 307 ++++++++++++++++++++++++++++++++++++++++++++
 src/util/virperf.h          |  63 +++++++++
 6 files changed, 387 insertions(+)
 create mode 100644 src/util/virperf.c
 create mode 100644 src/util/virperf.h

diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index 1f0702b..c6abdf7 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -130,6 +130,8 @@ typedef enum {
     VIR_FROM_LOGGING = 63,      /* Error from log manager */
     VIR_FROM_XENXL = 64,        /* Error from Xen xl config code */
 
+    VIR_FROM_PERF = 65,         /* Error from perf */
+
 # ifdef VIR_ENUM_SENTINELS
     VIR_ERR_DOMAIN_LAST
 # endif
diff --git a/src/Makefile.am b/src/Makefile.am
index dad7bab..1726d06 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -98,6 +98,7 @@ UTIL_SOURCES =							\
 		util/virauthconfig.c util/virauthconfig.h	\
 		util/virbitmap.c util/virbitmap.h		\
 		util/virbuffer.c util/virbuffer.h		\
+		util/virperf.c util/virperf.h			\
 		util/vircgroup.c util/vircgroup.h util/vircgrouppriv.h	\
 		util/virclosecallbacks.c util/virclosecallbacks.h		\
 		util/vircommand.c util/vircommand.h util/vircommandpriv.h \
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index af133c5..7c44047 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2023,6 +2023,18 @@ virPCIStubDriverTypeFromString;
 virPCIStubDriverTypeToString;
 
 
+# util/virperf.h
+virPerfEventDisable;
+virPerfEventEnable;
+virPerfEventIsEnabled;
+virPerfEventTypeFromString;
+virPerfEventTypeToString;
+virPerfFree;
+virPerfGetEventFd;
+virPerfNew;
+virPerfReadEvent;
+
+
 # util/virpidfile.h
 virPidFileAcquire;
 virPidFileAcquirePath;
diff --git a/src/util/virerror.c b/src/util/virerror.c
index 61b9ea0..4766e13 100644
--- a/src/util/virerror.c
+++ b/src/util/virerror.c
@@ -136,6 +136,8 @@ VIR_ENUM_IMPL(virErrorDomain, VIR_ERR_DOMAIN_LAST,
               "Admin Interface",
               "Log Manager",
               "Xen XL Config",
+
+              "Perf",
     )
 
 
diff --git a/src/util/virperf.c b/src/util/virperf.c
new file mode 100644
index 0000000..42eee85
--- /dev/null
+++ b/src/util/virperf.c
@@ -0,0 +1,307 @@
+/*
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Ren Qiaowei <qiaowei.ren at intel.com>
+ */
+#include <config.h>
+
+#include <sys/ioctl.h>
+#if defined HAVE_SYS_SYSCALL_H
+# include <sys/syscall.h>
+#endif
+
+#include "virperf.h"
+#include "viralloc.h"
+#include "virerror.h"
+#include "virlog.h"
+#include "virfile.h"
+#include "virstring.h"
+#include "virtypedparam.h"
+
+VIR_LOG_INIT("util.perf");
+
+#define VIR_FROM_THIS VIR_FROM_PERF
+
+VIR_ENUM_IMPL(virPerfEvent, VIR_PERF_EVENT_LAST,
+              "cmt");
+
+struct virPerfEvent {
+    int type;
+    int fd;
+    bool enabled;
+    union {
+        /* cmt */
+        struct {
+            int scale;
+        } cmt;
+    } efields;
+};
+typedef struct virPerfEvent *virPerfEventPtr;
+
+struct virPerf {
+    struct virPerfEvent events[VIR_PERF_EVENT_LAST];
+};
+
+virPerfPtr
+virPerfNew(void)
+{
+    size_t i;
+    virPerfPtr perf;
+
+    if (VIR_ALLOC(perf) < 0)
+        return NULL;
+
+    for (i = 0; i < VIR_PERF_EVENT_LAST; i++) {
+        perf->events[i].type = i;
+        perf->events[i].fd = -1;
+        perf->events[i].enabled = false;
+    }
+
+    return perf;
+}
+
+void
+virPerfFree(virPerfPtr 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);
+    }
+
+    VIR_FREE(perf);
+}
+
+#if defined(__linux__) && defined(HAVE_SYS_SYSCALL_H)
+
+# include <linux/perf_event.h>
+
+static virPerfEventPtr
+virPerfGetEvent(virPerfPtr perf,
+                virPerfEventType type)
+{
+    if (type >= VIR_PERF_EVENT_LAST) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Event '%d' is not supported"),
+                       type);
+        return NULL;
+    }
+
+    return perf->events + type;
+}
+
+static int
+virPerfCmtEnable(virPerfEventPtr event,
+                 pid_t pid)
+{
+    struct perf_event_attr cmt_attr;
+    char *buf = NULL;
+    char *tmp = NULL;
+    unsigned int event_type, scale;
+
+    if (virFileReadAll("/sys/devices/intel_cqm/type",
+                       10, &buf) < 0)
+        goto cleanup;
+
+    if ((tmp = strchr(buf, '\n')))
+        *tmp = '\0';
+
+    if (virStrToLong_ui(buf, NULL, 10, &event_type) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("failed to get cmt event type"));
+        goto cleanup;
+    }
+    VIR_FREE(buf);
+
+    if (virFileReadAll("/sys/devices/intel_cqm/events/llc_occupancy.scale",
+                       10, &buf) < 0)
+        goto cleanup;
+
+    if (virStrToLong_ui(buf, NULL, 10, &scale) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("failed to get cmt scaling factor"));
+        goto cleanup;
+    }
+
+    event->efields.cmt.scale = scale;
+
+    memset(&cmt_attr, 0, sizeof(cmt_attr));
+    cmt_attr.size = sizeof(cmt_attr);
+    cmt_attr.type = event_type;
+    cmt_attr.config = 1;
+    cmt_attr.inherit = 1;
+    cmt_attr.disabled = 1;
+    cmt_attr.enable_on_exec = 0;
+
+    event->fd = syscall(__NR_perf_event_open, &cmt_attr, pid, -1, -1, 0);
+    if (event->fd < 0) {
+        virReportSystemError(errno,
+                             _("Unable to open perf type=%d for pid=%d"),
+                             event_type, pid);
+        goto cleanup;
+    }
+
+    if (ioctl(event->fd, PERF_EVENT_IOC_ENABLE) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Unable to enable perf event for CMT"));
+        goto cleanup;
+    }
+
+    event->enabled = true;
+    return 0;
+
+ cleanup:
+    VIR_FORCE_CLOSE(event->fd);
+    VIR_FREE(buf);
+    return -1;
+}
+
+int
+virPerfEventEnable(virPerfPtr perf,
+                   virPerfEventType type,
+                   pid_t pid)
+{
+    virPerfEventPtr event = virPerfGetEvent(perf, type);
+    if (event == NULL)
+        return -1;
+
+    switch (type) {
+    case VIR_PERF_EVENT_CMT:
+        if (virPerfCmtEnable(event, pid))
+            return -1;
+        break;
+    case VIR_PERF_EVENT_LAST:
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unexpected perf event type=%d"), type);
+        return -1;
+    }
+
+    return 0;
+}
+
+int
+virPerfEventDisable(virPerfPtr perf,
+                    virPerfEventType type)
+{
+    virPerfEventPtr event = virPerfGetEvent(perf, type);
+    if (event == NULL)
+        return -1;
+
+    if (ioctl(event->fd, PERF_EVENT_IOC_DISABLE) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to disable perf event type=%d"),
+                             event->type);
+        return -1;
+    }
+
+    event->enabled = false;
+    VIR_FORCE_CLOSE(event->fd);
+    return 0;
+}
+
+bool virPerfEventIsEnabled(virPerfPtr perf,
+                           virPerfEventType type)
+{
+    virPerfEventPtr event = virPerfGetEvent(perf, type);
+    if (event == NULL)
+        return false;
+
+    return event->enabled;
+}
+
+int virPerfGetEventFd(virPerfPtr perf,
+                      virPerfEventType type)
+{
+    virPerfEventPtr event = virPerfGetEvent(perf, type);
+    if (event == NULL)
+        return false;
+
+    return event->fd;
+}
+
+int
+virPerfReadEvent(virPerfPtr perf,
+                 virPerfEventType type,
+                 uint64_t *value)
+{
+    virPerfEventPtr event = virPerfGetEvent(perf, type);
+    if (event == NULL || !event->enabled)
+        return -1;
+
+    if (read(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
+int
+virPerfEventEnable(virPerfPtr perf ATTRIBUTE_UNUSED,
+                   pid_t pid ATTRIBUTE_UNUSED)
+{
+    virReportSystemError(ENXIO, "%s",
+                         _("Perf not supported on this platform"));
+    return -1;
+}
+
+int
+virPerfEventDisable(virPerfPtr perf ATTRIBUTE_UNUSED,
+                    int event ATTRIBUTE_UNUSED)
+{
+    virReportSystemError(ENXIO, "%s",
+                         _("Perf not supported on this platform"));
+    return -1;
+}
+
+bool
+virPerfEventIsEnabled(virPerfPtr perf,
+                           virPerfEventType type)
+{
+    return false;
+}
+
+int
+virPerfGetEventFd(virPerfPtr perf,
+                      virPerfEventType type)
+{
+    virReportSystemError(ENXIO, "%s",
+                         _("Perf not supported on this platform"));
+    return -1;
+}
+
+int
+virPerfReadEvent(virPerfPtr perf,
+                 virPerfEventType type
+                 uint64_t *value)
+{
+    virReportSystemError(ENXIO, "%s",
+                         _("Perf not supported on this platform"));
+    return -1;
+}
+
+#endif
diff --git a/src/util/virperf.h b/src/util/virperf.h
new file mode 100644
index 0000000..4c36b78
--- /dev/null
+++ b/src/util/virperf.h
@@ -0,0 +1,63 @@
+/*
+ * virperf.h: 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ *  Ren Qiaowei <qiaowei.ren at intel.com>
+ */
+
+#ifndef __VIR_PERF_H__
+# define __VIR_PERF_H__
+
+# include "virutil.h"
+
+typedef enum {
+    VIR_PERF_EVENT_CMT,
+
+    VIR_PERF_EVENT_LAST
+} virPerfEventType;
+
+VIR_ENUM_DECL(virPerfEvent);
+
+# define VIR_PERF_PARAMETERS                                   \
+    VIR_PERF_PARAM_CMT,              VIR_TYPED_PARAM_BOOLEAN,   \
+    NULL
+
+struct virPerf;
+typedef struct virPerf *virPerfPtr;
+
+virPerfPtr virPerfNew(void);
+
+void virPerfFree(virPerfPtr perf);
+
+int virPerfEventEnable(virPerfPtr perf,
+                       virPerfEventType type,
+                       pid_t pid);
+
+int virPerfEventDisable(virPerfPtr perf,
+                        virPerfEventType type);
+
+bool virPerfEventIsEnabled(virPerfPtr perf,
+                           virPerfEventType type);
+
+int virPerfGetEventFd(virPerfPtr perf,
+                      virPerfEventType type);
+
+int virPerfReadEvent(virPerfPtr perf,
+                     virPerfEventType type,
+                     uint64_t *value);
+
+#endif /* __VIR_PERF_H__ */
-- 
1.9.1




More information about the libvir-list mailing list