[sysprof] sysprofd: implement mechanics for perf_event_open



commit bc5c243407a4fbc7a85cfddd4714eaa621c1395d
Author: Christian Hergert <chergert redhat com>
Date:   Wed May 8 21:12:34 2019 -0700

    sysprofd: implement mechanics for perf_event_open

 src/sysprofd/ipc-service-impl.c | 226 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 226 insertions(+)
---
diff --git a/src/sysprofd/ipc-service-impl.c b/src/sysprofd/ipc-service-impl.c
index 1733bd1..c215134 100644
--- a/src/sysprofd/ipc-service-impl.c
+++ b/src/sysprofd/ipc-service-impl.c
@@ -22,7 +22,16 @@
 
 #include "config.h"
 
+#include <errno.h>
+#include <gio/gunixfdlist.h>
+#ifdef __linux__
+# include <linux/capability.h>
+# include <linux/perf_event.h>
+#endif
 #include <polkit/polkit.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
 
 #include "ipc-service-impl.h"
 
@@ -110,6 +119,220 @@ ipc_service_impl_handle_get_proc_file (IpcService            *service,
   return TRUE;
 }
 
+#ifdef __linux__
+static int
+_perf_event_open (struct perf_event_attr *attr,
+                  pid_t                   pid,
+                  int                     cpu,
+                  int                     group_fd,
+                  unsigned long           flags)
+{
+  g_assert (attr != NULL);
+
+  /* Quick sanity check */
+  if (attr->sample_period < 100000 && attr->type != PERF_TYPE_TRACEPOINT)
+    return -EINVAL;
+
+  return syscall (__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
+}
+
+static gboolean
+ipc_service_impl_handle_perf_event_open (IpcService            *service,
+                                         GDBusMethodInvocation *invocation,
+                                         GVariant              *options,
+                                         gint                   pid,
+                                         gint                   cpu,
+                                         guint64                flags)
+{
+  g_autoptr(GUnixFDList) fd_list = NULL;
+  struct perf_event_attr attr = {0};
+  GVariantIter iter;
+  GVariant *value;
+  gchar *key;
+  gint32 disabled = 0;
+  gint32 wakeup_events = 149;
+  gint32 type = 0;
+  guint64 sample_period = 0;
+  guint64 sample_type = 0;
+  guint64 config = 0;
+  gint clockid = CLOCK_MONOTONIC;
+  gint comm = 0;
+  gint mmap_ = 0;
+  gint task = 0;
+  gint exclude_idle = 0;
+  gint fd = -1;
+  gint handle;
+  gint use_clockid = 0;
+  gint sample_id_all = 0;
+
+  g_assert (IPC_IS_SERVICE_IMPL (service));
+  g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
+
+  if (pid < -1 || cpu < -1)
+    {
+      g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
+                                             G_DBUS_ERROR,
+                                             G_DBUS_ERROR_INVALID_ARGS,
+                                             "pid and cpu must be >= -1");
+      return TRUE;
+    }
+
+  g_variant_iter_init (&iter, options);
+
+  while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
+    {
+      if (FALSE) {}
+      else if (strcmp (key, "disabled") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
+            goto bad_arg;
+          disabled = g_variant_get_boolean (value);
+        }
+      else if (strcmp (key, "wakeup_events") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_UINT32))
+            goto bad_arg;
+          wakeup_events = g_variant_get_uint32 (value);
+        }
+      else if (strcmp (key, "sample_id_all") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
+            goto bad_arg;
+          sample_id_all = g_variant_get_boolean (value);
+        }
+      else if (strcmp (key, "clockid") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_INT32))
+            goto bad_arg;
+          clockid = g_variant_get_int32 (value);
+        }
+      else if (strcmp (key, "comm") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
+            goto bad_arg;
+          comm = g_variant_get_boolean (value);
+        }
+      else if (strcmp (key, "exclude_idle") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
+            goto bad_arg;
+          exclude_idle = g_variant_get_boolean (value);
+        }
+      else if (strcmp (key, "mmap") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
+            goto bad_arg;
+          mmap_ = g_variant_get_boolean (value);
+        }
+      else if (strcmp (key, "config") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_UINT64))
+            goto bad_arg;
+          config = g_variant_get_uint64 (value);
+        }
+      else if (strcmp (key, "sample_period") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_UINT64))
+            goto bad_arg;
+          sample_period = g_variant_get_uint64 (value);
+        }
+      else if (strcmp (key, "sample_type") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_UINT64))
+            goto bad_arg;
+          sample_type = g_variant_get_uint64 (value);
+        }
+      else if (strcmp (key, "task") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
+            goto bad_arg;
+          task = g_variant_get_boolean (value);
+        }
+      else if (strcmp (key, "type") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_UINT32))
+            goto bad_arg;
+          type = g_variant_get_uint32 (value);
+        }
+      else if (strcmp (key, "use_clockid") == 0)
+        {
+          if (!g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
+            goto bad_arg;
+          use_clockid = g_variant_get_boolean (value);
+        }
+
+      continue;
+
+    bad_arg:
+      g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
+                                             G_DBUS_ERROR,
+                                             G_DBUS_ERROR_INVALID_ARGS,
+                                             "Invalid type %s for option %s",
+                                             g_variant_get_type_string (value),
+                                             key);
+      g_clear_pointer (&value, g_variant_unref);
+      g_clear_pointer (&key, g_free);
+      return TRUE;
+    }
+
+  attr.comm = !!comm;
+  attr.config = config;
+  attr.disabled = disabled;
+  attr.exclude_idle = !!exclude_idle;
+  attr.mmap = !!mmap_;
+  attr.sample_id_all = sample_id_all;
+  attr.sample_period = sample_period;
+  attr.sample_type = sample_type;
+  attr.task = !!task;
+  attr.type = type;
+  attr.wakeup_events = wakeup_events;
+
+#ifdef HAVE_PERF_CLOCKID
+  if (!use_clockid || clockid < 0)
+    attr.clockid = CLOCK_MONOTONIC_RAW;
+  else
+    attr.clockid = clockid;
+  attr.use_clockid = use_clockid;
+#endif
+
+  attr.size = sizeof attr;
+
+  errno = 0;
+  fd = _perf_event_open (&attr, pid, cpu, -1, 0);
+
+  if (fd < 0)
+    {
+      g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
+                                             G_DBUS_ERROR,
+                                             G_DBUS_ERROR_FAILED,
+                                             "Failed to open perf event stream: %s",
+                                             g_strerror (errno));
+    }
+
+  fd_list = g_unix_fd_list_new ();
+  handle = g_unix_fd_list_append (fd_list, fd, NULL);
+
+  if (handle < 0)
+    {
+      g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
+                                             G_DBUS_ERROR,
+                                             G_DBUS_ERROR_FAILED,
+                                             "Failed to send Unix FD List");
+      goto close_fd;
+    }
+
+  g_dbus_method_invocation_return_value_with_unix_fd_list (g_steal_pointer (&invocation),
+                                                           g_variant_new_handle (handle),
+                                                           fd_list);
+
+close_fd:
+  if (fd != -1)
+    close (fd);
+
+  return TRUE;
+}
+#endif
+
 static gboolean
 ipc_service_impl_g_authorize_method (GDBusInterfaceSkeleton *skeleton,
                                      GDBusMethodInvocation  *invocation)
@@ -152,6 +375,9 @@ init_service_iface (IpcServiceIface *iface)
 {
   iface->handle_list_processes = ipc_service_impl_handle_list_processes;
   iface->handle_get_proc_file = ipc_service_impl_handle_get_proc_file;
+#ifdef __linux__
+  iface->handle_perf_event_open = ipc_service_impl_handle_perf_event_open;
+#endif
 }
 
 G_DEFINE_TYPE_WITH_CODE (IpcServiceImpl, ipc_service_impl, IPC_TYPE_SERVICE_SKELETON,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]