mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
Another step towards dropping GLib as a dependency of libsysprof-capture. Unlike the previous commit which replaced GLib integer types with the bitwise equivalent C standard types, `stdbool` is potentially a different width from `gboolean`, so this is an ABI break. It therefore involves some changes to callback functions in the tests and tools, and in libsysprof. Signed-off-by: Philip Withnall <withnall@endlessm.com> Helps: #40
314 lines
8.8 KiB
C
314 lines
8.8 KiB
C
/* sysprof-control-source.c
|
|
*
|
|
* Copyright 2020 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-control-source"
|
|
|
|
#include "config.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <glib-unix.h>
|
|
#include <glib/gstdio.h>
|
|
#include <gio/gunixfdlist.h>
|
|
#include <gio/gunixinputstream.h>
|
|
#include <gio/gunixoutputstream.h>
|
|
#include <gio/gunixconnection.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "mapped-ring-buffer.h"
|
|
#include "mapped-ring-buffer-source.h"
|
|
|
|
#include "sysprof-control-source.h"
|
|
|
|
#define CREATRING "CreatRing\0"
|
|
#define CREATRING_LEN 10
|
|
|
|
struct _SysprofControlSource
|
|
{
|
|
GObject parent_instance;
|
|
SysprofCaptureWriter *writer;
|
|
GArray *source_ids;
|
|
|
|
#ifdef G_OS_UNIX
|
|
GUnixConnection *conn;
|
|
#endif
|
|
|
|
GCancellable *cancellable;
|
|
|
|
/* Control messages are 10 bytes */
|
|
gchar read_buf[10];
|
|
|
|
guint stopped : 1;
|
|
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
SysprofControlSource *self;
|
|
guint id;
|
|
} RingData;
|
|
|
|
static void source_iface_init (SysprofSourceInterface *iface);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (SysprofControlSource, sysprof_control_source, G_TYPE_OBJECT,
|
|
G_IMPLEMENT_INTERFACE (SYSPROF_TYPE_SOURCE, source_iface_init))
|
|
|
|
static void
|
|
ring_data_free (RingData *rd)
|
|
{
|
|
g_clear_object (&rd->self);
|
|
g_slice_free (RingData, rd);
|
|
}
|
|
|
|
SysprofControlSource *
|
|
sysprof_control_source_new (void)
|
|
{
|
|
return g_object_new (SYSPROF_TYPE_CONTROL_SOURCE, NULL);
|
|
}
|
|
|
|
static void
|
|
remove_source_id (gpointer data)
|
|
{
|
|
guint *id = data;
|
|
g_source_remove (*id);
|
|
}
|
|
|
|
static void
|
|
sysprof_control_source_finalize (GObject *object)
|
|
{
|
|
SysprofControlSource *self = (SysprofControlSource *)object;
|
|
|
|
#ifdef G_OS_UNIX
|
|
g_clear_object (&self->conn);
|
|
#endif
|
|
|
|
if (self->source_ids->len > 0)
|
|
g_array_remove_range (self->source_ids, 0, self->source_ids->len);
|
|
|
|
g_clear_pointer (&self->source_ids, g_array_unref);
|
|
|
|
G_OBJECT_CLASS (sysprof_control_source_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
sysprof_control_source_class_init (SysprofControlSourceClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = sysprof_control_source_finalize;
|
|
}
|
|
|
|
static void
|
|
sysprof_control_source_init (SysprofControlSource *self)
|
|
{
|
|
self->cancellable = g_cancellable_new ();
|
|
self->source_ids = g_array_new (FALSE, FALSE, sizeof (guint));
|
|
g_array_set_clear_func (self->source_ids, remove_source_id);
|
|
}
|
|
|
|
static bool
|
|
event_frame_cb (const void *data,
|
|
size_t *length,
|
|
void *user_data)
|
|
{
|
|
const SysprofCaptureFrame *fr = data;
|
|
RingData *rd = user_data;
|
|
|
|
g_assert (rd != NULL);
|
|
g_assert (SYSPROF_IS_CONTROL_SOURCE (rd->self));
|
|
g_assert (rd->id > 0);
|
|
|
|
if G_UNLIKELY (rd->self->writer == NULL ||
|
|
*length < sizeof *fr ||
|
|
*length < fr->len ||
|
|
fr->type >= SYSPROF_CAPTURE_FRAME_LAST)
|
|
goto remove_source;
|
|
|
|
_sysprof_capture_writer_add_raw (rd->self->writer, fr);
|
|
|
|
*length = fr->len;
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
remove_source:
|
|
for (guint i = 0; i < rd->self->source_ids->len; i++)
|
|
{
|
|
guint id = g_array_index (rd->self->source_ids, guint, i);
|
|
|
|
if (id == rd->id)
|
|
{
|
|
g_array_remove_index (rd->self->source_ids, i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return G_SOURCE_REMOVE;
|
|
}
|
|
|
|
#ifdef G_OS_UNIX
|
|
static void
|
|
sysprof_control_source_read_cb (GObject *object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
g_autoptr(SysprofControlSource) self = user_data;
|
|
GInputStream *input_stream = (GInputStream *)object;
|
|
gssize ret;
|
|
|
|
g_assert (SYSPROF_IS_CONTROL_SOURCE (self));
|
|
g_assert (G_IS_ASYNC_RESULT (result));
|
|
g_assert (G_IS_INPUT_STREAM (input_stream));
|
|
|
|
ret = g_input_stream_read_finish (G_INPUT_STREAM (input_stream), result, NULL);
|
|
|
|
if (ret == sizeof self->read_buf)
|
|
{
|
|
if (memcmp (self->read_buf, CREATRING, CREATRING_LEN) == 0)
|
|
{
|
|
g_autoptr(MappedRingBuffer) buffer = NULL;
|
|
|
|
if ((buffer = mapped_ring_buffer_new_reader (0)))
|
|
{
|
|
int fd = mapped_ring_buffer_get_fd (buffer);
|
|
RingData *rd;
|
|
|
|
rd = g_slice_new0 (RingData);
|
|
rd->self = g_object_ref (self);
|
|
rd->id = mapped_ring_buffer_create_source_full (buffer,
|
|
event_frame_cb,
|
|
rd,
|
|
(GDestroyNotify)ring_data_free);
|
|
|
|
g_array_append_val (self->source_ids, rd->id);
|
|
g_unix_connection_send_fd (self->conn, fd, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
if (!g_cancellable_is_cancelled (self->cancellable))
|
|
g_input_stream_read_async (G_INPUT_STREAM (input_stream),
|
|
self->read_buf,
|
|
sizeof self->read_buf,
|
|
G_PRIORITY_HIGH,
|
|
self->cancellable,
|
|
sysprof_control_source_read_cb,
|
|
g_object_ref (self));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
sysprof_control_source_modify_spawn (SysprofSource *source,
|
|
SysprofSpawnable *spawnable)
|
|
{
|
|
#ifdef G_OS_UNIX
|
|
SysprofControlSource *self = (SysprofControlSource *)source;
|
|
g_autofree gchar *child_no_str = NULL;
|
|
g_autoptr(GSocketConnection) stream = NULL;
|
|
g_autoptr(GSocket) sock = NULL;
|
|
GInputStream *input_stream;
|
|
int fds[2];
|
|
int child_no;
|
|
|
|
g_assert (SYSPROF_IS_SOURCE (source));
|
|
g_assert (SYSPROF_IS_SPAWNABLE (spawnable));
|
|
|
|
/* Create a socket pair to communicate D-Bus protocol over */
|
|
if (socketpair (AF_LOCAL, SOCK_STREAM, 0, fds) != 0)
|
|
return;
|
|
|
|
g_unix_set_fd_nonblocking (fds[0], TRUE, NULL);
|
|
g_unix_set_fd_nonblocking (fds[1], TRUE, NULL);
|
|
|
|
/* @child_no is assigned the FD the child will receive. We can
|
|
* use that to set the environment vaiable of the control FD.
|
|
*/
|
|
child_no = sysprof_spawnable_take_fd (spawnable, fds[1], -1);
|
|
child_no_str = g_strdup_printf ("%d", child_no);
|
|
sysprof_spawnable_setenv (spawnable, "SYSPROF_CONTROL_FD", child_no_str);
|
|
|
|
/* We need an IOStream for GDBusConnection to use. Since we need
|
|
* the ability to pass FDs, it must be a GUnixSocketConnection.
|
|
*/
|
|
if (!(sock = g_socket_new_from_fd (fds[0], NULL)))
|
|
{
|
|
close (fds[0]);
|
|
g_critical ("Failed to create GSocket");
|
|
return;
|
|
}
|
|
|
|
g_socket_set_blocking (sock, FALSE);
|
|
|
|
stream = g_socket_connection_factory_create_connection (sock);
|
|
|
|
g_assert (G_IS_UNIX_CONNECTION (stream));
|
|
|
|
self->conn = g_object_ref (G_UNIX_CONNECTION (stream));
|
|
|
|
input_stream = g_io_stream_get_input_stream (G_IO_STREAM (stream));
|
|
|
|
g_input_stream_read_async (input_stream,
|
|
self->read_buf,
|
|
sizeof self->read_buf,
|
|
G_PRIORITY_HIGH,
|
|
self->cancellable,
|
|
sysprof_control_source_read_cb,
|
|
g_object_ref (self));
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
sysprof_control_source_stop (SysprofSource *source)
|
|
{
|
|
SysprofControlSource *self = (SysprofControlSource *)source;
|
|
|
|
g_assert (SYSPROF_IS_CONTROL_SOURCE (self));
|
|
|
|
self->stopped = TRUE;
|
|
|
|
g_cancellable_cancel (self->cancellable);
|
|
|
|
if (self->source_ids->len > 0)
|
|
g_array_remove_range (self->source_ids, 0, self->source_ids->len);
|
|
|
|
sysprof_source_emit_finished (source);
|
|
}
|
|
|
|
static void
|
|
sysprof_control_source_set_writer (SysprofSource *source,
|
|
SysprofCaptureWriter *writer)
|
|
{
|
|
SysprofControlSource *self = (SysprofControlSource *)source;
|
|
|
|
g_assert (SYSPROF_IS_CONTROL_SOURCE (self));
|
|
|
|
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
|
|
|
|
if (writer != NULL)
|
|
self->writer = sysprof_capture_writer_ref (writer);
|
|
}
|
|
|
|
static void
|
|
source_iface_init (SysprofSourceInterface *iface)
|
|
{
|
|
iface->set_writer = sysprof_control_source_set_writer;
|
|
iface->modify_spawn = sysprof_control_source_modify_spawn;
|
|
iface->stop = sysprof_control_source_stop;
|
|
}
|