[sysprof] sysprofd: implement mechanics for perf_event_open
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [sysprof] sysprofd: implement mechanics for perf_event_open
- Date: Wed, 29 May 2019 22:17:17 +0000 (UTC)
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]