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.
This commit is contained in:
Christian Hergert
2024-11-03 10:57:58 -08:00
parent c3cb2f71bc
commit f2b2dcf29d
6 changed files with 316 additions and 1 deletions

View File

@ -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',

View File

@ -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));
}

View File

@ -106,6 +106,41 @@
</child>
</object>
</child>
<child>
<object class="AdwExpanderRow">
<property name="title" translatable="yes">Unwind Stacks in User Space</property>
<property name="subtitle" translatable="yes">Copy stack contents and registers for unwinding in user-space</property>
<property name="expanded" bind-source="sample_user_stack" bind-property="active" bind-flags="sync-create|bidirectional"/>
<child type="action">
<object class="GtkSwitch" id="sample_user_stack">
<property name="halign">end</property>
<property name="valign">center</property>
<property name="active" bind-source="recording_template" bind-flags="bidirectional|sync-create" bind-property="user-stacks"/>
</object>
</child>
<child>
<object class="AdwComboRow" id="sample_user_stack_size">
<property name="title" translatable="yes">Stack Size</property>
<property name="subtitle" translatable="yes">The number of bytes to copy from the stack</property>
<property name="model">stack_sizes</property>
<property name="expression">
<lookup name="label" type="SysprofStackSize"/>
</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Unwinding in user-space has considerable overhead but may help in situations where frame-pointers are unavailable.</property>
<property name="xalign">0</property>
<property name="margin-top">8</property>
<style>
<class name="caption"/>
<class name="dim-label"/>
</style>
</object>
</child>
</object>
</child>
<child>
@ -664,4 +699,36 @@
</object>
<object class="GtkStringList" id="envvars">
</object>
<object class="GListStore" id="stack_sizes">
<child>
<object class="SysprofStackSize">
<property name="label">8 KB</property>
<property name="size">8192</property>
</object>
</child>
<child>
<object class="SysprofStackSize">
<property name="label">16 KB</property>
<property name="size">16384</property>
</object>
</child>
<child>
<object class="SysprofStackSize">
<property name="label">24 KB</property>
<property name="size">24576</property>
</object>
</child>
<child>
<object class="SysprofStackSize">
<property name="label">32 KB</property>
<property name="size">32768</property>
</object>
</child>
<child>
<object class="SysprofStackSize">
<property name="label">64 KB</property>
<property name="size">65536</property>
</object>
</child>
</object>
</interface>

View File

@ -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 ());

View File

@ -0,0 +1,141 @@
/*
* sysprof-stack-size.c
*
* Copyright 2024 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-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;
}

View File

@ -0,0 +1,35 @@
/*
* sysprof-stack-size.h
*
* Copyright 2024 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>
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