mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2026-02-11 07:30:54 +00:00
src: use helpers and add group_fd to remote API
This commit is contained in:
@ -44,11 +44,15 @@ libsysprof_public_headers = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
libsysprof_private_sources = [
|
libsysprof_private_sources = [
|
||||||
'../stackstash.c',
|
|
||||||
'binfile.c',
|
'binfile.c',
|
||||||
'demangle.cpp',
|
'demangle.cpp',
|
||||||
'elfparser.c',
|
'elfparser.c',
|
||||||
|
'ipc-service-client.c',
|
||||||
|
'sysprof-helpers.c',
|
||||||
'sysprof-line-reader.c',
|
'sysprof-line-reader.c',
|
||||||
|
ipc_service_src,
|
||||||
|
stackstash_sources,
|
||||||
|
helpers_sources,
|
||||||
]
|
]
|
||||||
|
|
||||||
libsysprof_public_sources += libsysprof_capture_sources
|
libsysprof_public_sources += libsysprof_capture_sources
|
||||||
@ -94,7 +98,9 @@ libsysprof = shared_library(
|
|||||||
'sysprof-@0@'.format(libsysprof_api_version),
|
'sysprof-@0@'.format(libsysprof_api_version),
|
||||||
libsysprof_public_sources + libsysprof_private_sources,
|
libsysprof_public_sources + libsysprof_private_sources,
|
||||||
|
|
||||||
include_directories: [include_directories('.'), libsysprof_capture_include_dirs],
|
include_directories: [include_directories('.'),
|
||||||
|
ipc_include_dirs,
|
||||||
|
libsysprof_capture_include_dirs],
|
||||||
dependencies: libsysprof_deps,
|
dependencies: libsysprof_deps,
|
||||||
c_args: libsysprof_c_args,
|
c_args: libsysprof_c_args,
|
||||||
install: true,
|
install: true,
|
||||||
|
|||||||
353
src/libsysprof/sysprof-helpers.c
Normal file
353
src/libsysprof/sysprof-helpers.c
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
/* sysprof-helpers.c
|
||||||
|
*
|
||||||
|
* Copyright 2019 Christian Hergert <chergert@redhat.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define G_LOG_DOMAIN "sysprof-helpers"
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <gio/gunixfdlist.h>
|
||||||
|
|
||||||
|
#include "sysprof-helpers.h"
|
||||||
|
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "ipc-service.h"
|
||||||
|
|
||||||
|
struct _SysprofHelpers
|
||||||
|
{
|
||||||
|
GObject parent_instance;
|
||||||
|
IpcService *proxy;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (SysprofHelpers, sysprof_helpers, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static void
|
||||||
|
sysprof_helpers_finalize (GObject *object)
|
||||||
|
{
|
||||||
|
SysprofHelpers *self = (SysprofHelpers *)object;
|
||||||
|
|
||||||
|
g_clear_object (&self->proxy);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (sysprof_helpers_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sysprof_helpers_class_init (SysprofHelpersClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
object_class->finalize = sysprof_helpers_finalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sysprof_helpers_init (SysprofHelpers *self)
|
||||||
|
{
|
||||||
|
g_autoptr(GDBusConnection) bus = NULL;
|
||||||
|
|
||||||
|
bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
|
||||||
|
g_return_if_fail (bus != NULL);
|
||||||
|
|
||||||
|
self->proxy = ipc_service_proxy_new_sync (bus,
|
||||||
|
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION,
|
||||||
|
"org.gnome.Sysprof3",
|
||||||
|
"/org/gnome/Sysprof3",
|
||||||
|
NULL, NULL);
|
||||||
|
g_return_if_fail (self->proxy != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
SysprofHelpers *
|
||||||
|
sysprof_helpers_get_default (void)
|
||||||
|
{
|
||||||
|
static SysprofHelpers *instance;
|
||||||
|
|
||||||
|
if (g_once_init_enter (&instance))
|
||||||
|
{
|
||||||
|
SysprofHelpers *self = g_object_new (SYSPROF_TYPE_HELPERS, NULL);
|
||||||
|
g_object_add_weak_pointer (G_OBJECT (instance), (gpointer *)&instance);
|
||||||
|
g_once_init_leave (&instance, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
fail_if_no_proxy (SysprofHelpers *self,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
g_assert (SYSPROF_IS_HELPERS (self));
|
||||||
|
g_assert (G_IS_TASK (task));
|
||||||
|
|
||||||
|
if (self->proxy == NULL)
|
||||||
|
{
|
||||||
|
g_task_return_new_error (task,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_CONNECTED,
|
||||||
|
"No D-Bus proxy to communicate with daemon");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sysprof_helpers_list_processes_cb (IpcService *service,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_autoptr(GTask) task = user_data;
|
||||||
|
g_autoptr(GVariant) processes = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
|
||||||
|
g_assert (IPC_IS_SERVICE (service));
|
||||||
|
g_assert (G_IS_ASYNC_RESULT (result));
|
||||||
|
g_assert (G_IS_TASK (task));
|
||||||
|
|
||||||
|
if (!ipc_service_call_list_processes_finish (service, &processes, result, &error))
|
||||||
|
{
|
||||||
|
g_autofree gint32 *out_processes = NULL;
|
||||||
|
gsize out_n_processes = 0;
|
||||||
|
|
||||||
|
if (helpers_list_processes (&out_processes, &out_n_processes))
|
||||||
|
processes = g_variant_new_fixed_array (G_VARIANT_TYPE_INT32,
|
||||||
|
out_processes,
|
||||||
|
out_n_processes,
|
||||||
|
sizeof (gint32));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processes != NULL)
|
||||||
|
g_task_return_pointer (task, g_steal_pointer (&processes), (GDestroyNotify) g_variant_unref);
|
||||||
|
else
|
||||||
|
g_task_return_new_error (task,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"Failed to list processes");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sysprof_helpers_list_processes_async (SysprofHelpers *self,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_autoptr(GTask) task = NULL;
|
||||||
|
|
||||||
|
g_return_if_fail (SYSPROF_IS_HELPERS (self));
|
||||||
|
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
|
||||||
|
|
||||||
|
task = g_task_new (self, cancellable, callback, user_data);
|
||||||
|
g_task_set_source_tag (task, sysprof_helpers_list_processes_async);
|
||||||
|
|
||||||
|
if (!fail_if_no_proxy (self, task))
|
||||||
|
ipc_service_call_list_processes (self->proxy,
|
||||||
|
cancellable,
|
||||||
|
(GAsyncReadyCallback) sysprof_helpers_list_processes_cb,
|
||||||
|
g_steal_pointer (&task));
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
sysprof_helpers_list_processes_finish (SysprofHelpers *self,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gint32 **processes,
|
||||||
|
gsize *n_processes,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(GVariant) ret = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
|
||||||
|
g_return_val_if_fail (G_IS_TASK (result), FALSE);
|
||||||
|
|
||||||
|
if ((ret = g_task_propagate_pointer (G_TASK (result), error)))
|
||||||
|
{
|
||||||
|
const gint32 *p;
|
||||||
|
gsize n;
|
||||||
|
|
||||||
|
p = g_variant_get_fixed_array (ret, &n, sizeof (gint32));
|
||||||
|
|
||||||
|
if (processes != NULL)
|
||||||
|
*processes = g_memdup (p, n * sizeof (gint32));
|
||||||
|
|
||||||
|
if (n_processes != NULL)
|
||||||
|
*n_processes = n;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
static void
|
||||||
|
sysprof_helpers_get_proc_file_cb (IpcService *service,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_autoptr(GTask) task = user_data;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
g_autofree gchar *contents = NULL;
|
||||||
|
|
||||||
|
g_assert (IPC_IS_SERVICE (service));
|
||||||
|
g_assert (G_IS_ASYNC_RESULT (result));
|
||||||
|
g_assert (G_IS_TASK (task));
|
||||||
|
|
||||||
|
if (!ipc_service_call_get_proc_file_finish (service, &contents, result, &error))
|
||||||
|
g_task_return_error (task, g_steal_pointer (&error));
|
||||||
|
else
|
||||||
|
g_task_return_pointer (task, g_steal_pointer (&contents), g_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sysprof_helpers_get_proc_file_async (SysprofHelpers *self,
|
||||||
|
const gchar *path,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_autoptr(GTask) task = NULL;
|
||||||
|
|
||||||
|
g_return_if_fail (SYSPROF_IS_HELPERS (self));
|
||||||
|
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
|
||||||
|
|
||||||
|
task = g_task_new (self, cancellable, callback, user_data);
|
||||||
|
g_task_set_source_tag (task, sysprof_helpers_list_processes_async);
|
||||||
|
|
||||||
|
if (!fail_if_no_proxy (self, task))
|
||||||
|
ipc_service_call_get_proc_file (self->proxy,
|
||||||
|
path,
|
||||||
|
cancellable,
|
||||||
|
(GAsyncReadyCallback) sysprof_helpers_get_proc_file_cb,
|
||||||
|
g_steal_pointer (&task));
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
sysprof_helpers_get_proc_file_finish (SysprofHelpers *self,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gchar **contents,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autofree gchar *ret = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
|
||||||
|
g_return_val_if_fail (G_IS_TASK (result), FALSE);
|
||||||
|
|
||||||
|
if ((ret = g_task_propagate_pointer (G_TASK (result), error)))
|
||||||
|
{
|
||||||
|
if (contents != NULL)
|
||||||
|
*contents = g_steal_pointer (&ret);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sysprof_helpers_perf_event_open_cb (IpcService *service,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_autoptr(GTask) task = user_data;
|
||||||
|
g_autoptr(GUnixFDList) fd_list = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
|
||||||
|
g_assert (IPC_IS_SERVICE (service));
|
||||||
|
g_assert (G_IS_ASYNC_RESULT (result));
|
||||||
|
g_assert (G_IS_TASK (task));
|
||||||
|
|
||||||
|
if (!g_dbus_proxy_call_with_unix_fd_list_finish (G_DBUS_PROXY (service),
|
||||||
|
&fd_list,
|
||||||
|
result,
|
||||||
|
&error))
|
||||||
|
g_task_return_error (task, g_steal_pointer (&error));
|
||||||
|
else
|
||||||
|
g_task_return_pointer (task, g_steal_pointer (&fd_list), g_object_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sysprof_helpers_perf_event_open_async (SysprofHelpers *self,
|
||||||
|
struct perf_event_attr *attr,
|
||||||
|
gint32 pid,
|
||||||
|
gint32 cpu,
|
||||||
|
gint32 group_fd,
|
||||||
|
guint64 flags,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_autoptr(GTask) task = NULL;
|
||||||
|
g_autoptr(GVariant) options = NULL;
|
||||||
|
|
||||||
|
g_return_if_fail (SYSPROF_IS_HELPERS (self));
|
||||||
|
g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
|
||||||
|
|
||||||
|
task = g_task_new (self, cancellable, callback, user_data);
|
||||||
|
g_task_set_source_tag (task, sysprof_helpers_list_processes_async);
|
||||||
|
|
||||||
|
if (!fail_if_no_proxy (self, task))
|
||||||
|
g_dbus_proxy_call_with_unix_fd_list (G_DBUS_PROXY (self->proxy),
|
||||||
|
"PerfEventOpen",
|
||||||
|
g_variant_new ("(@a{sv}iit)",
|
||||||
|
options,
|
||||||
|
pid,
|
||||||
|
cpu,
|
||||||
|
flags),
|
||||||
|
G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
|
||||||
|
-1,
|
||||||
|
NULL,
|
||||||
|
cancellable,
|
||||||
|
(GAsyncReadyCallback) sysprof_helpers_perf_event_open_cb,
|
||||||
|
g_steal_pointer (&task));
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
sysprof_helpers_perf_event_open_finish (SysprofHelpers *self,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gint *out_fd,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
g_autoptr(GUnixFDList) fd_list = NULL;
|
||||||
|
|
||||||
|
g_return_val_if_fail (SYSPROF_IS_HELPERS (self), FALSE);
|
||||||
|
g_return_val_if_fail (G_IS_TASK (result), FALSE);
|
||||||
|
|
||||||
|
if ((fd_list = g_task_propagate_pointer (G_TASK (result), error)))
|
||||||
|
{
|
||||||
|
if (g_unix_fd_list_get_length (fd_list) != 1)
|
||||||
|
{
|
||||||
|
g_set_error (error,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_FAILED,
|
||||||
|
"Incorrect number of FDs from peer");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_fd != NULL)
|
||||||
|
{
|
||||||
|
gint fd = g_unix_fd_list_get (fd_list, 0, error);
|
||||||
|
|
||||||
|
if (fd == -1)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
*out_fd = fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
#endif /* __linux__ */
|
||||||
70
src/libsysprof/sysprof-helpers.h
Normal file
70
src/libsysprof/sysprof-helpers.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/* sysprof-helpers.h
|
||||||
|
*
|
||||||
|
* Copyright 2019 Christian Hergert <chergert@redhat.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
# include <linux/perf_event.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define SYSPROF_TYPE_HELPERS (sysprof_helpers_get_type())
|
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE (SysprofHelpers, sysprof_helpers, SYSPROF, HELPERS, GObject)
|
||||||
|
|
||||||
|
SysprofHelpers *sysprof_helpers_get_default (void);
|
||||||
|
void sysprof_helpers_list_processes_async (SysprofHelpers *self,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean sysprof_helpers_list_processes_finish (SysprofHelpers *self,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gint32 **processes,
|
||||||
|
gsize *n_processes,
|
||||||
|
GError **error);
|
||||||
|
#ifdef __linux__
|
||||||
|
void sysprof_helpers_get_proc_file_async (SysprofHelpers *self,
|
||||||
|
const gchar *path,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean sysprof_helpers_get_proc_file_finish (SysprofHelpers *self,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gchar **contents,
|
||||||
|
GError **error);
|
||||||
|
void sysprof_helpers_perf_event_open_async (SysprofHelpers *self,
|
||||||
|
struct perf_event_attr *attr,
|
||||||
|
gint32 pid,
|
||||||
|
gint32 cpu,
|
||||||
|
gint32 group_fd,
|
||||||
|
guint64 flags,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean sysprof_helpers_perf_event_open_finish (SysprofHelpers *self,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gint *out_fd,
|
||||||
|
GError **error);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
@ -7,6 +7,18 @@ sysprof_version_conf.set('MINOR_VERSION', sysprof_version[1])
|
|||||||
sysprof_version_conf.set('MICRO_VERSION', sysprof_version[2])
|
sysprof_version_conf.set('MICRO_VERSION', sysprof_version[2])
|
||||||
sysprof_version_conf.set('VERSION', meson.project_version())
|
sysprof_version_conf.set('VERSION', meson.project_version())
|
||||||
|
|
||||||
|
ipc_service_src = gnome.gdbus_codegen('ipc-service',
|
||||||
|
sources: 'org.gnome.Sysprof3.Service.xml',
|
||||||
|
interface_prefix: 'org.gnome.Sysprof3.',
|
||||||
|
namespace: 'Ipc',
|
||||||
|
)
|
||||||
|
|
||||||
|
ipc_include_dirs = include_directories('.')
|
||||||
|
|
||||||
|
stackstash_sources = files([
|
||||||
|
'stackstash.c',
|
||||||
|
])
|
||||||
|
|
||||||
helpers_sources = files([
|
helpers_sources = files([
|
||||||
'helpers.c',
|
'helpers.c',
|
||||||
])
|
])
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
@options: key-value pair of attributes for the perf_event_open() syscall.
|
@options: key-value pair of attributes for the perf_event_open() syscall.
|
||||||
@pid: the process id to monitor, or -1 for system-wide.
|
@pid: the process id to monitor, or -1 for system-wide.
|
||||||
@cpu: affinity to cpu.
|
@cpu: affinity to cpu.
|
||||||
|
@group: a FD handle for an existing group
|
||||||
@flags: flags for perf_event_open() syscall.
|
@flags: flags for perf_event_open() syscall.
|
||||||
@perf_stream_fd: (out): A fd to communicate with perf.
|
@perf_stream_fd: (out): A fd to communicate with perf.
|
||||||
|
|
||||||
@ -17,6 +18,7 @@
|
|||||||
<arg name="options" type="a{sv}" direction="in"/>
|
<arg name="options" type="a{sv}" direction="in"/>
|
||||||
<arg name="pid" type="i" direction="in"/>
|
<arg name="pid" type="i" direction="in"/>
|
||||||
<arg name="cpu" type="i" direction="in"/>
|
<arg name="cpu" type="i" direction="in"/>
|
||||||
|
<arg name="group_fd" type="h" direction="in"/>
|
||||||
<arg name="flags" type="t" direction="in"/>
|
<arg name="flags" type="t" direction="in"/>
|
||||||
<arg name="perf_stream_fd" type="h" direction="out"/>
|
<arg name="perf_stream_fd" type="h" direction="out"/>
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
@ -24,15 +24,12 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <gio/gunixfdlist.h>
|
#include <gio/gunixfdlist.h>
|
||||||
#ifdef __linux__
|
|
||||||
# include <linux/capability.h>
|
|
||||||
# include <linux/perf_event.h>
|
|
||||||
#endif
|
|
||||||
#include <polkit/polkit.h>
|
#include <polkit/polkit.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "helpers.h"
|
||||||
#include "ipc-service-impl.h"
|
#include "ipc-service-impl.h"
|
||||||
|
|
||||||
struct _IpcServiceImpl
|
struct _IpcServiceImpl
|
||||||
@ -44,47 +41,26 @@ static gboolean
|
|||||||
ipc_service_impl_handle_list_processes (IpcService *service,
|
ipc_service_impl_handle_list_processes (IpcService *service,
|
||||||
GDBusMethodInvocation *invocation)
|
GDBusMethodInvocation *invocation)
|
||||||
{
|
{
|
||||||
g_autoptr(GDir) dir = NULL;
|
g_autofree gint32 *processes = NULL;
|
||||||
g_autoptr(GArray) pids = NULL;
|
gsize n_processes = 0;
|
||||||
const gchar *name;
|
|
||||||
|
|
||||||
g_assert (IPC_IS_SERVICE_IMPL (service));
|
g_assert (IPC_IS_SERVICE_IMPL (service));
|
||||||
g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
|
g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
|
||||||
|
|
||||||
g_message ("ListProcesses()");
|
g_message ("ListProcesses()");
|
||||||
|
|
||||||
if (!(dir = g_dir_open ("/proc/", 0, NULL)))
|
if (!helpers_list_processes (&processes, &n_processes))
|
||||||
{
|
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
||||||
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
G_DBUS_ERROR,
|
||||||
G_DBUS_ERROR,
|
G_DBUS_ERROR_NOT_SUPPORTED,
|
||||||
G_DBUS_ERROR_FILE_NOT_FOUND,
|
"Failed to access processes");
|
||||||
"Failed to access /proc");
|
else
|
||||||
return TRUE;
|
ipc_service_complete_list_processes (service,
|
||||||
}
|
g_steal_pointer (&invocation),
|
||||||
|
g_variant_new_fixed_array (G_VARIANT_TYPE_INT32,
|
||||||
pids = g_array_new (FALSE, FALSE, sizeof (gint32));
|
processes,
|
||||||
|
n_processes,
|
||||||
while ((name = g_dir_read_name (dir)))
|
sizeof (gint32)));
|
||||||
{
|
|
||||||
if (g_ascii_isalnum (*name))
|
|
||||||
{
|
|
||||||
gchar *endptr = NULL;
|
|
||||||
gint64 val = g_ascii_strtoll (name, &endptr, 10);
|
|
||||||
|
|
||||||
if (endptr != NULL && *endptr == 0 && val < G_MAXINT && val >= 0)
|
|
||||||
{
|
|
||||||
gint32 v32 = val;
|
|
||||||
g_array_append_val (pids, v32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ipc_service_complete_list_processes (service,
|
|
||||||
g_steal_pointer (&invocation),
|
|
||||||
g_variant_new_fixed_array (G_VARIANT_TYPE_INT32,
|
|
||||||
pids->data,
|
|
||||||
pids->len,
|
|
||||||
sizeof (gint32)));
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -94,7 +70,6 @@ ipc_service_impl_handle_get_proc_file (IpcService *service,
|
|||||||
GDBusMethodInvocation *invocation,
|
GDBusMethodInvocation *invocation,
|
||||||
const gchar *path)
|
const gchar *path)
|
||||||
{
|
{
|
||||||
g_autofree gchar *canon = NULL;
|
|
||||||
g_autofree gchar *contents = NULL;
|
g_autofree gchar *contents = NULL;
|
||||||
gsize len;
|
gsize len;
|
||||||
|
|
||||||
@ -103,18 +78,11 @@ ipc_service_impl_handle_get_proc_file (IpcService *service,
|
|||||||
|
|
||||||
g_message ("GetProcFile(%s)", path);
|
g_message ("GetProcFile(%s)", path);
|
||||||
|
|
||||||
canon = g_canonicalize_filename (path, "/proc/");
|
if (!helpers_get_proc_file (path, &contents, &len))
|
||||||
|
|
||||||
if (!g_str_has_prefix (canon, "/proc/"))
|
|
||||||
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
||||||
G_DBUS_ERROR,
|
G_DBUS_ERROR,
|
||||||
G_DBUS_ERROR_ACCESS_DENIED,
|
G_DBUS_ERROR_ACCESS_DENIED,
|
||||||
"File is not within /proc/");
|
"Failed to load proc file");
|
||||||
else if (!g_file_get_contents (canon, &contents, &len, NULL))
|
|
||||||
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
|
||||||
G_DBUS_ERROR,
|
|
||||||
G_DBUS_ERROR_FILE_NOT_FOUND,
|
|
||||||
"Failed to locate the file");
|
|
||||||
else
|
else
|
||||||
ipc_service_complete_get_proc_file (service,
|
ipc_service_complete_get_proc_file (service,
|
||||||
g_steal_pointer (&invocation),
|
g_steal_pointer (&invocation),
|
||||||
@ -123,222 +91,79 @@ ipc_service_impl_handle_get_proc_file (IpcService *service,
|
|||||||
return TRUE;
|
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
|
static gboolean
|
||||||
ipc_service_impl_handle_perf_event_open (IpcService *service,
|
ipc_service_impl_handle_perf_event_open (IpcService *service,
|
||||||
GDBusMethodInvocation *invocation,
|
GDBusMethodInvocation *invocation,
|
||||||
GVariant *options,
|
GVariant *options,
|
||||||
gint pid,
|
gint32 pid,
|
||||||
gint cpu,
|
gint32 cpu,
|
||||||
|
GVariant *group_fdv,
|
||||||
guint64 flags)
|
guint64 flags)
|
||||||
{
|
{
|
||||||
g_autoptr(GUnixFDList) fd_list = NULL;
|
GUnixFDList *in_fd_list = NULL;
|
||||||
struct perf_event_attr attr = {0};
|
GDBusMessage *message;
|
||||||
GVariantIter iter;
|
gint group_fd = -1;
|
||||||
GVariant *value;
|
gint out_fd = -1;
|
||||||
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 handle;
|
||||||
gint use_clockid = 0;
|
|
||||||
gint sample_id_all = 0;
|
|
||||||
|
|
||||||
g_assert (IPC_IS_SERVICE_IMPL (service));
|
g_assert (IPC_IS_SERVICE_IMPL (service));
|
||||||
g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
|
g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
|
||||||
|
|
||||||
g_message ("PerfEventOpen(pid=%d, cpu=%d)", pid, cpu);
|
g_message ("PerfEventOpen(pid=%d, cpu=%d)", pid, cpu);
|
||||||
|
|
||||||
if (pid < -1 || cpu < -1)
|
/* Consistency check for cpu/pid */
|
||||||
|
if (pid < -1 || cpu < -1 || !(pid == -1 && cpu == -1))
|
||||||
{
|
{
|
||||||
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
||||||
G_DBUS_ERROR,
|
G_DBUS_ERROR,
|
||||||
G_DBUS_ERROR_INVALID_ARGS,
|
G_DBUS_ERROR_INVALID_ARGS,
|
||||||
"pid and cpu must be >= -1");
|
"pid and cpu must be >= -1 and only one may be -1");
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_variant_iter_init (&iter, options);
|
/* Get the group_fd if provided */
|
||||||
|
message = g_dbus_method_invocation_get_message (invocation);
|
||||||
while (g_variant_iter_loop (&iter, "{sv}", &key, &value))
|
if ((in_fd_list = g_dbus_message_get_unix_fd_list (message)) &&
|
||||||
{
|
(handle = g_variant_get_handle (group_fdv)) > -1)
|
||||||
if (FALSE) {}
|
group_fd = g_unix_fd_list_get (in_fd_list, handle, NULL);
|
||||||
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;
|
errno = 0;
|
||||||
fd = _perf_event_open (&attr, pid, cpu, -1, 0);
|
if (!helpers_perf_event_open (options, pid, cpu, group_fd, flags, &out_fd))
|
||||||
|
|
||||||
if (fd < 0)
|
|
||||||
{
|
{
|
||||||
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
||||||
G_DBUS_ERROR,
|
G_DBUS_ERROR,
|
||||||
G_DBUS_ERROR_FAILED,
|
G_DBUS_ERROR_FAILED,
|
||||||
"Failed to open perf event stream: %s",
|
"Failed to create perf counter: %s",
|
||||||
g_strerror (errno));
|
g_strerror (errno));
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
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_autoptr(GUnixFDList) out_fd_list = g_unix_fd_list_new ();
|
||||||
G_DBUS_ERROR,
|
g_autoptr(GError) error = NULL;
|
||||||
G_DBUS_ERROR_FAILED,
|
|
||||||
"Failed to send Unix FD List");
|
if (-1 == (handle = g_unix_fd_list_append (out_fd_list, out_fd, &error)))
|
||||||
goto close_fd;
|
{
|
||||||
|
g_dbus_method_invocation_return_error (g_steal_pointer (&invocation),
|
||||||
|
G_DBUS_ERROR,
|
||||||
|
G_DBUS_ERROR_LIMITS_EXCEEDED,
|
||||||
|
"Failed to create file-descriptor for reply");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_value_with_unix_fd_list (g_steal_pointer (&invocation),
|
||||||
|
g_variant_new ("(h)", handle),
|
||||||
|
out_fd_list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_dbus_method_invocation_return_value_with_unix_fd_list (g_steal_pointer (&invocation),
|
if (out_fd != -1)
|
||||||
g_variant_new_handle (handle),
|
close (out_fd);
|
||||||
fd_list);
|
|
||||||
|
|
||||||
close_fd:
|
if (group_fd != -1)
|
||||||
if (fd != -1)
|
close (group_fd);
|
||||||
close (fd);
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
ipc_service_impl_g_authorize_method (GDBusInterfaceSkeleton *skeleton,
|
ipc_service_impl_g_authorize_method (GDBusInterfaceSkeleton *skeleton,
|
||||||
@ -352,6 +177,8 @@ ipc_service_impl_g_authorize_method (GDBusInterfaceSkeleton *skeleton,
|
|||||||
g_assert (IPC_IS_SERVICE_IMPL (skeleton));
|
g_assert (IPC_IS_SERVICE_IMPL (skeleton));
|
||||||
g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
|
g_assert (G_IS_DBUS_METHOD_INVOCATION (invocation));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
peer_name = g_dbus_method_invocation_get_sender (invocation);
|
peer_name = g_dbus_method_invocation_get_sender (invocation);
|
||||||
|
|
||||||
if (!(authority = polkit_authority_get_sync (NULL, NULL)) ||
|
if (!(authority = polkit_authority_get_sync (NULL, NULL)) ||
|
||||||
@ -382,9 +209,7 @@ init_service_iface (IpcServiceIface *iface)
|
|||||||
{
|
{
|
||||||
iface->handle_list_processes = ipc_service_impl_handle_list_processes;
|
iface->handle_list_processes = ipc_service_impl_handle_list_processes;
|
||||||
iface->handle_get_proc_file = ipc_service_impl_handle_get_proc_file;
|
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;
|
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,
|
G_DEFINE_TYPE_WITH_CODE (IpcServiceImpl, ipc_service_impl, IPC_TYPE_SERVICE_SKELETON,
|
||||||
|
|||||||
@ -1,14 +1,9 @@
|
|||||||
if get_option('with_sysprofd') == 'bundled'
|
if get_option('with_sysprofd') == 'bundled'
|
||||||
|
|
||||||
ipc_service_src = gnome.gdbus_codegen('ipc-service',
|
|
||||||
sources: 'org.gnome.Sysprof3.Service.xml',
|
|
||||||
interface_prefix: 'org.gnome.Sysprof3.',
|
|
||||||
namespace: 'Ipc',
|
|
||||||
)
|
|
||||||
|
|
||||||
sysprofd_sources = [
|
sysprofd_sources = [
|
||||||
'sysprofd.c',
|
'sysprofd.c',
|
||||||
'ipc-service-impl.c',
|
'ipc-service-impl.c',
|
||||||
|
helpers_sources,
|
||||||
ipc_service_src,
|
ipc_service_src,
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -27,6 +22,7 @@ sysprofd = executable('sysprofd', sysprofd_sources,
|
|||||||
install: true,
|
install: true,
|
||||||
install_dir: pkglibexecdir,
|
install_dir: pkglibexecdir,
|
||||||
pie: true,
|
pie: true,
|
||||||
|
include_directories: [include_directories('.'), ipc_include_dirs],
|
||||||
)
|
)
|
||||||
|
|
||||||
sysprofdconf = configuration_data()
|
sysprofdconf = configuration_data()
|
||||||
|
|||||||
Reference in New Issue
Block a user