From f02e19820e5a3b223f2b563946daa80693c98ca2 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Sun, 3 Nov 2024 10:57:58 -0800 Subject: [PATCH 19/31] sysprof: add UI for live unwinding This adds UI to specify the amount of stack contents to copy along with the CPU registers so that you may unwind in user-space. --- src/sysprof/meson.build | 1 + src/sysprof/sysprof-greeter.c | 32 +++++ src/sysprof/sysprof-greeter.ui | 67 +++++++++++ src/sysprof/sysprof-recording-template.c | 41 ++++++- src/sysprof/sysprof-stack-size.c | 141 +++++++++++++++++++++++ src/sysprof/sysprof-stack-size.h | 35 ++++++ 6 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 src/sysprof/sysprof-stack-size.c create mode 100644 src/sysprof/sysprof-stack-size.h diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index ca42ead3..ab6a701a 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -57,6 +57,7 @@ sysprof_sources = [ 'sysprof-sidebar.c', 'sysprof-single-model.c', 'sysprof-split-layer.c', + 'sysprof-stack-size.c', 'sysprof-storage-section.c', 'sysprof-symbol-label.c', 'sysprof-task-row.c', diff --git a/src/sysprof/sysprof-greeter.c b/src/sysprof/sysprof-greeter.c index 72f4dd5d..52eff370 100644 --- a/src/sysprof/sysprof-greeter.c +++ b/src/sysprof/sysprof-greeter.c @@ -31,6 +31,7 @@ #include "sysprof-power-profiles.h" #include "sysprof-recording-pad.h" #include "sysprof-recording-template.h" +#include "sysprof-stack-size.h" #include "sysprof-window.h" G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofCaptureWriter, sysprof_capture_writer_unref) @@ -56,6 +57,7 @@ struct _SysprofGreeter GtkSwitch *bundle_symbols; GtkButton *record_to_memory; AdwComboRow *power_combo; + AdwComboRow *sample_user_stack_size; SysprofRecordingTemplate *recording_template; }; @@ -455,6 +457,26 @@ translate_power_profile (GtkStringObject *strobj) return g_strdup (str); } +static void +on_stack_size_changed_cb (SysprofGreeter *self, + GParamSpec *pspec, + AdwComboRow *row) +{ + GObject *item; + + g_assert (SYSPROF_IS_GREETER (self)); + g_assert (ADW_IS_COMBO_ROW (row)); + + if ((item = adw_combo_row_get_selected_item (row))) + { + guint stack_size = sysprof_stack_size_get_size (SYSPROF_STACK_SIZE (item)); + + g_object_set (self->recording_template, + "stack-size", stack_size, + NULL); + } +} + static void sysprof_greeter_dispose (GObject *object) { @@ -492,6 +514,7 @@ sysprof_greeter_class_init (SysprofGreeterClass *klass) gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, recording_template); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, sample_javascript_stacks); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, sample_native_stacks); + gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, sample_user_stack_size); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, sidebar_list_box); gtk_widget_class_bind_template_child (widget_class, SysprofGreeter, view_stack); @@ -507,6 +530,7 @@ sysprof_greeter_class_init (SysprofGreeterClass *klass) g_type_ensure (SYSPROF_TYPE_ENTRY_POPOVER); g_type_ensure (SYSPROF_TYPE_RECORDING_TEMPLATE); + g_type_ensure (SYSPROF_TYPE_STACK_SIZE); } static void @@ -540,6 +564,14 @@ sysprof_greeter_init (SysprofGreeter *self) gtk_list_box_select_row (self->sidebar_list_box, row); sidebar_row_activated_cb (self, row, self->sidebar_list_box); + g_signal_connect_object (self->sample_user_stack_size, + "notify::selected-item", + G_CALLBACK (on_stack_size_changed_cb), + self, + G_CONNECT_SWAPPED); + /* Set to 16KB */ + adw_combo_row_set_selected (self->sample_user_stack_size, 1); + gtk_widget_grab_focus (GTK_WIDGET (self->record_to_memory)); } diff --git a/src/sysprof/sysprof-greeter.ui b/src/sysprof/sysprof-greeter.ui index 35e790f5..f7ebbc29 100644 --- a/src/sysprof/sysprof-greeter.ui +++ b/src/sysprof/sysprof-greeter.ui @@ -106,6 +106,41 @@ + + + Unwind Stacks in User Space + Copy stack contents and registers for unwinding in user-space + + + + end + center + + + + + + Stack Size + The number of bytes to copy from the stack + stack_sizes + + + + + + + + + + Unwinding in user-space has considerable overhead but may help in situations where frame-pointers are unavailable. + 0 + 8 + + + @@ -664,4 +699,36 @@ + + + + 8 KB + 8192 + + + + + 16 KB + 16384 + + + + + 24 KB + 24576 + + + + + 32 KB + 32768 + + + + + 64 KB + 65536 + + + diff --git a/src/sysprof/sysprof-recording-template.c b/src/sysprof/sysprof-recording-template.c index 50f6c957..ee4d4f01 100644 --- a/src/sysprof/sysprof-recording-template.c +++ b/src/sysprof/sysprof-recording-template.c @@ -22,6 +22,8 @@ #include "sysprof-recording-template.h" +#define DEFAULT_STACK_SIZE (4096*4) + struct _SysprofRecordingTemplate { GObject parent_instance; @@ -31,6 +33,8 @@ struct _SysprofRecordingTemplate char *power_profile; char **environ; + guint stack_size; + guint battery_charge : 1; guint bundle_symbols : 1; guint clear_environ : 1; @@ -49,6 +53,7 @@ struct _SysprofRecordingTemplate guint session_bus : 1; guint system_bus : 1; guint system_log : 1; + guint user_stacks : 1; }; enum { @@ -73,8 +78,10 @@ enum { PROP_POWER_PROFILE, PROP_SCHEDULER_DETAILS, PROP_SESSION_BUS, + PROP_STACK_SIZE, PROP_SYSTEM_BUS, PROP_SYSTEM_LOG, + PROP_USER_STACKS, N_PROPS }; @@ -185,6 +192,10 @@ sysprof_recording_template_get_property (GObject *object, g_value_set_boolean (value, self->session_bus); break; + case PROP_STACK_SIZE: + g_value_set_uint (value, self->stack_size); + break; + case PROP_SYSTEM_BUS: g_value_set_boolean (value, self->system_bus); break; @@ -193,6 +204,10 @@ sysprof_recording_template_get_property (GObject *object, g_value_set_boolean (value, self->system_log); break; + case PROP_USER_STACKS: + g_value_set_boolean (value, self->user_stacks); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -289,6 +304,10 @@ sysprof_recording_template_set_property (GObject *object, self->session_bus = g_value_get_boolean (value); break; + case PROP_STACK_SIZE: + self->stack_size = g_value_get_uint (value); + break; + case PROP_SYSTEM_BUS: self->system_bus = g_value_get_boolean (value); break; @@ -297,6 +316,10 @@ sysprof_recording_template_set_property (GObject *object, self->system_log = g_value_get_boolean (value); break; + case PROP_USER_STACKS: + self->user_stacks = g_value_get_boolean (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -421,6 +444,16 @@ sysprof_recording_template_class_init (SysprofRecordingTemplateClass *klass) TRUE, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + properties[PROP_USER_STACKS] = + g_param_spec_boolean ("user-stacks", NULL, NULL, + FALSE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_STACK_SIZE] = + g_param_spec_uint ("stack-size", NULL, NULL, + 0, G_MAXUINT, DEFAULT_STACK_SIZE, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_properties (object_class, N_PROPS, properties); } @@ -439,6 +472,7 @@ sysprof_recording_template_init (SysprofRecordingTemplate *self) self->system_log = TRUE; self->command_line = g_strdup (""); self->cwd = g_strdup(""); + self->stack_size = DEFAULT_STACK_SIZE; } SysprofRecordingTemplate * @@ -619,7 +653,12 @@ sysprof_recording_template_apply (SysprofRecordingTemplate *self, sysprof_profiler_add_instrument (profiler, sysprof_memory_usage_new ()); if (self->native_stacks) - sysprof_profiler_add_instrument (profiler, sysprof_sampler_new ()); + { + if (self->user_stacks) + sysprof_profiler_add_instrument (profiler, sysprof_user_sampler_new (self->stack_size)); + else + sysprof_profiler_add_instrument (profiler, sysprof_sampler_new ()); + } if (self->network_usage) sysprof_profiler_add_instrument (profiler, sysprof_network_usage_new ()); diff --git a/src/sysprof/sysprof-stack-size.c b/src/sysprof/sysprof-stack-size.c new file mode 100644 index 00000000..d6652766 --- /dev/null +++ b/src/sysprof/sysprof-stack-size.c @@ -0,0 +1,141 @@ +/* + * sysprof-stack-size.c + * + * Copyright 2024 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-stack-size.h" + +struct _SysprofStackSize +{ + GObject parent_instance; + char *label; + guint size; +}; + +enum { + PROP_0, + PROP_SIZE, + PROP_LABEL, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofStackSize, sysprof_stack_size, G_TYPE_OBJECT) + +static GParamSpec *properties[N_PROPS]; + +static void +sysprof_stack_size_finalize (GObject *object) +{ + SysprofStackSize *self = (SysprofStackSize *)object; + + g_clear_pointer (&self->label, g_free); + + G_OBJECT_CLASS (sysprof_stack_size_parent_class)->finalize (object); +} + +static void +sysprof_stack_size_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofStackSize *self = SYSPROF_STACK_SIZE (object); + + switch (prop_id) + { + case PROP_SIZE: + g_value_set_uint (value, self->size); + break; + + case PROP_LABEL: + g_value_set_string (value, self->label); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_stack_size_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofStackSize *self = SYSPROF_STACK_SIZE (object); + + switch (prop_id) + { + case PROP_SIZE: + self->size = g_value_get_uint (value); + break; + + case PROP_LABEL: + g_set_str (&self->label, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_stack_size_class_init (SysprofStackSizeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_stack_size_finalize; + object_class->get_property = sysprof_stack_size_get_property; + object_class->set_property = sysprof_stack_size_set_property; + + properties[PROP_SIZE] = + g_param_spec_uint ("size", NULL, NULL, + 0, (4096*32), 0, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_LABEL] = + g_param_spec_string ("label", NULL, NULL, + NULL, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_stack_size_init (SysprofStackSize *self) +{ +} + +guint +sysprof_stack_size_get_size (SysprofStackSize *self) +{ + g_return_val_if_fail (SYSPROF_IS_STACK_SIZE (self), 0); + + return self->size; +} + +const char * +sysprof_stack_size_get_label (SysprofStackSize *self) +{ + g_return_val_if_fail (SYSPROF_IS_STACK_SIZE (self), NULL); + + return self->label; +} diff --git a/src/sysprof/sysprof-stack-size.h b/src/sysprof/sysprof-stack-size.h new file mode 100644 index 00000000..5dd7f9c2 --- /dev/null +++ b/src/sysprof/sysprof-stack-size.h @@ -0,0 +1,35 @@ +/* + * sysprof-stack-size.h + * + * Copyright 2024 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_STACK_SIZE (sysprof_stack_size_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofStackSize, sysprof_stack_size, SYSPROF, STACK_SIZE, GObject) + +guint sysprof_stack_size_get_size (SysprofStackSize *self); +const char *sysprof_stack_size_get_label (SysprofStackSize *self); + +G_END_DECLS -- 2.45.2