libsysprof-profile: add SysprofDiagnostic to recordings

This allows instruments to record a diagnostic and have it land as an
object in a GListModel of diagnostics available to the API consumer.

Such items may be used by recording UI to display issues with the recording
to the user.
This commit is contained in:
Christian Hergert
2023-06-13 11:30:45 -07:00
parent 6ab28ff641
commit 1f6cc39554
9 changed files with 356 additions and 14 deletions

View File

@ -1,6 +1,7 @@
libsysprof_profile_public_sources = [
'sysprof-battery-charge.c',
'sysprof-cpu-usage.c',
'sysprof-diagnostic.c',
'sysprof-disk-usage.c',
'sysprof-energy-usage.c',
'sysprof-instrument.c',
@ -28,6 +29,7 @@ libsysprof_profile_public_headers = [
'sysprof-battery-charge.h',
'sysprof-cpu-usage.h',
'sysprof-diagnostic.h',
'sysprof-disk-usage.h',
'sysprof-energy-usage.h',
'sysprof-instrument.h',

View File

@ -0,0 +1,31 @@
/* sysprof-diagnostic-private.h
*
* Copyright 2023 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 "sysprof-diagnostic.h"
G_BEGIN_DECLS
SysprofDiagnostic *_sysprof_diagnostic_new (char *domain,
char *message,
gboolean fatal);
G_END_DECLS

View File

@ -0,0 +1,151 @@
/* sysprof-diagnostic.c
*
* Copyright 2023 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
*/
#include "config.h"
#include "sysprof-diagnostic-private.h"
struct _SysprofDiagnostic
{
GObject parent_instance;
GRefString *domain;
char *message;
guint fatal : 1;
};
enum {
PROP_0,
PROP_DOMAIN,
PROP_MESSAGE,
PROP_FATAL,
N_PROPS
};
G_DEFINE_FINAL_TYPE (SysprofDiagnostic, sysprof_diagnostic, G_TYPE_OBJECT)
static GParamSpec *properties [N_PROPS];
static void
sysprof_diagnostic_finalize (GObject *object)
{
SysprofDiagnostic *self = (SysprofDiagnostic *)object;
g_clear_pointer (&self->domain, g_free);
g_clear_pointer (&self->message, g_free);
G_OBJECT_CLASS (sysprof_diagnostic_parent_class)->finalize (object);
}
static void
sysprof_diagnostic_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
SysprofDiagnostic *self = SYSPROF_DIAGNOSTIC (object);
switch (prop_id)
{
case PROP_DOMAIN:
g_value_set_string (value, self->domain);
break;
case PROP_MESSAGE:
g_value_set_string (value, self->message);
break;
case PROP_FATAL:
g_value_set_boolean (value, self->fatal);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
sysprof_diagnostic_class_init (SysprofDiagnosticClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = sysprof_diagnostic_finalize;
object_class->get_property = sysprof_diagnostic_get_property;
properties [PROP_DOMAIN] =
g_param_spec_string ("domain", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_MESSAGE] =
g_param_spec_string ("message", NULL, NULL,
NULL,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
properties [PROP_FATAL] =
g_param_spec_boolean ("fatal", NULL, NULL,
FALSE,
(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
g_object_class_install_properties (object_class, N_PROPS, properties);
}
static void
sysprof_diagnostic_init (SysprofDiagnostic *self)
{
}
const char *
sysprof_diagnostic_get_domain (SysprofDiagnostic *self)
{
g_return_val_if_fail (SYSPROF_IS_DIAGNOSTIC (self), NULL);
return self->domain;
}
const char *
sysprof_diagnostic_get_message (SysprofDiagnostic *self)
{
g_return_val_if_fail (SYSPROF_IS_DIAGNOSTIC (self), NULL);
return self->message;
}
gboolean
sysprof_diagnostic_get_fatal (SysprofDiagnostic *self)
{
g_return_val_if_fail (SYSPROF_IS_DIAGNOSTIC (self), FALSE);
return self->fatal;
}
SysprofDiagnostic *
_sysprof_diagnostic_new (char *domain,
char *message,
gboolean fatal)
{
SysprofDiagnostic *self;
self = g_object_new (SYSPROF_TYPE_DIAGNOSTIC, NULL);
self->message = message;
self->domain = domain;
self->fatal = !!fatal;
return self;
}

View File

@ -0,0 +1,41 @@
/* sysprof-diagnostic.h
*
* Copyright 2023 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 <glib-object.h>
#include <sysprof-capture.h>
G_BEGIN_DECLS
#define SYSPROF_TYPE_DIAGNOSTIC (sysprof_diagnostic_get_type())
SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofDiagnostic, sysprof_diagnostic, SYSPROF, DIAGNOSTIC, GObject)
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_diagnostic_get_domain (SysprofDiagnostic *self);
SYSPROF_AVAILABLE_IN_ALL
const char *sysprof_diagnostic_get_message (SysprofDiagnostic *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_diagnostic_get_fatal (SysprofDiagnostic *self);
G_END_DECLS

View File

@ -27,6 +27,7 @@ G_BEGIN_DECLS
#define SYSPROF_PROFILE_INSIDE
# include "sysprof-battery-charge.h"
# include "sysprof-cpu-usage.h"
# include "sysprof-diagnostic.h"
# include "sysprof-disk-usage.h"
# include "sysprof-energy-usage.h"
# include "sysprof-instrument.h"

View File

@ -42,5 +42,13 @@ void _sysprof_recording_add_file_data (SysprofRecording *s
const char *path,
const char *contents,
gssize length);
void _sysprof_recording_diagnostic (SysprofRecording *self,
const char *domain,
const char *format,
...) G_GNUC_PRINTF (3, 4);
void _sysprof_recording_error (SysprofRecording *self,
const char *domain,
const char *format,
...) G_GNUC_PRINTF (3, 4);
G_END_DECLS

View File

@ -22,6 +22,7 @@
#include <libdex.h>
#include "sysprof-diagnostic-private.h"
#include "sysprof-instrument-private.h"
#include "sysprof-polkit-private.h"
#include "sysprof-recording-private.h"
@ -35,6 +36,13 @@ struct _SysprofRecording
{
GObject parent_instance;
/* Diagnostics that may be added by instruments during the recording.
* Some may be fatal, meaning that they stop the recording when the
* diagnostic is submitted. That can happen in situations like
* miss-configuration or failed authorization.
*/
GListStore *diagnostics;
/* If we are spawning a process as part of this recording, this
* is the SysprofSpawnable used to spawn the process.
*/
@ -197,6 +205,7 @@ sysprof_recording_finalize (GObject *object)
g_clear_pointer (&self->writer, sysprof_capture_writer_unref);
g_clear_pointer (&self->instruments, g_ptr_array_unref);
g_clear_object (&self->spawnable);
g_clear_object (&self->diagnostics);
dex_clear (&self->fiber);
G_OBJECT_CLASS (sysprof_recording_parent_class)->finalize (object);
@ -215,6 +224,7 @@ sysprof_recording_init (SysprofRecording *self)
{
self->channel = dex_channel_new (0);
self->instruments = g_ptr_array_new_with_free_func (g_object_unref);
self->diagnostics = g_list_store_new (SYSPROF_TYPE_DIAGNOSTIC);
}
SysprofRecording *
@ -510,3 +520,70 @@ _sysprof_recording_add_file_data (SysprofRecording *self,
contents += to_write;
}
}
static void
_sysprof_recording_message_internal (SysprofRecording *self,
const char *domain,
const char *format,
va_list *args,
gboolean fatal)
{
g_autoptr(SysprofDiagnostic) diagnostic = NULL;
g_assert (SYSPROF_IS_RECORDING (self));
g_assert (domain != NULL);
g_assert (format != NULL);
g_assert (args != NULL);
diagnostic = _sysprof_diagnostic_new (g_strdup (domain),
g_strdup_vprintf (format, *args),
fatal);
g_list_store_append (self->diagnostics, diagnostic);
if (fatal)
sysprof_recording_stop_async (self, NULL, NULL, NULL);
}
void
_sysprof_recording_diagnostic (SysprofRecording *self,
const char *domain,
const char *format,
...)
{
va_list args;
va_start (args, format);
_sysprof_recording_message_internal (self, domain, format, &args, FALSE);
va_end (args);
}
void
_sysprof_recording_error (SysprofRecording *self,
const char *domain,
const char *format,
...)
{
va_list args;
va_start (args, format);
_sysprof_recording_message_internal (self, domain, format, &args, TRUE);
va_end (args);
}
/**
* sysprof_recording_list_diagnostics:
* @self: a #SysprofRecording
*
* Gets the diagnostics for the recording which may be updated as
* instruments discover issues with the recording or configuration.
*
* Returns: (transfer full): a #GListModel of #SysprofDiagnostic
*/
GListModel *
sysprof_recording_list_diagnostics (SysprofRecording *self)
{
g_return_val_if_fail (SYSPROF_IS_RECORDING (self), NULL);
return g_object_ref (G_LIST_MODEL (self->diagnostics));
}

View File

@ -32,22 +32,24 @@ SYSPROF_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (SysprofRecording, sysprof_recording, SYSPROF, RECORDING, GObject)
SYSPROF_AVAILABLE_IN_ALL
void sysprof_recording_wait_async (SysprofRecording *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GListModel *sysprof_recording_list_diagnostics (SysprofRecording *self);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_recording_wait_finish (SysprofRecording *self,
GAsyncResult *result,
GError **error);
void sysprof_recording_wait_async (SysprofRecording *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
void sysprof_recording_stop_async (SysprofRecording *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean sysprof_recording_wait_finish (SysprofRecording *self,
GAsyncResult *result,
GError **error);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_recording_stop_finish (SysprofRecording *self,
GAsyncResult *result,
GError **error);
void sysprof_recording_stop_async (SysprofRecording *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
SYSPROF_AVAILABLE_IN_ALL
gboolean sysprof_recording_stop_finish (SysprofRecording *self,
GAsyncResult *result,
GError **error);
G_END_DECLS

View File

@ -51,6 +51,23 @@ wait_cb (GObject *object,
g_main_loop_quit (main_loop);
}
static void
diagnostics_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
gpointer user_data)
{
for (guint i = 0; i < added; i++)
{
g_autoptr(SysprofDiagnostic) diagnostic = g_list_model_get_item (model, position+i);
g_printerr ("%s: %s\n",
sysprof_diagnostic_get_domain (diagnostic),
sysprof_diagnostic_get_message (diagnostic));
}
}
static void
record_cb (GObject *object,
GAsyncResult *result,
@ -58,11 +75,23 @@ record_cb (GObject *object,
{
g_autoptr(GError) error = NULL;
g_autoptr(SysprofRecording) recording = sysprof_profiler_record_finish (SYSPROF_PROFILER (object), result, &error);
g_autoptr(GListModel) diagnostics = NULL;
g_assert_no_error (error);
g_assert_nonnull (recording);
g_assert_true (SYSPROF_IS_RECORDING (recording));
diagnostics = sysprof_recording_list_diagnostics (recording);
g_signal_connect (diagnostics,
"items-changed",
G_CALLBACK (diagnostics_items_changed_cb),
NULL);
diagnostics_items_changed_cb (diagnostics,
0,
0,
g_list_model_get_n_items (diagnostics),
NULL);
sysprof_recording_wait_async (recording, NULL, wait_cb, NULL);
active_recording = recording;