From 8bf8b4455f27a7487e771e3a45e93c6646ba69ee Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sun, 6 Aug 2023 00:44:41 -0700 Subject: [PATCH] sysprof: add SysprofRecordingTemplate The goal here is to have a backing object that represents what we want to record so it becomes easier later on to save/restore state. --- src/sysprof/meson.build | 1 + src/sysprof/sysprof-recording-template.c | 539 +++++++++++++++++++++++ src/sysprof/sysprof-recording-template.h | 41 ++ 3 files changed, 581 insertions(+) create mode 100644 src/sysprof/sysprof-recording-template.c create mode 100644 src/sysprof/sysprof-recording-template.h diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index 653a8466..7308cee2 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -41,6 +41,7 @@ sysprof_sources = [ 'sysprof-processes-section.c', 'sysprof-progress-cell.c', 'sysprof-recording-pad.c', + 'sysprof-recording-template.c', 'sysprof-samples-section.c', 'sysprof-scheduler.c', 'sysprof-section.c', diff --git a/src/sysprof/sysprof-recording-template.c b/src/sysprof/sysprof-recording-template.c new file mode 100644 index 00000000..20971099 --- /dev/null +++ b/src/sysprof/sysprof-recording-template.c @@ -0,0 +1,539 @@ +/* sysprof-recording-template.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 "sysprof-recording-template.h" + +struct _SysprofRecordingTemplate +{ + GObject parent_instance; + + char *command_line; + char *cwd; + char *power_profile; + char **environ; + + guint battery_charge : 1; + guint bundle_symbols : 1; + guint clear_environ : 1; + guint cpu_usage : 1; + guint disk_usage : 1; + guint energy_usage : 1; + guint frame_timings : 1; + guint javascript_stacks : 1; + guint memory_allocations : 1; + guint memory_usage : 1; + guint native_stacks : 1; + guint network_usage : 1; + guint session_bus : 1; + guint system_bus : 1; + guint system_log : 1; +}; + +enum { + PROP_0, + PROP_BATTERY_CHARGE, + PROP_BUNDLE_SYMBOLS, + PROP_CLEAR_ENVIRON, + PROP_COMMAND_LINE, + PROP_CPU_USAGE, + PROP_CWD, + PROP_DISK_USAGE, + PROP_ENERGY_USAGE, + PROP_ENVIRON, + PROP_FRAME_TIMINGS, + PROP_JAVASCRIPT_STACKS, + PROP_MEMORY_ALLOCATIONS, + PROP_MEMORY_USAGE, + PROP_NATIVE_STACKS, + PROP_NETWORK_USAGE, + PROP_POWER_PROFILE, + PROP_SESSION_BUS, + PROP_SYSTEM_BUS, + PROP_SYSTEM_LOG, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofRecordingTemplate, sysprof_recording_template, G_TYPE_OBJECT) + +static GParamSpec *properties [N_PROPS]; + +static void +sysprof_recording_template_finalize (GObject *object) +{ + SysprofRecordingTemplate *self = (SysprofRecordingTemplate *)object; + + g_clear_pointer (&self->command_line, g_free); + g_clear_pointer (&self->cwd, g_free); + g_clear_pointer (&self->power_profile, g_free); + g_clear_pointer (&self->environ, g_free); + + G_OBJECT_CLASS (sysprof_recording_template_parent_class)->finalize (object); +} + +static void +sysprof_recording_template_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofRecordingTemplate *self = SYSPROF_RECORDING_TEMPLATE (object); + + switch (prop_id) + { + case PROP_BATTERY_CHARGE: + g_value_set_boolean (value, self->battery_charge); + break; + + case PROP_BUNDLE_SYMBOLS: + g_value_set_boolean (value, self->bundle_symbols); + break; + + case PROP_CLEAR_ENVIRON: + g_value_set_boolean (value, self->clear_environ); + break; + + case PROP_COMMAND_LINE: + g_value_set_string (value, self->command_line); + break; + + case PROP_CPU_USAGE: + g_value_set_boolean (value, self->cpu_usage); + break; + + case PROP_CWD: + g_value_set_string (value, self->cwd); + break; + + case PROP_DISK_USAGE: + g_value_set_boolean (value, self->disk_usage); + break; + + case PROP_ENERGY_USAGE: + g_value_set_boolean (value, self->energy_usage); + break; + + case PROP_ENVIRON: + g_value_set_boxed (value, self->environ); + break; + + case PROP_FRAME_TIMINGS: + g_value_set_boolean (value, self->frame_timings); + break; + + case PROP_JAVASCRIPT_STACKS: + g_value_set_boolean (value, self->javascript_stacks); + break; + + case PROP_MEMORY_ALLOCATIONS: + g_value_set_boolean (value, self->memory_allocations); + break; + + case PROP_NATIVE_STACKS: + g_value_set_boolean (value, self->native_stacks); + break; + + case PROP_NETWORK_USAGE: + g_value_set_boolean (value, self->network_usage); + break; + + case PROP_POWER_PROFILE: + g_value_set_string (value, self->power_profile); + break; + + case PROP_SESSION_BUS: + g_value_set_boolean (value, self->session_bus); + break; + + case PROP_SYSTEM_BUS: + g_value_set_boolean (value, self->system_bus); + break; + + case PROP_SYSTEM_LOG: + g_value_set_boolean (value, self->system_log); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_recording_template_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofRecordingTemplate *self = SYSPROF_RECORDING_TEMPLATE (object); + + switch (prop_id) + { + case PROP_BATTERY_CHARGE: + self->battery_charge = g_value_get_boolean (value); + break; + + case PROP_BUNDLE_SYMBOLS: + self->bundle_symbols = g_value_get_boolean (value); + break; + + case PROP_CLEAR_ENVIRON: + self->clear_environ = g_value_get_boolean (value); + break; + + case PROP_COMMAND_LINE: + g_set_str (&self->command_line, g_value_get_string (value)); + break; + + case PROP_CPU_USAGE: + self->cpu_usage = g_value_get_boolean (value); + break; + + case PROP_CWD: + g_set_str (&self->cwd, g_value_get_string (value)); + break; + + case PROP_DISK_USAGE: + self->disk_usage = g_value_get_boolean (value); + break; + + case PROP_ENERGY_USAGE: + self->energy_usage = g_value_get_boolean (value); + break; + + case PROP_ENVIRON: + g_clear_pointer (&self->environ, g_strfreev); + self->environ = g_value_dup_boxed (value); + break; + + case PROP_FRAME_TIMINGS: + self->frame_timings = g_value_get_boolean (value); + break; + + case PROP_JAVASCRIPT_STACKS: + self->javascript_stacks = g_value_get_boolean (value); + break; + + case PROP_MEMORY_ALLOCATIONS: + self->memory_allocations = g_value_get_boolean (value); + break; + + case PROP_NATIVE_STACKS: + self->native_stacks = g_value_get_boolean (value); + break; + + case PROP_NETWORK_USAGE: + self->network_usage = g_value_get_boolean (value); + break; + + case PROP_POWER_PROFILE: + g_set_str (&self->power_profile, g_value_get_string (value)); + break; + + case PROP_SESSION_BUS: + self->session_bus = g_value_get_boolean (value); + break; + + case PROP_SYSTEM_BUS: + self->system_bus = g_value_get_boolean (value); + break; + + case PROP_SYSTEM_LOG: + self->system_log = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_recording_template_class_init (SysprofRecordingTemplateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_recording_template_finalize; + object_class->get_property = sysprof_recording_template_get_property; + object_class->set_property = sysprof_recording_template_set_property; + + properties[PROP_BATTERY_CHARGE] = + g_param_spec_boolean ("battery-charge", NULL, NULL, + FALSE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_BUNDLE_SYMBOLS] = + g_param_spec_boolean ("bundle-symbols", NULL, NULL, + TRUE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_CLEAR_ENVIRON] = + g_param_spec_boolean ("clear-environ", NULL, NULL, + FALSE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_COMMAND_LINE] = + g_param_spec_string ("command-line", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_CPU_USAGE] = + g_param_spec_boolean ("cpu-usage", NULL, NULL, + TRUE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_CWD] = + g_param_spec_string ("cwd", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_DISK_USAGE] = + g_param_spec_boolean ("disk-usage", NULL, NULL, + TRUE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_ENERGY_USAGE] = + g_param_spec_boolean ("energy-usage", NULL, NULL, + FALSE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_ENVIRON] = + g_param_spec_boxed ("environ", NULL, NULL, + G_TYPE_STRV, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_FRAME_TIMINGS] = + g_param_spec_boolean ("frame-timings", NULL, NULL, + TRUE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_JAVASCRIPT_STACKS] = + g_param_spec_boolean ("javascript-stacks", NULL, NULL, + FALSE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_MEMORY_ALLOCATIONS] = + g_param_spec_boolean ("memory-allocations", NULL, NULL, + FALSE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_MEMORY_USAGE] = + g_param_spec_boolean ("memory-usage", NULL, NULL, + TRUE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_NATIVE_STACKS] = + g_param_spec_boolean ("native-stacks", NULL, NULL, + TRUE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_NETWORK_USAGE] = + g_param_spec_boolean ("network-usage", NULL, NULL, + TRUE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_POWER_PROFILE] = + g_param_spec_string ("power-profile", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_SESSION_BUS] = + g_param_spec_boolean ("session-bus", NULL, NULL, + FALSE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_SYSTEM_BUS] = + g_param_spec_boolean ("system-bus", NULL, NULL, + FALSE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_SYSTEM_LOG] = + g_param_spec_boolean ("system-log", NULL, NULL, + TRUE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_recording_template_init (SysprofRecordingTemplate *self) +{ + self->bundle_symbols = TRUE; + self->cpu_usage = TRUE; + self->disk_usage = TRUE; + self->frame_timings = TRUE; + self->memory_usage = TRUE; + self->native_stacks = TRUE; + self->network_usage = TRUE; + self->system_log = TRUE; +} + +SysprofRecordingTemplate * +sysprof_recording_template_new (void) +{ + return g_object_new (SYSPROF_TYPE_RECORDING_TEMPLATE, NULL); +} + +static gboolean +environ_parse (const char *pair, + char **key, + char **value) +{ + const gchar *eq; + + g_return_val_if_fail (pair != NULL, FALSE); + + if (key != NULL) + *key = NULL; + + if (value != NULL) + *value = NULL; + + if ((eq = strchr (pair, '='))) + { + if (key != NULL) + *key = g_strndup (pair, eq - pair); + + if (value != NULL) + *value = g_strdup (eq + 1); + + return TRUE; + } + + return FALSE; +} + +static void +add_trace_fd (SysprofProfiler *profiler, + SysprofSpawnable *spawnable, + const char *name) +{ + int trace_fd; + + g_assert (SYSPROF_IS_PROFILER (profiler)); + g_assert (!spawnable || SYSPROF_IS_SPAWNABLE (spawnable)); + + if (spawnable == NULL) + return; + + trace_fd = sysprof_spawnable_add_trace_fd (spawnable, name); + sysprof_profiler_add_instrument (profiler, sysprof_tracefd_consumer_new (trace_fd)); +} + +SysprofProfiler * +sysprof_recording_template_apply (SysprofRecordingTemplate *self, + GError **error) +{ + g_autoptr(SysprofProfiler) profiler = NULL; + + g_return_val_if_fail (SYSPROF_IS_RECORDING_TEMPLATE (self), NULL); + + profiler = sysprof_profiler_new (); + + if (self->command_line && self->command_line[0]) + { + g_autofree char *stripped = g_strstrip (g_strdup (self->command_line)); + g_autoptr(SysprofSpawnable) spawnable = NULL; + g_autoptr(GError) local_error = NULL; + g_auto(GStrv) argv = NULL; + g_auto(GStrv) env = NULL; + int argc; + + if (!g_shell_parse_argv (stripped, &argc, &argv, &local_error)) + { + g_set_error_literal (error, + SYSPROF_RECORDING_TEMPLATE_ERROR, + SYSPROF_RECORDING_TEMPLATE_ERROR_COMMAND_LINE, + local_error->message); + return FALSE; + } + + spawnable = sysprof_spawnable_new (); + sysprof_spawnable_append_args (spawnable, (const char * const *)argv); + + if (self->cwd && self->cwd[0]) + sysprof_spawnable_set_cwd (spawnable, self->cwd); + + if (!self->clear_environ) + env = g_get_environ (); + + if (self->environ) + { + for (guint i = 0; self->environ[i]; i++) + { + g_autofree char *key = NULL; + g_autofree char *value = NULL; + + if (environ_parse (self->environ[i], &key, &value)) + env = g_environ_setenv (env, key, value, TRUE); + } + } + + if (self->memory_allocations) + sysprof_spawnable_add_ld_preload (spawnable, PACKAGE_LIBDIR"/libsysprof-memory-"API_VERSION_S".so"); + + sysprof_profiler_set_spawnable (profiler, spawnable); + + if (self->javascript_stacks) + add_trace_fd (profiler, spawnable, "GJS_TRACE_FD"); + } + + if (self->power_profile && self->power_profile[0]) + sysprof_profiler_add_instrument (profiler, sysprof_power_profile_new (self->power_profile)); + + if (self->battery_charge) + sysprof_profiler_add_instrument (profiler, sysprof_battery_charge_new ()); + + if (self->bundle_symbols) + sysprof_profiler_add_instrument (profiler, sysprof_symbols_bundle_new ()); + + if (self->disk_usage) + sysprof_profiler_add_instrument (profiler, sysprof_disk_usage_new ()); + + if (self->energy_usage) + sysprof_profiler_add_instrument (profiler, sysprof_energy_usage_new ()); + + if (self->frame_timings) + sysprof_profiler_add_instrument (profiler, + sysprof_proxied_instrument_new (G_BUS_TYPE_SESSION, + "org.gnome.Shell", + "/org/gnome/Sysprof3/Profiler")); + + if (self->memory_usage) + sysprof_profiler_add_instrument (profiler, sysprof_memory_usage_new ()); + + if (self->native_stacks) + sysprof_profiler_add_instrument (profiler, sysprof_sampler_new ()); + + if (self->network_usage) + sysprof_profiler_add_instrument (profiler, sysprof_network_usage_new ()); + + if (self->session_bus) + sysprof_profiler_add_instrument (profiler, sysprof_dbus_monitor_new (G_BUS_TYPE_SESSION)); + + if (self->system_bus) + sysprof_profiler_add_instrument (profiler, sysprof_dbus_monitor_new (G_BUS_TYPE_SYSTEM)); + + if (self->system_log) + sysprof_profiler_add_instrument (profiler, sysprof_system_logs_new ()); + + return g_steal_pointer (&profiler); +} + +G_DEFINE_QUARK (SysprofRecordingTemplateError, sysprof_recording_template_error) diff --git a/src/sysprof/sysprof-recording-template.h b/src/sysprof/sysprof-recording-template.h new file mode 100644 index 00000000..7bd807bb --- /dev/null +++ b/src/sysprof/sysprof-recording-template.h @@ -0,0 +1,41 @@ +/* sysprof-recording-template.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 + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_RECORDING_TEMPLATE (sysprof_recording_template_get_type()) +#define SYSPROF_RECORDING_TEMPLATE_ERROR (sysprof_recording_template_error_quark()) + +typedef enum +{ + SYSPROF_RECORDING_TEMPLATE_ERROR_COMMAND_LINE = 1, +} SysprofRecordingTemplateError; + +G_DECLARE_FINAL_TYPE (SysprofRecordingTemplate, sysprof_recording_template, SYSPROF, RECORDING_TEMPLATE, GObject) + +GQuark sysprof_recording_template_error_quark (void) G_GNUC_CONST; +SysprofProfiler *sysprof_recording_template_apply (SysprofRecordingTemplate *self, + GError **error); + +G_END_DECLS