diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index cfffe112..6fd8b7f7 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -9,6 +9,7 @@ libsysprof_public_sources = [ 'sysprof-category-summary.c', 'sysprof-cpu-info.c', 'sysprof-cpu-usage.c', + 'sysprof-dbus-monitor.c', 'sysprof-diagnostic.c', 'sysprof-disk-usage.c', 'sysprof-document-allocation.c', @@ -16,6 +17,7 @@ libsysprof_public_sources = [ 'sysprof-document-counter.c', 'sysprof-document-ctrdef.c', 'sysprof-document-ctrset.c', + 'sysprof-document-dbus-message.c', 'sysprof-document-exit.c', 'sysprof-document-file-chunk.c', 'sysprof-document-file.c', @@ -68,6 +70,7 @@ libsysprof_public_headers = [ 'sysprof-category-summary.h', 'sysprof-cpu-info.h', 'sysprof-cpu-usage.h', + 'sysprof-dbus-monitor.h', 'sysprof-diagnostic.h', 'sysprof-disk-usage.h', 'sysprof-document-allocation.h', diff --git a/src/libsysprof/sysprof-dbus-monitor.c b/src/libsysprof/sysprof-dbus-monitor.c new file mode 100644 index 00000000..becfe734 --- /dev/null +++ b/src/libsysprof/sysprof-dbus-monitor.c @@ -0,0 +1,367 @@ +/* sysprof-dbus-monitor.c + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-instrument-private.h" +#include "sysprof-dbus-monitor.h" +#include "sysprof-recording-private.h" + +#define MAX_EMBEDDABLE_MESSAGE_SIZE ((1<<16)-sizeof(SysprofCaptureDBusMessage)-16) + +struct _SysprofDBusMonitor +{ + SysprofInstrument parent_instance; + GBusType bus_type; + char *bus_address; +}; + +struct _SysprofDBusMonitorClass +{ + SysprofInstrumentClass parent_instance; +}; + +enum { + PROP_0, + PROP_BUS_ADDRESS, + PROP_BUS_TYPE, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDBusMonitor, sysprof_dbus_monitor, SYSPROF_TYPE_INSTRUMENT) + +static GParamSpec *properties [N_PROPS]; + +static char ** +sysprof_dbus_monitor_list_required_policy (SysprofInstrument *instrument) +{ + /* TODO: If we add a new policy for monitoring the system bus, then we can + * make sysprofd open the monitor and feed that data to the instrument + * for recording. That would allow for monitoring --system without + * having to be in fallback observation mode. + * + * "org.gnome.sysprof3.profile" is probably enough though. + */ + return NULL; +} + +typedef struct _Record +{ + SysprofRecording *recording; + DexFuture *cancellable; + char *bus_address; + GBusType bus_type; +} Record; + +static void +record_free (gpointer data) +{ + Record *record = data; + + g_clear_object (&record->recording); + dex_clear (&record->cancellable); + g_free (record); +} + +static DexFuture * +sysprof_dbus_monitor_record_fiber (gpointer user_data) +{ + Record *record = user_data; + g_autofree guint8 *read_buffer = NULL; + g_autoptr(GSubprocessLauncher) launcher = NULL; + g_autoptr(GSubprocess) subprocess = NULL; + g_autoptr(GPtrArray) argv = NULL; + g_autoptr(GError) error = NULL; + SysprofCaptureWriter *writer; + GInputStream *stdout_stream; + + g_assert (record != NULL); + g_assert (SYSPROF_IS_RECORDING (record->recording)); + g_assert (DEX_IS_FUTURE (record->cancellable)); + + writer = _sysprof_recording_writer (record->recording); + read_buffer = g_malloc (MAX_EMBEDDABLE_MESSAGE_SIZE); + argv = g_ptr_array_new_null_terminated (0, NULL, TRUE); + + g_ptr_array_add (argv, (char *)"dbus-monitor"); + g_ptr_array_add (argv, (char *)"--profile"); + g_ptr_array_add (argv, (char *)"--binary"); + + if (record->bus_address != NULL) + { + g_ptr_array_add (argv, (char *)"--address"); + g_ptr_array_add (argv, (char *)record->bus_address); + } + else if (record->bus_type == G_BUS_TYPE_SESSION) + { + g_ptr_array_add (argv, (char *)"--session"); + } + else if (record->bus_type == G_BUS_TYPE_SYSTEM) + { + g_ptr_array_add (argv, (char *)"--system"); + } + else + { + _sysprof_recording_diagnostic (record->recording, + "D-Bus Monitor", + "Misconfigured D-Bus monitor, will not record"); + return dex_future_new_for_boolean (TRUE); + } + + launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE|G_SUBPROCESS_FLAGS_STDERR_SILENCE); + subprocess = g_subprocess_launcher_spawnv (launcher, (const char * const *)argv->pdata, &error); + + if (subprocess == NULL) + return dex_future_new_for_error (g_steal_pointer (&error)); + + stdout_stream = g_subprocess_get_stdout_pipe (subprocess); + + for (;;) + { + g_autoptr(DexFuture) header_future = NULL; + guint8 header[16]; + gssize n_needed; + + /* Read enough bytes to decode message length */ + header_future = dex_input_stream_read (stdout_stream, + header, + sizeof header, + G_PRIORITY_DEFAULT); + + /* Wait until we have incoming data or the recording is cancelled */ + if (!dex_await (dex_future_first (dex_ref (record->cancellable), + dex_ref (header_future), + NULL), &error)) + break; + + /* Stop if we reached EOF or errored on reading message header */ + if (dex_await_int64 (dex_ref (header_future), &error) != sizeof header) + break; + + /* If we got a decoding error, just bail */ + if ((n_needed = g_dbus_message_bytes_needed (header, sizeof header, NULL)) < 0) + break; + + g_assert (n_needed >= 16); + + /* If this is a message that will be too large to store, then note that + * and skip the appropriate number of bytes. Our largest frame size is + * 64k - sizeof(SysprofCaptureFrameDBus). + */ + if (n_needed > MAX_EMBEDDABLE_MESSAGE_SIZE) + { + sysprof_capture_writer_add_dbus_message (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + SYSPROF_CAPTURE_DBUS_FLAGS_MESSAGE_TOO_LARGE, + NULL, 0); + + /* Skip the remaining bytes for the message or bail on cancellation */ + if (!dex_await (dex_future_first (dex_ref (record->cancellable), + dex_input_stream_skip (stdout_stream, + n_needed - sizeof header, + G_PRIORITY_DEFAULT), + NULL), &error)) + break; + } + else + { + g_autoptr(DexFuture) body_future = NULL; + + /* Read the rest of the incoming message */ + memcpy (read_buffer, header, sizeof header); + body_future = dex_input_stream_read (stdout_stream, + read_buffer + sizeof header, + n_needed - sizeof header, + G_PRIORITY_DEFAULT); + + /* Wait for body or cancellation of recording */ + if (!dex_await (dex_future_first (dex_ref (record->cancellable), + dex_ref (body_future), + NULL), &error)) + break; + + /* If we didn't read what we expected to, just bail */ + if (dex_await_int64 (dex_ref (body_future), &error) != n_needed - sizeof header) + break; + + sysprof_capture_writer_add_dbus_message (writer, + SYSPROF_CAPTURE_CURRENT_TIME, + -1, + -1, + 0, + read_buffer, n_needed); + } + } + + return dex_future_new_for_boolean (TRUE); +} + +static DexFuture * +sysprof_dbus_monitor_record (SysprofInstrument *instrument, + SysprofRecording *recording, + GCancellable *cancellable) +{ + SysprofDBusMonitor *self = (SysprofDBusMonitor *)instrument; + Record *record; + + g_assert (SYSPROF_IS_DBUS_MONITOR (self)); + g_assert (SYSPROF_IS_RECORDING (recording)); + g_assert (G_IS_CANCELLABLE (cancellable)); + + record = g_new0 (Record, 1); + record->recording = g_object_ref (recording); + record->cancellable = dex_cancellable_new_from_cancellable (cancellable); + record->bus_type = self->bus_type; + record->bus_address = g_strdup (self->bus_address); + + return dex_scheduler_spawn (NULL, 0, + sysprof_dbus_monitor_record_fiber, + record, + record_free); +} + +static void +sysprof_dbus_monitor_finalize (GObject *object) +{ + SysprofDBusMonitor *self = (SysprofDBusMonitor *)object; + + g_clear_pointer (&self->bus_address, g_free); + + G_OBJECT_CLASS (sysprof_dbus_monitor_parent_class)->finalize (object); +} + +static void +sysprof_dbus_monitor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDBusMonitor *self = SYSPROF_DBUS_MONITOR (object); + + switch (prop_id) + { + case PROP_BUS_ADDRESS: + g_value_set_string (value, sysprof_dbus_monitor_get_bus_address (self)); + break; + + case PROP_BUS_TYPE: + g_value_set_enum (value, sysprof_dbus_monitor_get_bus_type (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_dbus_monitor_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofDBusMonitor *self = SYSPROF_DBUS_MONITOR (object); + + switch (prop_id) + { + case PROP_BUS_ADDRESS: + self->bus_address = g_value_dup_string (value); + break; + + case PROP_BUS_TYPE: + self->bus_type = g_value_get_enum (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_dbus_monitor_class_init (SysprofDBusMonitorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass); + + object_class->finalize = sysprof_dbus_monitor_finalize; + object_class->get_property = sysprof_dbus_monitor_get_property; + object_class->set_property = sysprof_dbus_monitor_set_property; + + instrument_class->list_required_policy = sysprof_dbus_monitor_list_required_policy; + instrument_class->record = sysprof_dbus_monitor_record; + + properties[PROP_BUS_ADDRESS] = + g_param_spec_string ("bus-address", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + properties[PROP_BUS_TYPE] = + g_param_spec_enum ("bus-type", NULL, NULL, + G_TYPE_BUS_TYPE, + G_BUS_TYPE_SESSION, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_dbus_monitor_init (SysprofDBusMonitor *self) +{ +} + +SysprofInstrument * +sysprof_dbus_monitor_new (GBusType bus_type) +{ + g_return_val_if_fail (bus_type == G_BUS_TYPE_SESSION || bus_type == G_BUS_TYPE_SYSTEM, + NULL); + + return g_object_new (SYSPROF_TYPE_DBUS_MONITOR, + "bus-type", bus_type, + NULL); +} + +SysprofInstrument * +sysprof_dbus_monitor_new_for_bus_address (const char *bus_address) +{ + g_return_val_if_fail (bus_address != NULL, NULL); + + return g_object_new (SYSPROF_TYPE_DBUS_MONITOR, + "bus-address", bus_address, + NULL); +} + +GBusType +sysprof_dbus_monitor_get_bus_type (SysprofDBusMonitor *self) +{ + g_return_val_if_fail (SYSPROF_IS_DBUS_MONITOR (self), 0); + + return self->bus_type; +} + +const char * +sysprof_dbus_monitor_get_bus_address (SysprofDBusMonitor *self) +{ + g_return_val_if_fail (SYSPROF_IS_DBUS_MONITOR (self), NULL); + + return self->bus_address; +} diff --git a/src/libsysprof/sysprof-dbus-monitor.h b/src/libsysprof/sysprof-dbus-monitor.h new file mode 100644 index 00000000..b1bfef1a --- /dev/null +++ b/src/libsysprof/sysprof-dbus-monitor.h @@ -0,0 +1,48 @@ +/* sysprof-dbus-monitor.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-instrument.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DBUS_MONITOR (sysprof_dbus_monitor_get_type()) +#define SYSPROF_IS_DBUS_MONITOR(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DBUS_MONITOR) +#define SYSPROF_DBUS_MONITOR(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DBUS_MONITOR, SysprofDBusMonitor) +#define SYSPROF_DBUS_MONITOR_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DBUS_MONITOR, SysprofDBusMonitorClass) + +typedef struct _SysprofDBusMonitor SysprofDBusMonitor; +typedef struct _SysprofDBusMonitorClass SysprofDBusMonitorClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_dbus_monitor_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_dbus_monitor_new (GBusType bus_type); +SYSPROF_AVAILABLE_IN_ALL +SysprofInstrument *sysprof_dbus_monitor_new_for_bus_address (const char *bus_address); +SYSPROF_AVAILABLE_IN_ALL +GBusType sysprof_dbus_monitor_get_bus_type (SysprofDBusMonitor *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_dbus_monitor_get_bus_address (SysprofDBusMonitor *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDBusMonitor, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof/sysprof-document-dbus-message.c b/src/libsysprof/sysprof-document-dbus-message.c new file mode 100644 index 00000000..83cbb2c3 --- /dev/null +++ b/src/libsysprof/sysprof-document-dbus-message.c @@ -0,0 +1,253 @@ +/* sysprof-document-dbus-message.c + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-document-frame-private.h" +#include "sysprof-document-dbus-message.h" + +struct _SysprofDocumentDBusMessage +{ + SysprofDocumentFrame parent_instance; + GDBusMessage *message; +}; + +struct _SysprofDocumentDBusMessageClass +{ + SysprofDocumentFrameClass parent_class; +}; + +enum { + PROP_0, + PROP_MESSAGE, + PROP_MESSAGE_LENGTH, + PROP_SERIAL, + PROP_SENDER, + PROP_DESTINATION, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofDocumentDBusMessage, sysprof_document_dbus_message, SYSPROF_TYPE_DOCUMENT_FRAME) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_document_dbus_message_dispose (GObject *object) +{ + SysprofDocumentDBusMessage *self = (SysprofDocumentDBusMessage *)object; + + g_clear_object (&self->message); + + G_OBJECT_CLASS (sysprof_document_dbus_message_parent_class)->dispose (object); +} + +static void +sysprof_document_dbus_message_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentDBusMessage *self = SYSPROF_DOCUMENT_DBUS_MESSAGE (object); + + switch (prop_id) + { + case PROP_MESSAGE: + g_value_take_object (value, sysprof_document_dbus_message_dup_message (self)); + break; + + case PROP_MESSAGE_LENGTH: + g_value_set_uint (value, sysprof_document_dbus_message_get_message_length (self)); + break; + + case PROP_SERIAL: + g_value_set_uint (value, sysprof_document_dbus_message_get_serial (self)); + break; + + case PROP_SENDER: + g_value_set_string (value, sysprof_document_dbus_message_get_sender (self)); + break; + + case PROP_DESTINATION: + g_value_set_string (value, sysprof_document_dbus_message_get_destination (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_dbus_message_class_init (SysprofDocumentDBusMessageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + SysprofDocumentFrameClass *document_frame_class = SYSPROF_DOCUMENT_FRAME_CLASS (klass); + + object_class->dispose = sysprof_document_dbus_message_dispose; + object_class->get_property = sysprof_document_dbus_message_get_property; + + document_frame_class->type_name = N_("D-Bus Message"); + + properties[PROP_MESSAGE_LENGTH] = + g_param_spec_uint ("message-length", NULL, NULL, + 0, G_MAXUINT16, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_MESSAGE] = + g_param_spec_object ("message", NULL, NULL, + G_TYPE_DBUS_MESSAGE, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_SERIAL] = + g_param_spec_uint ("serial", NULL, NULL, + 0, G_MAXUINT, 0, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_SENDER] = + g_param_spec_string ("sender", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_DESTINATION] = + g_param_spec_string ("destination", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_dbus_message_init (SysprofDocumentDBusMessage *self) +{ +} + +guint +sysprof_document_dbus_message_get_message_length (SysprofDocumentDBusMessage *self) +{ + const SysprofCaptureDBusMessage *dbus_message; + guint len; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_DBUS_MESSAGE (self), FALSE); + + dbus_message = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureDBusMessage); + + len = SYSPROF_DOCUMENT_FRAME_UINT16 (self, dbus_message->message_len); + + if (len > SYSPROF_DOCUMENT_FRAME (self)->frame_len - sizeof *dbus_message) + return 0; + + return len; +} + +/** + * sysprof_document_dbus_message_get_message_data: + * @self: a #SysprofDocumentDBusMessage + * @length: (out): the location of the length of the data + * + * Returns: (transfer none) (nullable): the message data or %NULL + * if length is zero. + */ +const guint8 * +sysprof_document_dbus_message_get_message_data (SysprofDocumentDBusMessage *self, + guint *length) +{ + const SysprofCaptureDBusMessage *dbus_message; + guint len; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_DBUS_MESSAGE (self), FALSE); + + dbus_message = SYSPROF_DOCUMENT_FRAME_GET (self, SysprofCaptureDBusMessage); + len = sysprof_document_dbus_message_get_message_length (self); + + if (length != NULL) + *length = len; + + if (len == 0) + return NULL; + + return dbus_message->message; +} + +/** + * sysprof_document_dbus_message_dup_message: + * @self: a #SysprofDocumentDBusMessage + * + * Tries to decode the message. + * + * Returns: (transfer none) (nullable): a #GDBusMessage or %NULL + */ +GDBusMessage * +sysprof_document_dbus_message_dup_message (SysprofDocumentDBusMessage *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_DBUS_MESSAGE (self), NULL); + + if (self->message == NULL) + { + const guint8 *data; + guint len; + + if ((data = sysprof_document_dbus_message_get_message_data (self, &len))) + self->message = g_dbus_message_new_from_blob ((guchar *)data, len, G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING, NULL); + } + + return self->message ? g_object_ref (self->message) : NULL; +} + +guint +sysprof_document_dbus_message_get_serial (SysprofDocumentDBusMessage *self) +{ + g_autoptr(GDBusMessage) message = NULL; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_DBUS_MESSAGE (self), 0); + + if (!(message = sysprof_document_dbus_message_dup_message (self))) + return 0; + + return g_dbus_message_get_serial (message); +} + +const char * +sysprof_document_dbus_message_get_sender (SysprofDocumentDBusMessage *self) +{ + g_autoptr(GDBusMessage) message = NULL; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_DBUS_MESSAGE (self), NULL); + + if (!(message = sysprof_document_dbus_message_dup_message (self))) + return NULL; + + /* Safe because of self->message */ + return g_dbus_message_get_sender (message); +} + +const char * +sysprof_document_dbus_message_get_destination (SysprofDocumentDBusMessage *self) +{ + g_autoptr(GDBusMessage) message = NULL; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_DBUS_MESSAGE (self), NULL); + + if (!(message = sysprof_document_dbus_message_dup_message (self))) + return NULL; + + /* Safe because of self->message */ + return g_dbus_message_get_destination (message); +} diff --git a/src/libsysprof/sysprof-document-dbus-message.h b/src/libsysprof/sysprof-document-dbus-message.h new file mode 100644 index 00000000..7073f735 --- /dev/null +++ b/src/libsysprof/sysprof-document-dbus-message.h @@ -0,0 +1,55 @@ +/* sysprof-document-dbus-message.h + * + * Copyright 2023 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-document-frame.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_DBUS_MESSAGE (sysprof_document_dbus_message_get_type()) +#define SYSPROF_IS_DOCUMENT_DBUS_MESSAGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DOCUMENT_DBUS_MESSAGE) +#define SYSPROF_DOCUMENT_DBUS_MESSAGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DOCUMENT_DBUS_MESSAGE, SysprofDocumentDBusMessage) +#define SYSPROF_DOCUMENT_DBUS_MESSAGE_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DOCUMENT_DBUS_MESSAGE, SysprofDocumentDBusMessageClass) + +typedef struct _SysprofDocumentDBusMessage SysprofDocumentDBusMessage; +typedef struct _SysprofDocumentDBusMessageClass SysprofDocumentDBusMessageClass; + +SYSPROF_AVAILABLE_IN_ALL +GType sysprof_document_dbus_message_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_dbus_message_get_message_length (SysprofDocumentDBusMessage *self); +SYSPROF_AVAILABLE_IN_ALL +const guint8 *sysprof_document_dbus_message_get_message_data (SysprofDocumentDBusMessage *self, + guint *length); +SYSPROF_AVAILABLE_IN_ALL +GDBusMessage *sysprof_document_dbus_message_dup_message (SysprofDocumentDBusMessage *self); +SYSPROF_AVAILABLE_IN_ALL +guint sysprof_document_dbus_message_get_serial (SysprofDocumentDBusMessage *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_dbus_message_get_destination (SysprofDocumentDBusMessage *self); +SYSPROF_AVAILABLE_IN_ALL +const char *sysprof_document_dbus_message_get_sender (SysprofDocumentDBusMessage *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentDBusMessage, g_object_unref) + +G_END_DECLS diff --git a/src/libsysprof/sysprof-document-frame.c b/src/libsysprof/sysprof-document-frame.c index c191a35c..cfaa0a9e 100644 --- a/src/libsysprof/sysprof-document-frame.c +++ b/src/libsysprof/sysprof-document-frame.c @@ -27,6 +27,7 @@ #include "sysprof-document-allocation.h" #include "sysprof-document-ctrdef.h" #include "sysprof-document-ctrset.h" +#include "sysprof-document-dbus-message.h" #include "sysprof-document-exit.h" #include "sysprof-document-file-chunk.h" #include "sysprof-document-fork.h" @@ -258,6 +259,10 @@ _sysprof_document_frame_new (GMappedFile *mapped_file, gtype = SYSPROF_TYPE_DOCUMENT_CTRSET; break; + case SYSPROF_CAPTURE_FRAME_DBUS_MESSAGE: + gtype = SYSPROF_TYPE_DOCUMENT_DBUS_MESSAGE; + break; + default: gtype = SYSPROF_TYPE_DOCUMENT_FRAME; break; diff --git a/src/libsysprof/sysprof-document.c b/src/libsysprof/sysprof-document.c index b376c34a..e8cd7437 100644 --- a/src/libsysprof/sysprof-document.c +++ b/src/libsysprof/sysprof-document.c @@ -878,6 +878,7 @@ is_data_type (SysprofCaptureFrameType type) { case SYSPROF_CAPTURE_FRAME_ALLOCATION: case SYSPROF_CAPTURE_FRAME_CTRSET: + case SYSPROF_CAPTURE_FRAME_DBUS_MESSAGE: case SYSPROF_CAPTURE_FRAME_EXIT: case SYSPROF_CAPTURE_FRAME_FORK: case SYSPROF_CAPTURE_FRAME_LOG: diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h index b284e7e4..88444e11 100644 --- a/src/libsysprof/sysprof.h +++ b/src/libsysprof/sysprof.h @@ -33,9 +33,11 @@ G_BEGIN_DECLS # include "sysprof-category-summary.h" # include "sysprof-cpu-info.h" # include "sysprof-cpu-usage.h" +# include "sysprof-dbus-monitor.h" # include "sysprof-diagnostic.h" # include "sysprof-disk-usage.h" # include "sysprof-document-allocation.h" +# include "sysprof-document-dbus-message.h" # include "sysprof-document-counter-value.h" # include "sysprof-document-counter.h" # include "sysprof-document-ctrdef.h" diff --git a/src/libsysprof/tests/test-capture-model.c b/src/libsysprof/tests/test-capture-model.c index c509c6d8..79d60c6a 100644 --- a/src/libsysprof/tests/test-capture-model.c +++ b/src/libsysprof/tests/test-capture-model.c @@ -84,6 +84,12 @@ main (int argc, else if (SYSPROF_IS_DOCUMENT_JITMAP (frame)) g_print (" n_jitmaps=%u", sysprof_document_jitmap_get_size (SYSPROF_DOCUMENT_JITMAP (frame))); + else if (SYSPROF_IS_DOCUMENT_DBUS_MESSAGE (frame)) + g_print (" message-length=%u serial=0x%x sender=%s destination=%s", + sysprof_document_dbus_message_get_message_length (SYSPROF_DOCUMENT_DBUS_MESSAGE (frame)), + sysprof_document_dbus_message_get_serial (SYSPROF_DOCUMENT_DBUS_MESSAGE (frame)), + sysprof_document_dbus_message_get_sender (SYSPROF_DOCUMENT_DBUS_MESSAGE (frame)), + sysprof_document_dbus_message_get_destination (SYSPROF_DOCUMENT_DBUS_MESSAGE (frame))); else if (SYSPROF_IS_DOCUMENT_CTRDEF (frame)) { guint n_counters = sysprof_document_ctrdef_get_n_counters (SYSPROF_DOCUMENT_CTRDEF (frame)); diff --git a/src/libsysprof/tests/test-profiler.c b/src/libsysprof/tests/test-profiler.c index a1f6eda4..8430254e 100644 --- a/src/libsysprof/tests/test-profiler.c +++ b/src/libsysprof/tests/test-profiler.c @@ -32,6 +32,8 @@ static gboolean memprof; static gboolean tracer; static gboolean gnome_shell; static gboolean bundle_symbols; +static gboolean session_bus; +static gboolean system_bus; static char *power_profile; static const GOptionEntry entries[] = { { "capture", 'c', 0, G_OPTION_ARG_FILENAME, &capture_file, "The file to capture into", "CAPTURE" }, @@ -39,6 +41,8 @@ static const GOptionEntry entries[] = { { "tracer", 't', 0, G_OPTION_ARG_NONE, &tracer, "Enable tracing with __cyg_profile_enter" }, { "gnome-shell", 's', 0, G_OPTION_ARG_NONE, &gnome_shell, "Request GNOME Shell to provide profiler data" }, { "power-profile", 'p', 0, G_OPTION_ARG_STRING, &power_profile, "Use POWER_PROFILE for duration of recording", "power-saver|balanced|performance" }, + { "session-bus", 0, 0, G_OPTION_ARG_NONE, &session_bus, "Record D-Bus messages on the session bus" }, + { "system-bus", 0, 0, G_OPTION_ARG_NONE, &system_bus, "Record D-Bus messages on the system bus" }, { "bundle-symbols", 'b', 0, G_OPTION_ARG_STRING, &bundle_symbols, "Bundle synbols with the capture" }, { 0 } }; @@ -201,6 +205,12 @@ main (int argc, else sysprof_profiler_add_instrument (profiler, sysprof_sampler_new ()); + if (session_bus) + sysprof_profiler_add_instrument (profiler, sysprof_dbus_monitor_new (G_BUS_TYPE_SESSION)); + + if (system_bus) + sysprof_profiler_add_instrument (profiler, sysprof_dbus_monitor_new (G_BUS_TYPE_SYSTEM)); + for (int i = 1; i < argc; i++) { if (strcmp (argv[i], "--") == 0 && i+1 < argc)