mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2026-02-12 16:10:54 +00:00
libsysprof/perf-event-stream: do local syscall when DBus is unavailable
If we are setup without a D-Bus connection to use to talk to sysprofd then try to perform the syscall() directly for perf_event_open. Generally that means you need to be root.
This commit is contained in:
@ -40,9 +40,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <glib/gstdio.h>
|
#include <linux/perf_event.h>
|
||||||
#include <gio/gio.h>
|
|
||||||
#include <gio/gunixfdlist.h>
|
|
||||||
#ifdef HAVE_STDATOMIC_H
|
#ifdef HAVE_STDATOMIC_H
|
||||||
# include <stdatomic.h>
|
# include <stdatomic.h>
|
||||||
#endif
|
#endif
|
||||||
@ -52,6 +50,10 @@
|
|||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <glib/gstdio.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#include <gio/gunixfdlist.h>
|
||||||
|
|
||||||
#include "sysprof-perf-event-stream-private.h"
|
#include "sysprof-perf-event-stream-private.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -79,8 +81,6 @@ struct _SysprofPerfEventStream
|
|||||||
gpointer callback_data;
|
gpointer callback_data;
|
||||||
GDestroyNotify callback_data_destroy;
|
GDestroyNotify callback_data_destroy;
|
||||||
|
|
||||||
DexPromise *promise;
|
|
||||||
|
|
||||||
int perf_fd;
|
int perf_fd;
|
||||||
|
|
||||||
struct perf_event_mmap_page *map;
|
struct perf_event_mmap_page *map;
|
||||||
@ -109,6 +109,16 @@ G_DEFINE_FINAL_TYPE (SysprofPerfEventStream, sysprof_perf_event_stream, G_TYPE_O
|
|||||||
|
|
||||||
static GParamSpec *properties [N_PROPS];
|
static GParamSpec *properties [N_PROPS];
|
||||||
|
|
||||||
|
static int
|
||||||
|
_perf_event_open (const struct perf_event_attr *attr,
|
||||||
|
pid_t pid,
|
||||||
|
int cpu,
|
||||||
|
int group_fd,
|
||||||
|
unsigned long flags)
|
||||||
|
{
|
||||||
|
return syscall (SYS_perf_event_open, attr, pid, cpu, group_fd, flags);
|
||||||
|
}
|
||||||
|
|
||||||
GVariant *
|
GVariant *
|
||||||
_sysprof_perf_event_attr_to_variant (const struct perf_event_attr *attr)
|
_sysprof_perf_event_attr_to_variant (const struct perf_event_attr *attr)
|
||||||
{
|
{
|
||||||
@ -339,8 +349,6 @@ sysprof_perf_event_stream_finalize (GObject *object)
|
|||||||
|
|
||||||
self->callback = NULL;
|
self->callback = NULL;
|
||||||
|
|
||||||
dex_clear (&self->promise);
|
|
||||||
|
|
||||||
g_clear_object (&self->connection);
|
g_clear_object (&self->connection);
|
||||||
|
|
||||||
if (self->source != NULL)
|
if (self->source != NULL)
|
||||||
@ -351,6 +359,7 @@ sysprof_perf_event_stream_finalize (GObject *object)
|
|||||||
}
|
}
|
||||||
|
|
||||||
g_clear_fd (&self->perf_fd, NULL);
|
g_clear_fd (&self->perf_fd, NULL);
|
||||||
|
g_clear_fd (&self->group_fd, NULL);
|
||||||
|
|
||||||
if (self->map != NULL && self->map_size > 0)
|
if (self->map != NULL && self->map_size > 0)
|
||||||
{
|
{
|
||||||
@ -401,65 +410,102 @@ static void
|
|||||||
sysprof_perf_event_stream_init (SysprofPerfEventStream *self)
|
sysprof_perf_event_stream_init (SysprofPerfEventStream *self)
|
||||||
{
|
{
|
||||||
self->perf_fd = -1;
|
self->perf_fd = -1;
|
||||||
|
self->group_fd = -1;
|
||||||
self->self_pid = getpid ();
|
self->self_pid = getpid ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static DexFuture *
|
||||||
sysprof_perf_event_stream_new_cb (GObject *object,
|
sysprof_perf_event_stream_new_fiber (gpointer data)
|
||||||
GAsyncResult *result,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
{
|
||||||
GDBusConnection *connection = (GDBusConnection *)object;
|
SysprofPerfEventStream *self = data;
|
||||||
g_autoptr(SysprofPerfEventStream) self = user_data;
|
g_autofd int perf_fd = -1;
|
||||||
g_autoptr(GUnixFDList) fd_list = NULL;
|
gsize map_size;
|
||||||
g_autoptr(GVariant) ret = NULL;
|
guint8 *map;
|
||||||
g_autoptr(GError) error = NULL;
|
|
||||||
|
|
||||||
g_assert (G_IS_DBUS_CONNECTION (connection));
|
|
||||||
g_assert (G_IS_ASYNC_RESULT (result));
|
|
||||||
g_assert (SYSPROF_IS_PERF_EVENT_STREAM (self));
|
g_assert (SYSPROF_IS_PERF_EVENT_STREAM (self));
|
||||||
|
|
||||||
if ((ret = g_dbus_connection_call_with_unix_fd_list_finish (connection, &fd_list, result, &error)))
|
if (self->connection != NULL)
|
||||||
{
|
{
|
||||||
|
g_autoptr(GUnixFDList) fd_list = NULL;
|
||||||
|
g_autoptr(GUnixFDList) out_fd_list = NULL;
|
||||||
|
g_autoptr(GVariant) options = _sysprof_perf_event_attr_to_variant (&self->attr);
|
||||||
|
g_autoptr(DexFuture) future = NULL;
|
||||||
|
g_autoptr(GVariant) reply = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
int group_fd_handle = -1;
|
||||||
int handle;
|
int handle;
|
||||||
int fd;
|
|
||||||
|
|
||||||
g_variant_get (ret, "(h)", &handle);
|
if (self->group_fd > -1)
|
||||||
|
|
||||||
if (-1 != (fd = g_unix_fd_list_get (fd_list, handle, &error)))
|
|
||||||
{
|
{
|
||||||
gsize map_size;
|
fd_list = g_unix_fd_list_new ();
|
||||||
guint8 *map;
|
group_fd_handle = g_unix_fd_list_append (fd_list, self->group_fd, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
self->perf_fd = fd;
|
future = dex_dbus_connection_call_with_unix_fd_list (self->connection,
|
||||||
|
"org.gnome.Sysprof3",
|
||||||
|
"/org/gnome/Sysprof3",
|
||||||
|
"org.gnome.Sysprof3.Service",
|
||||||
|
"PerfEventOpen",
|
||||||
|
g_variant_new ("(@a{sv}iiht)",
|
||||||
|
options,
|
||||||
|
-1,
|
||||||
|
self->cpu,
|
||||||
|
group_fd_handle,
|
||||||
|
self->flags),
|
||||||
|
G_VARIANT_TYPE ("(h)"),
|
||||||
|
G_DBUS_CALL_FLAGS_NONE,
|
||||||
|
G_MAXUINT,
|
||||||
|
fd_list);
|
||||||
|
|
||||||
map_size = N_PAGES * sysprof_getpagesize () + sysprof_getpagesize ();
|
if (!dex_await (dex_ref (future), &error))
|
||||||
map = mmap (NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
return dex_future_new_for_error (g_steal_pointer (&error));
|
||||||
|
|
||||||
if ((gpointer)map == MAP_FAILED)
|
g_assert (DEX_IS_FUTURE_SET (future));
|
||||||
{
|
|
||||||
int errsv = errno;
|
if (!(out_fd_list = dex_await_object (dex_ref (dex_future_set_get_future_at (DEX_FUTURE_SET (future), 1)), &error)) ||
|
||||||
g_set_error_literal (&error,
|
!(reply = dex_await_variant (dex_ref (dex_future_set_get_future_at (DEX_FUTURE_SET (future), 0)), &error)))
|
||||||
G_IO_ERROR,
|
return dex_future_new_for_error (g_steal_pointer (&error));
|
||||||
g_io_error_from_errno (errsv),
|
|
||||||
g_strerror (errsv));
|
g_variant_get (reply, "(h)", &handle);
|
||||||
}
|
|
||||||
else
|
if (-1 == (perf_fd = g_unix_fd_list_get (out_fd_list, handle, &error)))
|
||||||
{
|
return dex_future_new_for_error (g_steal_pointer (&error));
|
||||||
self->map_size = map_size;
|
}
|
||||||
self->map = (gpointer)map;
|
else
|
||||||
self->map_data = map + sysprof_getpagesize ();
|
{
|
||||||
self->tail = 0;
|
perf_fd = _perf_event_open (&self->attr, -1, self->cpu, self->group_fd, self->flags);
|
||||||
}
|
|
||||||
|
if (perf_fd < 0)
|
||||||
|
{
|
||||||
|
int errsv = errno;
|
||||||
|
return dex_future_new_reject (G_IO_ERROR,
|
||||||
|
g_io_error_from_errno (errsv),
|
||||||
|
"%s", g_strerror (errsv));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error != NULL)
|
g_assert (self->perf_fd == -1);
|
||||||
dex_promise_reject (self->promise, g_steal_pointer (&error));
|
g_assert (perf_fd > -1);
|
||||||
else
|
|
||||||
dex_promise_resolve_object (self->promise, g_object_ref (self));
|
|
||||||
|
|
||||||
dex_clear (&self->promise);
|
self->perf_fd = g_steal_fd (&perf_fd);
|
||||||
|
|
||||||
|
map_size = N_PAGES * sysprof_getpagesize () + sysprof_getpagesize ();
|
||||||
|
map = mmap (NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, self->perf_fd, 0);
|
||||||
|
|
||||||
|
if ((gpointer)map == MAP_FAILED)
|
||||||
|
{
|
||||||
|
int errsv = errno;
|
||||||
|
return dex_future_new_reject (G_IO_ERROR,
|
||||||
|
g_io_error_from_errno (errsv),
|
||||||
|
"%s", g_strerror (errsv));
|
||||||
|
}
|
||||||
|
|
||||||
|
self->map_size = map_size;
|
||||||
|
self->map = (gpointer)map;
|
||||||
|
self->map_data = map + sysprof_getpagesize ();
|
||||||
|
self->tail = 0;
|
||||||
|
|
||||||
|
return dex_future_new_take_object (g_object_ref (self));
|
||||||
}
|
}
|
||||||
|
|
||||||
DexFuture *
|
DexFuture *
|
||||||
@ -474,29 +520,22 @@ sysprof_perf_event_stream_new (GDBusConnection *connection,
|
|||||||
{
|
{
|
||||||
SysprofPerfEventSource *source;
|
SysprofPerfEventSource *source;
|
||||||
g_autoptr(SysprofPerfEventStream) self = NULL;
|
g_autoptr(SysprofPerfEventStream) self = NULL;
|
||||||
g_autoptr(GUnixFDList) fd_list = NULL;
|
|
||||||
g_autoptr(DexPromise) promise = NULL;
|
|
||||||
g_autoptr(GVariant) options = NULL;
|
|
||||||
g_autofree char *name = NULL;
|
g_autofree char *name = NULL;
|
||||||
int group_fd_handle = -1;
|
|
||||||
|
|
||||||
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
|
g_return_val_if_fail (!connection || G_IS_DBUS_CONNECTION (connection), NULL);
|
||||||
g_return_val_if_fail (attr != NULL, NULL);
|
g_return_val_if_fail (attr != NULL, NULL);
|
||||||
g_return_val_if_fail (cpu > -1, NULL);
|
g_return_val_if_fail (cpu > -1, NULL);
|
||||||
g_return_val_if_fail (group_fd >= -1, NULL);
|
g_return_val_if_fail (group_fd >= -1, NULL);
|
||||||
|
|
||||||
promise = dex_promise_new ();
|
|
||||||
|
|
||||||
self = g_object_new (SYSPROF_TYPE_PERF_EVENT_STREAM, NULL);
|
self = g_object_new (SYSPROF_TYPE_PERF_EVENT_STREAM, NULL);
|
||||||
self->connection = g_object_ref (connection);
|
self->connection = connection ? g_object_ref (connection) : NULL;
|
||||||
self->attr = *attr;
|
self->attr = *attr;
|
||||||
self->cpu = cpu;
|
self->cpu = cpu;
|
||||||
self->group_fd = group_fd;
|
self->group_fd = group_fd > -1 ? dup (group_fd) : -1;
|
||||||
self->flags = flags;
|
self->flags = flags;
|
||||||
self->callback = callback;
|
self->callback = callback;
|
||||||
self->callback_data = callback_data;
|
self->callback_data = callback_data;
|
||||||
self->callback_data_destroy = callback_data_destroy;
|
self->callback_data_destroy = callback_data_destroy;
|
||||||
self->promise = dex_ref (promise);
|
|
||||||
|
|
||||||
source = (SysprofPerfEventSource *)
|
source = (SysprofPerfEventSource *)
|
||||||
g_source_new (&source_funcs, sizeof (SysprofPerfEventSource));
|
g_source_new (&source_funcs, sizeof (SysprofPerfEventSource));
|
||||||
@ -511,34 +550,10 @@ sysprof_perf_event_stream_new (GDBusConnection *connection,
|
|||||||
g_source_set_name (self->source, name);
|
g_source_set_name (self->source, name);
|
||||||
g_source_attach (self->source, NULL);
|
g_source_attach (self->source, NULL);
|
||||||
|
|
||||||
if (group_fd > -1)
|
return dex_scheduler_spawn (NULL, 0,
|
||||||
{
|
sysprof_perf_event_stream_new_fiber,
|
||||||
fd_list = g_unix_fd_list_new ();
|
g_object_ref (self),
|
||||||
group_fd_handle = g_unix_fd_list_append (fd_list, group_fd, NULL);
|
g_object_unref);
|
||||||
}
|
|
||||||
|
|
||||||
options = _sysprof_perf_event_attr_to_variant (attr);
|
|
||||||
|
|
||||||
g_dbus_connection_call_with_unix_fd_list (connection,
|
|
||||||
"org.gnome.Sysprof3",
|
|
||||||
"/org/gnome/Sysprof3",
|
|
||||||
"org.gnome.Sysprof3.Service",
|
|
||||||
"PerfEventOpen",
|
|
||||||
g_variant_new ("(@a{sv}iiht)",
|
|
||||||
options,
|
|
||||||
-1,
|
|
||||||
cpu,
|
|
||||||
group_fd_handle,
|
|
||||||
flags),
|
|
||||||
G_VARIANT_TYPE ("(h)"),
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
G_MAXUINT,
|
|
||||||
fd_list,
|
|
||||||
dex_promise_get_cancellable (promise),
|
|
||||||
sysprof_perf_event_stream_new_cb,
|
|
||||||
g_object_ref (self));
|
|
||||||
|
|
||||||
return DEX_FUTURE (g_steal_pointer (&promise));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
|
|||||||
Reference in New Issue
Block a user