Backport debuginfod and sysprof-live-unwinder

Resolves: RHEL-65743
This commit is contained in:
Christian Hergert 2024-11-06 12:39:21 -08:00
parent e6dbcc2e6a
commit 3869a62c8e
22 changed files with 7457 additions and 1 deletions

View File

@ -0,0 +1,53 @@
From 1804382418ba2b98d97ca47cbdc2a79cca7061e6 Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Wed, 9 Oct 2024 16:39:13 -0700
Subject: [PATCH 01/18] sysprof: update to AdwSpinner
---
src/sysprof/meson.build | 2 +-
src/sysprof/sysprof-window.ui | 9 +--------
2 files changed, 2 insertions(+), 9 deletions(-)
diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build
index ccd698ea..989bbd11 100644
--- a/src/sysprof/meson.build
+++ b/src/sysprof/meson.build
@@ -85,7 +85,7 @@ sysprof_resources = gnome.compile_resources('sysprof-resources', 'sysprof.gresou
sysprof_deps = [
cc.find_library('m', required: false),
dependency('gtk4', version: gtk_req_version),
- dependency('libadwaita-1', version: '>= 1.6.alpha'),
+ dependency('libadwaita-1', version: '>= 1.6.0'),
dependency('libpanel-1', version: '>= 1.7.0'),
libsysprof_static_dep,
diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui
index b33cd613..1b361e37 100644
--- a/src/sysprof/sysprof-window.ui
+++ b/src/sysprof/sysprof-window.ui
@@ -109,7 +109,7 @@
<property name="title-widget">
<object class="GtkCenterBox">
<child type="start">
- <object class="GtkSpinner">
+ <object class="AdwSpinner">
<binding name="visible">
<lookup name="busy" type="SysprofDocument">
<lookup name="document" type="SysprofSession">
@@ -117,13 +117,6 @@
</lookup>
</lookup>
</binding>
- <binding name="spinning">
- <lookup name="busy" type="SysprofDocument">
- <lookup name="document" type="SysprofSession">
- <lookup name="session">SysprofWindow</lookup>
- </lookup>
- </lookup>
- </binding>
</object>
</child>
<child type="center">
--
2.45.2

View File

@ -0,0 +1,28 @@
From 56dfb1d6422cb0b687751b79528e56b6f9e3dfe6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Barnab=C3=A1s=20P=C5=91cze?= <pobrn@protonmail.com>
Date: Wed, 9 Oct 2024 15:29:59 +0200
Subject: [PATCH 02/18] libsysprof: elf: do not allow setting self as debug
link
That will cause infinite recursion in `sysprof_elf_get_symbol_at_address_internal()`.
Also note that loops are still possible, this change
only prevents one way of creating loops.
---
src/libsysprof/sysprof-elf.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/libsysprof/sysprof-elf.c b/src/libsysprof/sysprof-elf.c
index f4e5f914..0534eccb 100644
--- a/src/libsysprof/sysprof-elf.c
+++ b/src/libsysprof/sysprof-elf.c
@@ -502,6 +502,7 @@ sysprof_elf_set_debug_link_elf (SysprofElf *self,
{
g_return_if_fail (SYSPROF_IS_ELF (self));
g_return_if_fail (!debug_link_elf || SYSPROF_IS_ELF (debug_link_elf));
+ g_return_if_fail (debug_link_elf != self);
if (g_set_object (&self->debug_link_elf, debug_link_elf))
g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEBUG_LINK_ELF]);
--
2.45.2

View File

@ -0,0 +1,140 @@
From 5792f34efabc3d2c2a5292d3b30a597d5c53374b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Barnab=C3=A1s=20P=C5=91cze?= <pobrn@protonmail.com>
Date: Wed, 9 Oct 2024 15:28:14 +0200
Subject: [PATCH 03/18] libsysprof: elf: do not generate fallback names
Fallback names are only used in `SysprofElfSymbolizer` at the
moment, but it also has code to generate fallback symbols.
---
src/libsysprof/sysprof-elf-private.h | 3 +--
src/libsysprof/sysprof-elf-symbolizer.c | 12 ++++++------
src/libsysprof/sysprof-elf.c | 17 +++++------------
3 files changed, 12 insertions(+), 20 deletions(-)
diff --git a/src/libsysprof/sysprof-elf-private.h b/src/libsysprof/sysprof-elf-private.h
index b4d0f737..146bc2de 100644
--- a/src/libsysprof/sysprof-elf-private.h
+++ b/src/libsysprof/sysprof-elf-private.h
@@ -42,8 +42,7 @@ const char *sysprof_elf_get_debug_link (SysprofElf *self);
char *sysprof_elf_get_symbol_at_address (SysprofElf *self,
guint64 address,
guint64 *begin_address,
- guint64 *end_address,
- gboolean *is_fallback);
+ guint64 *end_address);
SysprofElf *sysprof_elf_get_debug_link_elf (SysprofElf *self);
void sysprof_elf_set_debug_link_elf (SysprofElf *self,
SysprofElf *debug_link_elf);
diff --git a/src/libsysprof/sysprof-elf-symbolizer.c b/src/libsysprof/sysprof-elf-symbolizer.c
index 8fdf204f..f7aabef7 100644
--- a/src/libsysprof/sysprof-elf-symbolizer.c
+++ b/src/libsysprof/sysprof-elf-symbolizer.c
@@ -61,9 +61,9 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
g_autoptr(SysprofElf) elf = NULL;
SysprofDocumentMmap *map;
g_autofree char *name = NULL;
+ const char *nick = NULL;
const char *path;
const char *build_id;
- gboolean is_fallback = FALSE;
guint64 map_begin;
guint64 map_end;
guint64 relative_address;
@@ -115,14 +115,15 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
NULL)))
goto fallback;
+ nick = sysprof_elf_get_nick (elf);
+
/* Try to get the symbol name at the address and the begin/end address
* so that it can be inserted into our symbol cache.
*/
if (!(name = sysprof_elf_get_symbol_at_address (elf,
relative_address,
&begin_address,
- &end_address,
- &is_fallback)))
+ &end_address)))
goto fallback;
/* Sanitize address ranges if we have to. Sometimes that can happen
@@ -135,11 +136,10 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
ret = _sysprof_symbol_new (sysprof_strings_get (strings, name),
sysprof_strings_get (strings, path),
- sysprof_strings_get (strings, sysprof_elf_get_nick (elf)),
+ sysprof_strings_get (strings, nick),
map_begin + (begin_address - file_offset),
map_begin + (end_address - file_offset),
SYSPROF_SYMBOL_KIND_USER);
- ret->is_fallback = is_fallback;
return ret;
@@ -156,7 +156,7 @@ fallback:
ret = _sysprof_symbol_new (sysprof_strings_get (strings, name),
sysprof_strings_get (strings, path),
- NULL,
+ sysprof_strings_get (strings, nick),
begin_address, end_address,
SYSPROF_SYMBOL_KIND_USER);
ret->is_fallback = TRUE;
diff --git a/src/libsysprof/sysprof-elf.c b/src/libsysprof/sysprof-elf.c
index 0534eccb..df73ce6d 100644
--- a/src/libsysprof/sysprof-elf.c
+++ b/src/libsysprof/sysprof-elf.c
@@ -409,8 +409,7 @@ sysprof_elf_get_symbol_at_address_internal (SysprofElf *self,
guint64 address,
guint64 *begin_address,
guint64 *end_address,
- guint64 text_offset,
- gboolean *is_fallback)
+ guint64 text_offset)
{
const ElfSym *symbol;
char *ret = NULL;
@@ -421,7 +420,7 @@ sysprof_elf_get_symbol_at_address_internal (SysprofElf *self,
if (self->debug_link_elf != NULL)
{
- ret = sysprof_elf_get_symbol_at_address_internal (self->debug_link_elf, filename, address, begin_address, end_address, text_offset, is_fallback);
+ ret = sysprof_elf_get_symbol_at_address_internal (self->debug_link_elf, filename, address, begin_address, end_address, text_offset);
if (ret != NULL)
return ret;
@@ -447,11 +446,7 @@ sysprof_elf_get_symbol_at_address_internal (SysprofElf *self,
}
else
{
- begin = address;
- end = address + 1;
- ret = g_strdup_printf ("In File %s+0x%"G_GINT64_MODIFIER"x", filename, address);
- if (is_fallback)
- *is_fallback = TRUE;
+ return NULL;
}
if (begin_address)
@@ -467,16 +462,14 @@ char *
sysprof_elf_get_symbol_at_address (SysprofElf *self,
guint64 address,
guint64 *begin_address,
- guint64 *end_address,
- gboolean *is_fallback)
+ guint64 *end_address)
{
return sysprof_elf_get_symbol_at_address_internal (self,
self->file,
address,
begin_address,
end_address,
- self->text_offset,
- is_fallback);
+ self->text_offset);
}
/**
--
2.45.2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,120 @@
From a0ede5e2b3decc0002f452f0d44bd1a3e7e23870 Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Thu, 10 Oct 2024 17:03:30 -0700
Subject: [PATCH 05/18] libsysprof: add setup hooks for symbolizers
This gives the symbolizer access to the loader so we can propagate tasks
back to it.
---
src/libsysprof/sysprof-multi-symbolizer.c | 22 +++++++++++++++++++++
src/libsysprof/sysprof-symbolizer-private.h | 6 +++++-
src/libsysprof/sysprof-symbolizer.c | 11 +++++++++++
3 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/src/libsysprof/sysprof-multi-symbolizer.c b/src/libsysprof/sysprof-multi-symbolizer.c
index 483cdde5..e1ad90b0 100644
--- a/src/libsysprof/sysprof-multi-symbolizer.c
+++ b/src/libsysprof/sysprof-multi-symbolizer.c
@@ -27,6 +27,7 @@ struct _SysprofMultiSymbolizer
{
SysprofSymbolizer parent_instance;
GPtrArray *symbolizers;
+ guint frozen : 1;
};
struct _SysprofMultiSymbolizerClass
@@ -138,6 +139,25 @@ sysprof_multi_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
return NULL;
}
+static void
+sysprof_multi_symbolizer_setup (SysprofSymbolizer *symbolizer,
+ SysprofDocumentLoader *loader)
+{
+ SysprofMultiSymbolizer *self = (SysprofMultiSymbolizer *)symbolizer;
+
+ g_assert (SYSPROF_IS_MULTI_SYMBOLIZER (self));
+ g_assert (SYSPROF_IS_DOCUMENT_LOADER (loader));
+
+ self->frozen = TRUE;
+
+ for (guint i = 0; i < self->symbolizers->len; i++)
+ {
+ SysprofSymbolizer *child = g_ptr_array_index (self->symbolizers, i);
+
+ _sysprof_symbolizer_setup (child, loader);
+ }
+}
+
static void
sysprof_multi_symbolizer_finalize (GObject *object)
{
@@ -159,6 +179,7 @@ sysprof_multi_symbolizer_class_init (SysprofMultiSymbolizerClass *klass)
symbolizer_class->prepare_async = sysprof_multi_symbolizer_prepare_async;
symbolizer_class->prepare_finish = sysprof_multi_symbolizer_prepare_finish;
symbolizer_class->symbolize = sysprof_multi_symbolizer_symbolize;
+ symbolizer_class->setup = sysprof_multi_symbolizer_setup;
}
static void
@@ -188,6 +209,7 @@ sysprof_multi_symbolizer_take (SysprofMultiSymbolizer *self,
g_return_if_fail (SYSPROF_IS_MULTI_SYMBOLIZER (self));
g_return_if_fail (SYSPROF_IS_SYMBOLIZER (symbolizer));
g_return_if_fail ((gpointer)self != (gpointer)symbolizer);
+ g_return_if_fail (self->frozen == FALSE);
g_ptr_array_add (self->symbolizers, symbolizer);
}
diff --git a/src/libsysprof/sysprof-symbolizer-private.h b/src/libsysprof/sysprof-symbolizer-private.h
index dd917e44..3d68c52b 100644
--- a/src/libsysprof/sysprof-symbolizer-private.h
+++ b/src/libsysprof/sysprof-symbolizer-private.h
@@ -22,6 +22,7 @@
#include "sysprof-address-layout-private.h"
#include "sysprof-document.h"
+#include "sysprof-document-loader.h"
#include "sysprof-mount-namespace-private.h"
#include "sysprof-process-info-private.h"
#include "sysprof-strings-private.h"
@@ -41,6 +42,8 @@ struct _SysprofSymbolizerClass
{
GObjectClass parent_class;
+ void (*setup) (SysprofSymbolizer *self,
+ SysprofDocumentLoader *loader);
void (*prepare_async) (SysprofSymbolizer *self,
SysprofDocument *document,
GCancellable *cancellable,
@@ -56,7 +59,8 @@ struct _SysprofSymbolizerClass
SysprofAddress address);
};
-
+void _sysprof_symbolizer_setup (SysprofSymbolizer *self,
+ SysprofDocumentLoader *loader);
void _sysprof_symbolizer_prepare_async (SysprofSymbolizer *self,
SysprofDocument *document,
GCancellable *cancellable,
diff --git a/src/libsysprof/sysprof-symbolizer.c b/src/libsysprof/sysprof-symbolizer.c
index 9ad17ca2..47d6021a 100644
--- a/src/libsysprof/sysprof-symbolizer.c
+++ b/src/libsysprof/sysprof-symbolizer.c
@@ -99,3 +99,14 @@ _sysprof_symbolizer_symbolize (SysprofSymbolizer *self,
{
return SYSPROF_SYMBOLIZER_GET_CLASS (self)->symbolize (self, strings, process_info, context, address);
}
+
+void
+_sysprof_symbolizer_setup (SysprofSymbolizer *self,
+ SysprofDocumentLoader *loader)
+{
+ g_return_if_fail (SYSPROF_IS_SYMBOLIZER (self));
+ g_return_if_fail (SYSPROF_IS_DOCUMENT_LOADER (loader));
+
+ if (SYSPROF_SYMBOLIZER_GET_CLASS (self)->setup)
+ SYSPROF_SYMBOLIZER_GET_CLASS (self)->setup (self, loader);
+}
--
2.45.2

View File

@ -0,0 +1,159 @@
From 045589a30f5436d5a2887136952e8e0a28e8f2dc Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Thu, 10 Oct 2024 17:04:19 -0700
Subject: [PATCH 06/18] libsysprof: hoist fallback symbol creation
This makes sure that we get that even when not using the Elf symbolizer
but also means we can fallback through the Elf symbolizer into the
debuginfod symbolizer.
---
src/libsysprof/sysprof-document-symbols.c | 69 ++++++++++++++++++++++-
src/libsysprof/sysprof-elf-symbolizer.c | 24 +-------
2 files changed, 69 insertions(+), 24 deletions(-)
diff --git a/src/libsysprof/sysprof-document-symbols.c b/src/libsysprof/sysprof-document-symbols.c
index 2f8d1849..828c7fb6 100644
--- a/src/libsysprof/sysprof-document-symbols.c
+++ b/src/libsysprof/sysprof-document-symbols.c
@@ -85,6 +85,71 @@ symbolize_free (Symbolize *state)
g_free (state);
}
+static SysprofSymbol *
+do_symbolize (SysprofSymbolizer *symbolizer,
+ SysprofStrings *strings,
+ SysprofProcessInfo *process_info,
+ SysprofAddressContext last_context,
+ SysprofAddress address)
+{
+ SysprofDocumentMmap *map;
+ g_autofree char *name = NULL;
+ SysprofSymbol *ret;
+ const char *nick = NULL;
+ const char *path;
+ guint64 map_begin;
+ guint64 map_end;
+ guint64 relative_address;
+ guint64 begin_address;
+ guint64 end_address;
+ guint64 file_offset;
+
+ if ((ret = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address)))
+ return ret;
+
+ /* Fallback, we failed to locate the symbol within a file we can
+ * access, so tell the user about what file contained the symbol
+ * and where (relative to that file) the IP was.
+ */
+
+ if (!(map = sysprof_address_layout_lookup (process_info->address_layout, address)))
+ return NULL;
+
+ map_begin = sysprof_document_mmap_get_start_address (map);
+ map_end = sysprof_document_mmap_get_end_address (map);
+
+ g_assert (address >= map_begin);
+ g_assert (address < map_end);
+
+ file_offset = sysprof_document_mmap_get_file_offset (map);
+
+ relative_address = address;
+ relative_address -= map_begin;
+ relative_address += file_offset;
+
+ path = sysprof_document_mmap_get_file (map);
+
+ begin_address = CLAMP (begin_address, file_offset, file_offset + (map_end - map_begin));
+ end_address = CLAMP (end_address, file_offset, file_offset + (map_end - map_begin));
+ if (end_address == begin_address)
+ end_address++;
+
+ name = g_strdup_printf ("In File %s+0x%"G_GINT64_MODIFIER"x",
+ sysprof_document_mmap_get_file (map),
+ relative_address);
+ begin_address = address;
+ end_address = address + 1;
+
+ ret = _sysprof_symbol_new (sysprof_strings_get (strings, name),
+ sysprof_strings_get (strings, path),
+ sysprof_strings_get (strings, nick),
+ begin_address, end_address,
+ SYSPROF_SYMBOL_KIND_USER);
+ ret->is_fallback = TRUE;
+
+ return ret;
+}
+
static void
add_traceable (SysprofDocumentSymbols *self,
SysprofStrings *strings,
@@ -123,7 +188,7 @@ add_traceable (SysprofDocumentSymbols *self,
if (sysprof_symbol_cache_lookup (self->kernel_symbols, address) != NULL)
continue;
- if ((symbol = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address)))
+ if ((symbol = do_symbolize (symbolizer, strings, process_info, last_context, address)))
sysprof_symbol_cache_take (self->kernel_symbols, g_steal_pointer (&symbol));
}
else
@@ -134,7 +199,7 @@ add_traceable (SysprofDocumentSymbols *self,
sysprof_symbol_cache_lookup (process_info->symbol_cache, address) != NULL)
continue;
- if ((symbol = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address)))
+ if ((symbol = do_symbolize (symbolizer, strings, process_info, last_context, address)))
sysprof_symbol_cache_take (process_info->symbol_cache, g_steal_pointer (&symbol));
}
}
diff --git a/src/libsysprof/sysprof-elf-symbolizer.c b/src/libsysprof/sysprof-elf-symbolizer.c
index f7aabef7..05bd1d6c 100644
--- a/src/libsysprof/sysprof-elf-symbolizer.c
+++ b/src/libsysprof/sysprof-elf-symbolizer.c
@@ -113,7 +113,7 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
build_id,
file_inode,
NULL)))
- goto fallback;
+ return NULL;
nick = sysprof_elf_get_nick (elf);
@@ -124,7 +124,7 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
relative_address,
&begin_address,
&end_address)))
- goto fallback;
+ return NULL;
/* Sanitize address ranges if we have to. Sometimes that can happen
* for us, but it seems to be limited to glibc.
@@ -142,26 +142,6 @@ sysprof_elf_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
SYSPROF_SYMBOL_KIND_USER);
return ret;
-
-fallback:
- /* Fallback, we failed to locate the symbol within a file we can
- * access, so tell the user about what file contained the symbol
- * and where (relative to that file) the IP was.
- */
- name = g_strdup_printf ("In File %s+0x%"G_GINT64_MODIFIER"x",
- sysprof_document_mmap_get_file (map),
- relative_address);
- begin_address = address;
- end_address = address + 1;
-
- ret = _sysprof_symbol_new (sysprof_strings_get (strings, name),
- sysprof_strings_get (strings, path),
- sysprof_strings_get (strings, nick),
- begin_address, end_address,
- SYSPROF_SYMBOL_KIND_USER);
- ret->is_fallback = TRUE;
-
- return ret;
}
static void
--
2.45.2

View File

@ -0,0 +1,600 @@
From e2de54ccf2bf02dc869e83645a980db158509ae2 Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Thu, 10 Oct 2024 17:07:21 -0700
Subject: [PATCH 07/18] libsysprof: add debuginfod symbolizer
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This is based on a debuginfod_client provided by Barnabás Pőcze in !73.
It extends it to use the new task infrastructure to elevate the download
process to the user while loading a capture file.
---
config.h.meson | 2 +
meson.build | 2 +
meson_options.txt | 2 +
src/libsysprof/meson.build | 8 +
.../sysprof-debuginfod-symbolizer.c | 229 ++++++++++++++++++
.../sysprof-debuginfod-symbolizer.h | 42 ++++
.../sysprof-debuginfod-task-private.h | 42 ++++
src/libsysprof/sysprof-debuginfod-task.c | 131 ++++++++++
src/libsysprof/sysprof-document-loader.c | 15 +-
9 files changed, 468 insertions(+), 5 deletions(-)
create mode 100644 src/libsysprof/sysprof-debuginfod-symbolizer.c
create mode 100644 src/libsysprof/sysprof-debuginfod-symbolizer.h
create mode 100644 src/libsysprof/sysprof-debuginfod-task-private.h
create mode 100644 src/libsysprof/sysprof-debuginfod-task.c
diff --git a/config.h.meson b/config.h.meson
index d2f7589a..48b3c2c2 100644
--- a/config.h.meson
+++ b/config.h.meson
@@ -16,6 +16,8 @@
#mesondefine HAVE_LIBSYSTEMD
+#mesondefine HAVE_DEBUGINFOD
+
#mesondefine HAVE_PERF_CLOCKID
#mesondefine HAVE_POLKIT
diff --git a/meson.build b/meson.build
index dda0ca4e..81e97072 100644
--- a/meson.build
+++ b/meson.build
@@ -63,6 +63,7 @@ gio_unix_dep = dependency('gio-unix-2.0', version: glib_req_version,
required: need_glib and host_machine.system() != 'windows')
gtk_dep = dependency('gtk4', version: gtk_req_version, required: need_gtk)
libsystemd_dep = dependency('libsystemd', required: false)
+debuginfod_dep = dependency('libdebuginfod', required: get_option('debuginfod'))
config_h = configuration_data()
config_h.set_quoted('API_VERSION_S', libsysprof_api_version.to_string())
@@ -99,6 +100,7 @@ config_h.set10('ENABLE_NLS', true)
config_h.set_quoted('GETTEXT_PACKAGE', 'sysprof')
config_h.set_quoted('PACKAGE_LOCALE_DIR', join_paths(get_option('prefix'), get_option('datadir'), 'locale'))
config_h.set10('HAVE_LIBSYSTEMD', libsystemd_dep.found())
+config_h.set10('HAVE_DEBUGINFOD', debuginfod_dep.found())
polkit_agent_dep = dependency('polkit-agent-1', required: get_option('polkit-agent'))
config_h.set10('HAVE_POLKIT_AGENT', polkit_agent_dep.found())
diff --git a/meson_options.txt b/meson_options.txt
index 2f78fc3b..02bb6981 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -49,3 +49,5 @@ option('tests', type: 'boolean')
# Optionally disable the examples (this is mostly only useful for building only
# libsysprof-capture as a subproject)
option('examples', type: 'boolean')
+
+option('debuginfod', type: 'feature')
diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build
index 697e9665..b4e58078 100644
--- a/src/libsysprof/meson.build
+++ b/src/libsysprof/meson.build
@@ -153,6 +153,13 @@ libsysprof_private_sources = [
'timsort/gtktimsort.c',
]
+if debuginfod_dep.found() and get_option('debuginfod').enabled()
+ libsysprof_private_sources += [
+ 'sysprof-debuginfod-symbolizer.c',
+ 'sysprof-debuginfod-task.c'
+ ]
+endif
+
if polkit_dep.found()
libsysprof_private_sources += ['sysprof-polkit.c']
endif
@@ -192,6 +199,7 @@ libsysprof_deps = [
libsystemd_dep,
polkit_dep,
+ debuginfod_dep,
libeggbitset_static_dep,
libelfparser_static_dep,
diff --git a/src/libsysprof/sysprof-debuginfod-symbolizer.c b/src/libsysprof/sysprof-debuginfod-symbolizer.c
new file mode 100644
index 00000000..8dd60d19
--- /dev/null
+++ b/src/libsysprof/sysprof-debuginfod-symbolizer.c
@@ -0,0 +1,229 @@
+/* sysprof-debuginfod-symbolizer.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 <elfutils/debuginfod.h>
+#include <errno.h>
+#include <stdatomic.h>
+
+#include <glib/gstdio.h>
+
+#include "sysprof-symbolizer-private.h"
+#include "sysprof-debuginfod-symbolizer.h"
+#include "sysprof-debuginfod-task-private.h"
+#include "sysprof-elf-loader-private.h"
+#include "sysprof-symbol-private.h"
+
+struct _SysprofDebuginfodSymbolizer
+{
+ SysprofSymbolizer parent_instance;
+
+ GWeakRef loader_wr;
+
+ debuginfod_client *client;
+ SysprofElfLoader *loader;
+ GHashTable *cache;
+ GHashTable *failed;
+};
+
+struct _SysprofDebuginfodSymbolizerClass
+{
+ SysprofSymbolizerClass parent_class;
+};
+
+G_DEFINE_FINAL_TYPE (SysprofDebuginfodSymbolizer, sysprof_debuginfod_symbolizer, SYSPROF_TYPE_SYMBOLIZER)
+
+static SysprofSymbol *
+sysprof_debuginfod_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
+ SysprofStrings *strings,
+ const SysprofProcessInfo *process_info,
+ SysprofAddressContext context,
+ SysprofAddress address)
+{
+ SysprofDebuginfodSymbolizer *self = SYSPROF_DEBUGINFOD_SYMBOLIZER (symbolizer);
+ g_autoptr(SysprofElf) elf = NULL;
+ g_autofree char *name = NULL;
+ SysprofSymbol *sym = NULL;
+ SysprofDocumentMmap *map;
+ const char *build_id;
+ const char *path;
+ guint64 relative_address;
+ guint64 begin_address;
+ guint64 end_address;
+ guint64 file_offset;
+ guint64 map_begin;
+ guint64 map_end;
+
+ if (process_info == NULL ||
+ process_info->address_layout == NULL ||
+ process_info->mount_namespace == NULL ||
+ (context != SYSPROF_ADDRESS_CONTEXT_NONE && context != SYSPROF_ADDRESS_CONTEXT_USER) ||
+ !(map = sysprof_address_layout_lookup (process_info->address_layout, address)))
+ return NULL;
+
+ map_begin = sysprof_document_mmap_get_start_address (map);
+ map_end = sysprof_document_mmap_get_end_address (map);
+
+ g_assert (address < map_end);
+ g_assert (address >= map_begin);
+
+ file_offset = sysprof_document_mmap_get_file_offset (map);
+ path = sysprof_document_mmap_get_file (map);
+
+ if (g_hash_table_contains (self->failed, path))
+ return NULL;
+
+ elf = sysprof_elf_loader_load (self->loader,
+ process_info->mount_namespace,
+ path,
+ sysprof_document_mmap_get_build_id (map),
+ sysprof_document_mmap_get_file_inode (map),
+ NULL);
+ if (elf == NULL)
+ return NULL;
+
+ if (!(build_id = sysprof_elf_get_build_id (elf)))
+ return NULL;
+
+ if (!g_hash_table_contains (self->cache, elf))
+ {
+ g_autoptr(SysprofDebuginfodTask) task = sysprof_debuginfod_task_new ();
+ g_autoptr(SysprofDocumentLoader) loader = g_weak_ref_get (&self->loader_wr);
+ g_autoptr(SysprofDocumentTaskScope) scope = _sysprof_document_task_register (SYSPROF_DOCUMENT_TASK (task), loader);
+ g_autoptr(SysprofElf) debuginfo_elf = NULL;
+
+ if (!(debuginfo_elf = sysprof_debuginfod_task_find_debuginfo (task, self->client, path, build_id, NULL)))
+ {
+ g_hash_table_insert (self->failed, g_strdup (path), NULL);
+ return NULL;
+ }
+
+ sysprof_elf_set_debug_link_elf (elf, debuginfo_elf);
+
+ g_hash_table_insert (self->cache, g_object_ref (elf), NULL);
+ }
+
+ relative_address = address;
+ relative_address -= map_begin;
+ relative_address += file_offset;
+
+ name = sysprof_elf_get_symbol_at_address (elf,
+ relative_address,
+ &begin_address,
+ &end_address);
+ if (!name)
+ return NULL;
+
+ begin_address = CLAMP (begin_address, file_offset, file_offset + (map_end - map_begin));
+ end_address = CLAMP (end_address, file_offset, file_offset + (map_end - map_begin));
+ if (end_address == begin_address)
+ end_address++;
+
+ sym = _sysprof_symbol_new (sysprof_strings_get (strings, name),
+ sysprof_strings_get (strings, path),
+ sysprof_strings_get (strings, sysprof_elf_get_nick (elf)),
+ map_begin + (begin_address - file_offset),
+ map_begin + (end_address - file_offset),
+ SYSPROF_SYMBOL_KIND_USER);
+
+ return sym;
+}
+
+static void
+sysprof_debuginfod_symbolizer_setup (SysprofSymbolizer *symbolizer,
+ SysprofDocumentLoader *loader)
+{
+ SysprofDebuginfodSymbolizer *self = SYSPROF_DEBUGINFOD_SYMBOLIZER (symbolizer);
+
+ g_weak_ref_set (&self->loader_wr, loader);
+}
+
+static void
+sysprof_debuginfod_symbolizer_dispose (GObject *object)
+{
+ SysprofDebuginfodSymbolizer *self = SYSPROF_DEBUGINFOD_SYMBOLIZER (object);
+
+ g_hash_table_remove_all (self->cache);
+
+ g_weak_ref_set (&self->loader_wr, NULL);
+
+ G_OBJECT_CLASS (sysprof_debuginfod_symbolizer_parent_class)->dispose (object);
+}
+
+static void
+sysprof_debuginfod_symbolizer_finalize (GObject *object)
+{
+ SysprofDebuginfodSymbolizer *self = SYSPROF_DEBUGINFOD_SYMBOLIZER (object);
+
+ g_clear_object (&self->loader);
+
+ g_clear_pointer (&self->cache, g_hash_table_unref);
+ g_clear_pointer (&self->failed, g_hash_table_unref);
+ g_clear_pointer (&self->client, debuginfod_end);
+
+ g_weak_ref_clear (&self->loader_wr);
+
+ G_OBJECT_CLASS (sysprof_debuginfod_symbolizer_parent_class)->finalize (object);
+}
+
+static void
+sysprof_debuginfod_symbolizer_class_init (SysprofDebuginfodSymbolizerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass);
+
+ object_class->dispose = sysprof_debuginfod_symbolizer_dispose;
+ object_class->finalize = sysprof_debuginfod_symbolizer_finalize;
+
+ symbolizer_class->setup = sysprof_debuginfod_symbolizer_setup;
+ symbolizer_class->symbolize = sysprof_debuginfod_symbolizer_symbolize;
+}
+
+static void
+sysprof_debuginfod_symbolizer_init (SysprofDebuginfodSymbolizer *self)
+{
+ g_weak_ref_init (&self->loader_wr, NULL);
+}
+
+SysprofSymbolizer *
+sysprof_debuginfod_symbolizer_new (GError **error)
+{
+ g_autoptr(SysprofDebuginfodSymbolizer) self = NULL;
+
+ self = g_object_new (SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER, NULL);
+ self->client = debuginfod_begin ();
+
+ if (self->client == NULL)
+ {
+ int errsv = errno;
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (errsv),
+ g_strerror (errsv));
+ return NULL;
+ }
+
+ self->loader = sysprof_elf_loader_new ();
+ self->cache = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
+ self->failed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ return SYSPROF_SYMBOLIZER (g_steal_pointer (&self));
+}
diff --git a/src/libsysprof/sysprof-debuginfod-symbolizer.h b/src/libsysprof/sysprof-debuginfod-symbolizer.h
new file mode 100644
index 00000000..32dd347e
--- /dev/null
+++ b/src/libsysprof/sysprof-debuginfod-symbolizer.h
@@ -0,0 +1,42 @@
+/* sysprof-debuginfod-symbolizer.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 "sysprof-symbolizer.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER (sysprof_debuginfod_symbolizer_get_type())
+#define SYSPROF_IS_DEBUGINFOD_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER)
+#define SYSPROF_DEBUGINFOD_SYMBOLIZER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER, SysprofDebuginfodSymbolizer)
+#define SYSPROF_DEBUGINFOD_SYMBOLIZER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER, SysprofDebuginfodSymbolizerClass)
+
+typedef struct _SysprofDebuginfodSymbolizer SysprofDebuginfodSymbolizer;
+typedef struct _SysprofDebuginfodSymbolizerClass SysprofDebuginfodSymbolizerClass;
+
+SYSPROF_AVAILABLE_IN_ALL
+GType sysprof_debuginfod_symbolizer_get_type (void) G_GNUC_CONST;
+SYSPROF_AVAILABLE_IN_ALL
+SysprofSymbolizer *sysprof_debuginfod_symbolizer_new (GError **error);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDebuginfodSymbolizer, g_object_unref)
+
+G_END_DECLS
diff --git a/src/libsysprof/sysprof-debuginfod-task-private.h b/src/libsysprof/sysprof-debuginfod-task-private.h
new file mode 100644
index 00000000..33e595ec
--- /dev/null
+++ b/src/libsysprof/sysprof-debuginfod-task-private.h
@@ -0,0 +1,42 @@
+/*
+ * sysprof-debuginfod-task-private.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 <elfutils/debuginfod.h>
+
+#include "sysprof-document-task-private.h"
+#include "sysprof-elf-private.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_DEBUGINFOD_TASK (sysprof_debuginfod_task_get_type())
+
+G_DECLARE_FINAL_TYPE (SysprofDebuginfodTask, sysprof_debuginfod_task, SYSPROF, DEBUGINFOD_TASK, SysprofDocumentTask)
+
+SysprofDebuginfodTask *sysprof_debuginfod_task_new (void);
+SysprofElf *sysprof_debuginfod_task_find_debuginfo (SysprofDebuginfodTask *self,
+ debuginfod_client *client,
+ const char *path,
+ const char *build_id_string,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/libsysprof/sysprof-debuginfod-task.c b/src/libsysprof/sysprof-debuginfod-task.c
new file mode 100644
index 00000000..0f996d09
--- /dev/null
+++ b/src/libsysprof/sysprof-debuginfod-task.c
@@ -0,0 +1,131 @@
+/*
+ * sysprof-debuginfod-task.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 <glib/gstdio.h>
+#include <glib/gi18n.h>
+
+#include <gio/gio.h>
+
+#include "sysprof-debuginfod-task-private.h"
+
+struct _SysprofDebuginfodTask
+{
+ SysprofDocumentTask parent_instance;
+};
+
+G_DEFINE_FINAL_TYPE (SysprofDebuginfodTask, sysprof_debuginfod_task, SYSPROF_TYPE_DOCUMENT_TASK)
+
+static void
+sysprof_debuginfod_task_class_init (SysprofDebuginfodTaskClass *klass)
+{
+}
+
+static void
+sysprof_debuginfod_task_init (SysprofDebuginfodTask *self)
+{
+}
+
+SysprofDebuginfodTask *
+sysprof_debuginfod_task_new (void)
+{
+ return g_object_new (SYSPROF_TYPE_DEBUGINFOD_TASK, NULL);
+}
+
+static int
+sysprof_debuginfod_task_progress_cb (debuginfod_client *client,
+ long a,
+ long b)
+{
+ SysprofDocumentTask *task = debuginfod_get_user_data (client);
+ double progress;
+
+ g_assert (client != NULL);
+ g_assert (SYSPROF_IS_DEBUGINFOD_TASK (task));
+
+ if (b > 0)
+ progress = (double)a / (double)b;
+ else
+ progress = 0;
+
+ _sysprof_document_task_set_progress (task, progress);
+
+ if (sysprof_document_task_is_cancelled (task))
+ return -1;
+
+ return 0;
+}
+
+SysprofElf *
+sysprof_debuginfod_task_find_debuginfo (SysprofDebuginfodTask *self,
+ debuginfod_client *client,
+ const char *path,
+ const char *build_id_string,
+ GError **error)
+{
+ g_autoptr(GMappedFile) mapped_file = NULL;
+ g_autoptr(SysprofElf) debuginfo_elf = NULL;
+ g_autofd int fd = -1;
+ char *debuginfo_path = NULL;
+
+ g_return_val_if_fail (SYSPROF_IS_DEBUGINFOD_TASK (self), NULL);
+ g_return_val_if_fail (client != NULL, NULL);
+ g_return_val_if_fail (build_id_string != NULL, NULL);
+
+ debuginfod_set_user_data (client, self);
+ debuginfod_set_progressfn (client, sysprof_debuginfod_task_progress_cb);
+
+ _sysprof_document_task_set_title (SYSPROF_DOCUMENT_TASK (self), _("Downloading Symbols…"));
+ _sysprof_document_task_take_message (SYSPROF_DOCUMENT_TASK (self), g_strdup (path));
+
+ fd = debuginfod_find_debuginfo (client,
+ (const unsigned char *)build_id_string, 0,
+ &debuginfo_path);
+
+ if (fd < 0)
+ {
+ if (error != NULL)
+ {
+ int errsv = errno;
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ g_io_error_from_errno (errsv),
+ g_strerror (errsv));
+ }
+
+ goto failure;
+ }
+
+ if (!(mapped_file = g_mapped_file_new_from_fd (fd, FALSE, error)))
+ goto failure;
+
+ if (!(debuginfo_elf = sysprof_elf_new (debuginfo_path, g_steal_pointer (&mapped_file), 0, error)))
+ goto failure;
+
+failure:
+ free (debuginfo_path);
+
+ debuginfod_set_user_data (client, NULL);
+ debuginfod_set_progressfn (client, NULL);
+
+ return g_steal_pointer (&debuginfo_elf);
+}
diff --git a/src/libsysprof/sysprof-document-loader.c b/src/libsysprof/sysprof-document-loader.c
index 3cd408c4..00b525df 100644
--- a/src/libsysprof/sysprof-document-loader.c
+++ b/src/libsysprof/sysprof-document-loader.c
@@ -196,7 +196,6 @@ static void
set_default_symbolizer (SysprofDocumentLoader *self)
{
g_autoptr(SysprofMultiSymbolizer) multi = NULL;
- g_autoptr(SysprofSymbolizer) debuginfod = NULL;
g_autoptr(GError) error = NULL;
g_assert (SYSPROF_IS_DOCUMENT_LOADER (self));
@@ -209,10 +208,16 @@ set_default_symbolizer (SysprofDocumentLoader *self)
sysprof_multi_symbolizer_take (multi, sysprof_elf_symbolizer_new ());
sysprof_multi_symbolizer_take (multi, sysprof_jitmap_symbolizer_new ());
- if (!(debuginfod = sysprof_debuginfod_symbolizer_new (&error)))
- g_warning ("Failed to create debuginfod symbolizer: %s", error->message);
- else
- sysprof_multi_symbolizer_take (multi, g_steal_pointer (&debuginfod));
+#if HAVE_DEBUGINFOD
+ {
+ g_autoptr(SysprofSymbolizer) debuginfod = NULL;
+
+ if (!(debuginfod = sysprof_debuginfod_symbolizer_new (&error)))
+ g_warning ("Failed to create debuginfod symbolizer: %s", error->message);
+ else
+ sysprof_multi_symbolizer_take (multi, g_steal_pointer (&debuginfod));
+ }
+#endif
self->symbolizer = SYSPROF_SYMBOLIZER (g_steal_pointer (&multi));
}
--
2.45.2

View File

@ -0,0 +1,26 @@
From 27e5392cbf4ce792a26acc0945d36a243d9826cf Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Fri, 11 Oct 2024 11:08:52 -0700
Subject: [PATCH 08/18] libsysprof: ensure access to process info
---
src/libsysprof/sysprof-document-symbols.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/libsysprof/sysprof-document-symbols.c b/src/libsysprof/sysprof-document-symbols.c
index 828c7fb6..e5584a69 100644
--- a/src/libsysprof/sysprof-document-symbols.c
+++ b/src/libsysprof/sysprof-document-symbols.c
@@ -112,6 +112,9 @@ do_symbolize (SysprofSymbolizer *symbolizer,
* and where (relative to that file) the IP was.
*/
+ if (process_info == NULL || process_info->address_layout == NULL)
+ return NULL;
+
if (!(map = sysprof_address_layout_lookup (process_info->address_layout, address)))
return NULL;
--
2.45.2

View File

@ -0,0 +1,25 @@
From 970acbbcf5a25983b593ef781885c81f8d55cee2 Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Fri, 11 Oct 2024 11:26:13 -0700
Subject: [PATCH 09/18] libsysprof: fix building with -Ddebuginfod=auto
---
src/libsysprof/meson.build | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build
index b4e58078..ce6e37f2 100644
--- a/src/libsysprof/meson.build
+++ b/src/libsysprof/meson.build
@@ -153,7 +153,7 @@ libsysprof_private_sources = [
'timsort/gtktimsort.c',
]
-if debuginfod_dep.found() and get_option('debuginfod').enabled()
+if debuginfod_dep.found()
libsysprof_private_sources += [
'sysprof-debuginfod-symbolizer.c',
'sysprof-debuginfod-task.c'
--
2.45.2

View File

@ -0,0 +1,158 @@
From f9f78bfbd727b0b95dff4fad5325c72731e0e970 Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Fri, 11 Oct 2024 11:45:41 -0700
Subject: [PATCH 10/18] libsysprof: return NULL instance unless debuginfod
works
If we are configured with support for debuginfod and it fails to initialize
or we are not configured to use debuginfod, always ensure g_object_new()
will return a NULL instance back.
This helps prevent against misconfigured instances so we do not need to
do so many checks in vfuncs as well as allowing us to have the GType be
available always even when not built with debuginfod.
---
.../sysprof-debuginfod-symbolizer.c | 66 +++++++++++++++----
1 file changed, 53 insertions(+), 13 deletions(-)
diff --git a/src/libsysprof/sysprof-debuginfod-symbolizer.c b/src/libsysprof/sysprof-debuginfod-symbolizer.c
index 8dd60d19..bcad9a5c 100644
--- a/src/libsysprof/sysprof-debuginfod-symbolizer.c
+++ b/src/libsysprof/sysprof-debuginfod-symbolizer.c
@@ -20,18 +20,22 @@
#include "config.h"
-#include <elfutils/debuginfod.h>
#include <errno.h>
-#include <stdatomic.h>
#include <glib/gstdio.h>
#include "sysprof-symbolizer-private.h"
#include "sysprof-debuginfod-symbolizer.h"
-#include "sysprof-debuginfod-task-private.h"
#include "sysprof-elf-loader-private.h"
#include "sysprof-symbol-private.h"
+#if HAVE_DEBUGINFOD
+# include <elfutils/debuginfod.h>
+# include "sysprof-debuginfod-task-private.h"
+#else
+typedef struct _debuginfod_client debuginfod_client;
+#endif
+
struct _SysprofDebuginfodSymbolizer
{
SysprofSymbolizer parent_instance;
@@ -58,6 +62,7 @@ sysprof_debuginfod_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
SysprofAddressContext context,
SysprofAddress address)
{
+#if HAVE_DEBUGINFOD
SysprofDebuginfodSymbolizer *self = SYSPROF_DEBUGINFOD_SYMBOLIZER (symbolizer);
g_autoptr(SysprofElf) elf = NULL;
g_autofree char *name = NULL;
@@ -145,6 +150,9 @@ sysprof_debuginfod_symbolizer_symbolize (SysprofSymbolizer *symbolizer,
SYSPROF_SYMBOL_KIND_USER);
return sym;
+#else
+ return NULL;
+#endif
}
static void
@@ -177,19 +185,54 @@ sysprof_debuginfod_symbolizer_finalize (GObject *object)
g_clear_pointer (&self->cache, g_hash_table_unref);
g_clear_pointer (&self->failed, g_hash_table_unref);
+
+#if HAVE_DEBUGINFOD
g_clear_pointer (&self->client, debuginfod_end);
+#endif
g_weak_ref_clear (&self->loader_wr);
G_OBJECT_CLASS (sysprof_debuginfod_symbolizer_parent_class)->finalize (object);
}
+static GObject *
+sysprof_debuginfod_symbolizer_constructor (GType type,
+ guint n_construct_params,
+ GObjectConstructParam *construct_properties)
+{
+#if HAVE_DEBUGINFOD
+ debuginfod_client *client;
+ GObject *object;
+
+ g_assert (type == SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER);
+
+ /* Don't even allow creating a SysprofDebuginfodSymbolizer instance unless we
+ * can create a new debuginfod_client. This ensures that even if an application
+ * does `g_object_new(SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER, NULL)` they will get
+ * `NULL` back instead of a misconfigured instance.
+ */
+ if (!(client = debuginfod_begin ()))
+ return NULL;
+
+ object = G_OBJECT_CLASS (sysprof_debuginfod_symbolizer_parent_class)
+ ->constructor (type, n_construct_params, construct_properties);
+
+ SYSPROF_DEBUGINFOD_SYMBOLIZER (object)->client = client;
+
+ return object;
+#else
+ errno = ENOTSUP;
+ return NULL;
+#endif
+}
+
static void
sysprof_debuginfod_symbolizer_class_init (SysprofDebuginfodSymbolizerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
SysprofSymbolizerClass *symbolizer_class = SYSPROF_SYMBOLIZER_CLASS (klass);
+ object_class->constructor = sysprof_debuginfod_symbolizer_constructor;
object_class->dispose = sysprof_debuginfod_symbolizer_dispose;
object_class->finalize = sysprof_debuginfod_symbolizer_finalize;
@@ -201,17 +244,18 @@ static void
sysprof_debuginfod_symbolizer_init (SysprofDebuginfodSymbolizer *self)
{
g_weak_ref_init (&self->loader_wr, NULL);
+
+ self->loader = sysprof_elf_loader_new ();
+ self->cache = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
+ self->failed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
}
SysprofSymbolizer *
sysprof_debuginfod_symbolizer_new (GError **error)
{
- g_autoptr(SysprofDebuginfodSymbolizer) self = NULL;
+ SysprofSymbolizer *self;
- self = g_object_new (SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER, NULL);
- self->client = debuginfod_begin ();
-
- if (self->client == NULL)
+ if (!(self = g_object_new (SYSPROF_TYPE_DEBUGINFOD_SYMBOLIZER, NULL)))
{
int errsv = errno;
g_set_error_literal (error,
@@ -221,9 +265,5 @@ sysprof_debuginfod_symbolizer_new (GError **error)
return NULL;
}
- self->loader = sysprof_elf_loader_new ();
- self->cache = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
- self->failed = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-
- return SYSPROF_SYMBOLIZER (g_steal_pointer (&self));
+ return self;
}
--
2.45.2

View File

@ -0,0 +1,56 @@
From 7d2fccd26931a2ffb5006511a6ecaf529016d7ae Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Fri, 11 Oct 2024 11:46:26 -0700
Subject: [PATCH 11/18] build: always build debuginfod symbolizer
Even if it is disabled, we want the GType enabled and part of our ABI. We
will return NULL if one is created and debuginfod is not supported or if
we failed to create a client.
---
src/libsysprof/meson.build | 3 ++-
src/libsysprof/sysprof.h | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build
index ce6e37f2..549042de 100644
--- a/src/libsysprof/meson.build
+++ b/src/libsysprof/meson.build
@@ -10,6 +10,7 @@ libsysprof_public_sources = [
'sysprof-cpu-info.c',
'sysprof-cpu-usage.c',
'sysprof-dbus-monitor.c',
+ 'sysprof-debuginfod-symbolizer.c',
'sysprof-diagnostic.c',
'sysprof-disk-usage.c',
'sysprof-document-allocation.c',
@@ -75,6 +76,7 @@ libsysprof_public_headers = [
'sysprof-cpu-info.h',
'sysprof-cpu-usage.h',
'sysprof-dbus-monitor.h',
+ 'sysprof-debuginfod-symbolizer.h',
'sysprof-diagnostic.h',
'sysprof-disk-usage.h',
'sysprof-document-allocation.h',
@@ -155,7 +157,6 @@ libsysprof_private_sources = [
if debuginfod_dep.found()
libsysprof_private_sources += [
- 'sysprof-debuginfod-symbolizer.c',
'sysprof-debuginfod-task.c'
]
endif
diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h
index c2176619..d30f9fd4 100644
--- a/src/libsysprof/sysprof.h
+++ b/src/libsysprof/sysprof.h
@@ -34,6 +34,7 @@ G_BEGIN_DECLS
# include "sysprof-cpu-info.h"
# include "sysprof-cpu-usage.h"
# include "sysprof-dbus-monitor.h"
+# include "sysprof-debuginfod-symbolizer.h"
# include "sysprof-diagnostic.h"
# include "sysprof-disk-usage.h"
# include "sysprof-document-allocation.h"
--
2.45.2

View File

@ -0,0 +1,49 @@
From d19dd1b167de2c49f33c86908b7715790481a3de Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Wed, 23 Oct 2024 11:54:33 -0700
Subject: [PATCH 12/18] libsysprof: remove unnecessary address calculation
We are only fallback symbols here, which is 1 address-wide.
---
src/libsysprof/sysprof-document-symbols.c | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/src/libsysprof/sysprof-document-symbols.c b/src/libsysprof/sysprof-document-symbols.c
index e5584a69..642f702b 100644
--- a/src/libsysprof/sysprof-document-symbols.c
+++ b/src/libsysprof/sysprof-document-symbols.c
@@ -100,8 +100,6 @@ do_symbolize (SysprofSymbolizer *symbolizer,
guint64 map_begin;
guint64 map_end;
guint64 relative_address;
- guint64 begin_address;
- guint64 end_address;
guint64 file_offset;
if ((ret = _sysprof_symbolizer_symbolize (symbolizer, strings, process_info, last_context, address)))
@@ -132,21 +130,14 @@ do_symbolize (SysprofSymbolizer *symbolizer,
path = sysprof_document_mmap_get_file (map);
- begin_address = CLAMP (begin_address, file_offset, file_offset + (map_end - map_begin));
- end_address = CLAMP (end_address, file_offset, file_offset + (map_end - map_begin));
- if (end_address == begin_address)
- end_address++;
-
name = g_strdup_printf ("In File %s+0x%"G_GINT64_MODIFIER"x",
sysprof_document_mmap_get_file (map),
relative_address);
- begin_address = address;
- end_address = address + 1;
ret = _sysprof_symbol_new (sysprof_strings_get (strings, name),
sysprof_strings_get (strings, path),
sysprof_strings_get (strings, nick),
- begin_address, end_address,
+ address, address + 1,
SYSPROF_SYMBOL_KIND_USER);
ret->is_fallback = TRUE;
--
2.45.2

View File

@ -0,0 +1,251 @@
From 37b1710bcb04bcee2f9c05293ac535c514cd392b Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Fri, 25 Oct 2024 10:49:15 -0700
Subject: [PATCH 13/18] libsysprof: add muxer GSource
This allows copying events from a capture stream transparently into the
destination. No processing of the stream is performed, but that may change
in the future to accomidate JIT/Counter translations.
Internal only as support for upcoming live unwinding via external process.
---
src/libsysprof/meson.build | 1 +
src/libsysprof/sysprof-muxer-source.c | 173 ++++++++++++++++++++++++++
src/libsysprof/sysprof-muxer-source.h | 33 +++++
3 files changed, 207 insertions(+)
create mode 100644 src/libsysprof/sysprof-muxer-source.c
create mode 100644 src/libsysprof/sysprof-muxer-source.h
diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build
index 549042de..f3fa0850 100644
--- a/src/libsysprof/meson.build
+++ b/src/libsysprof/meson.build
@@ -147,6 +147,7 @@ libsysprof_private_sources = [
'sysprof-mount-device.c',
'sysprof-mount-namespace.c',
'sysprof-mount.c',
+ 'sysprof-muxer-source.c',
'sysprof-perf-event-stream.c',
'sysprof-podman.c',
'sysprof-process-info.c',
diff --git a/src/libsysprof/sysprof-muxer-source.c b/src/libsysprof/sysprof-muxer-source.c
new file mode 100644
index 00000000..c037a38f
--- /dev/null
+++ b/src/libsysprof/sysprof-muxer-source.c
@@ -0,0 +1,173 @@
+/*
+ * sysprof-muxer-source.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 <glib/gstdio.h>
+#include <glib-unix.h>
+
+#include "sysprof-muxer-source.h"
+
+#define DEFAULT_BUFFER_SIZE (4096*16)
+
+typedef struct _SysprofMuxerSource
+{
+ GSource gsource;
+ int capture_fd;
+ SysprofCaptureWriter *writer;
+ struct {
+ guint8 *allocation;
+ guint8 *begin;
+ guint8 *end;
+ guint8 *capacity;
+ gsize to_skip;
+ } buffer;
+} SysprofMuxerSource;
+
+static gboolean
+sysprof_muxer_source_size (SysprofMuxerSource *source)
+{
+ return source->buffer.end - source->buffer.begin;
+}
+
+static gboolean
+sysprof_muxer_source_dispatch (GSource *gsource,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ SysprofMuxerSource *source = (SysprofMuxerSource *)gsource;
+ gssize n_read;
+
+ g_assert (source != NULL);
+ g_assert (source->writer != NULL);
+
+ /* Try to read the next chunk */
+ n_read = read (source->capture_fd, source->buffer.end, source->buffer.capacity - source->buffer.end);
+
+ if (n_read > 0)
+ {
+ const SysprofCaptureFrame *frame;
+
+ /* Advance tail to what was filled */
+ source->buffer.end += n_read;
+
+ /* Get to next alignment */
+ if (source->buffer.to_skip)
+ {
+ gsize amount = MIN (source->buffer.to_skip, source->buffer.end - source->buffer.begin);
+ source->buffer.begin += amount;
+ source->buffer.to_skip -= amount;
+ }
+
+ /* If there is enough to read the frame header, try to read and dispatch
+ * it in raw form. We assume we're the same endianness here because this
+ * is coming from the same host (live-unwinder currently).
+ */
+ while (sysprof_muxer_source_size (source) >= sizeof *frame)
+ {
+ frame = (const SysprofCaptureFrame *)source->buffer.begin;
+
+ if (frame->len <= sysprof_muxer_source_size (source))
+ {
+ source->buffer.begin += frame->len;
+
+ if (frame->len % sizeof (guint64) != 0)
+ source->buffer.to_skip = sizeof (guint64) - (frame->len % sizeof (guint64));
+
+ /* TODO: Technically for counters/JIT map we need to translate them. */
+
+ _sysprof_capture_writer_add_raw (source->writer, frame);
+ }
+
+ if (source->buffer.to_skip > 0 &&
+ source->buffer.to_skip <= sysprof_muxer_source_size (source))
+ {
+ source->buffer.begin += source->buffer.to_skip;
+ source->buffer.to_skip = 0;
+ continue;
+ }
+
+ break;
+ }
+
+ /* Move anything left to the head of the buffer so we can
+ * fill in the entire next frame of data.
+ */
+ if (source->buffer.begin < source->buffer.end)
+ {
+ /* TODO: Should we adjust for alignment here? */
+
+ memmove (source->buffer.allocation,
+ source->buffer.begin,
+ source->buffer.end - source->buffer.begin);
+ source->buffer.end = source->buffer.allocation + (source->buffer.end - source->buffer.begin);
+ source->buffer.begin = source->buffer.allocation;
+ }
+ else
+ {
+ source->buffer.end = source->buffer.allocation;
+ source->buffer.begin = source->buffer.allocation;
+ }
+ }
+
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+sysprof_muxer_source_finalize (GSource *gsource)
+{
+ SysprofMuxerSource *source = (SysprofMuxerSource *)gsource;
+
+ g_clear_fd (&source->capture_fd, NULL);
+ g_clear_pointer (&source->writer, sysprof_capture_writer_unref);
+ g_clear_pointer (&source->buffer.begin, g_free);
+}
+
+static const GSourceFuncs source_funcs = {
+ .dispatch = sysprof_muxer_source_dispatch,
+ .finalize = sysprof_muxer_source_finalize,
+};
+
+GSource *
+sysprof_muxer_source_new (int capture_fd,
+ SysprofCaptureWriter *writer)
+{
+ SysprofMuxerSource *source;
+
+ g_return_val_if_fail (capture_fd > -1, NULL);
+ g_return_val_if_fail (writer != NULL, NULL);
+
+ source = (SysprofMuxerSource *)g_source_new ((GSourceFuncs *)&source_funcs, sizeof (SysprofMuxerSource));
+ source->capture_fd = capture_fd;
+ source->writer = sysprof_capture_writer_ref (writer);
+ source->buffer.allocation = g_malloc (DEFAULT_BUFFER_SIZE);
+ source->buffer.begin = source->buffer.allocation;
+ source->buffer.end = source->buffer.allocation;
+ source->buffer.capacity = source->buffer.allocation + DEFAULT_BUFFER_SIZE;
+ source->buffer.to_skip = sizeof (SysprofCaptureFileHeader);
+
+ g_unix_set_fd_nonblocking (capture_fd, TRUE, NULL);
+
+ g_source_add_unix_fd ((GSource *)source, capture_fd, G_IO_IN);
+
+ return (GSource *)source;
+}
diff --git a/src/libsysprof/sysprof-muxer-source.h b/src/libsysprof/sysprof-muxer-source.h
new file mode 100644
index 00000000..9b9a94e1
--- /dev/null
+++ b/src/libsysprof/sysprof-muxer-source.h
@@ -0,0 +1,33 @@
+/*
+ * sysprof-muxer-source.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.h>
+
+#include <sysprof-capture.h>
+
+G_BEGIN_DECLS
+
+GSource *sysprof_muxer_source_new (int capture_fd,
+ SysprofCaptureWriter *writer);
+
+G_END_DECLS
--
2.45.2

View File

@ -0,0 +1,138 @@
From 38646818c06b45417b693695819d05198fb8e53b Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Sun, 3 Nov 2024 10:39:23 -0800
Subject: [PATCH 14/18] libsysprof: add support for stack/regs options in attr
This requires a coordinating sysprofd that knows how to handle reading the
new attributes. Setting these fields will allow snapshotting the contents
of the stack and registers to do offline unwinding.
Also make the conversion to GVariant available outside the module so that
we can consume it for live unwinding.
---
.../sysprof-perf-event-stream-private.h | 25 ++++++++++---------
src/libsysprof/sysprof-perf-event-stream.c | 10 +++++---
src/sysprofd/helpers.c | 16 ++++++++++++
3 files changed, 36 insertions(+), 15 deletions(-)
diff --git a/src/libsysprof/sysprof-perf-event-stream-private.h b/src/libsysprof/sysprof-perf-event-stream-private.h
index 4ac0fd1e..7e3d9deb 100644
--- a/src/libsysprof/sysprof-perf-event-stream-private.h
+++ b/src/libsysprof/sysprof-perf-event-stream-private.h
@@ -165,17 +165,18 @@ typedef void (*SysprofPerfEventCallback) (const SysprofPerfEvent *event,
G_DECLARE_FINAL_TYPE (SysprofPerfEventStream, sysprof_perf_event_stream, SYSPROF, PERF_EVENT_STREAM, GObject)
-DexFuture *sysprof_perf_event_stream_new (GDBusConnection *connection,
- struct perf_event_attr *attr,
- int cpu,
- int group_fd,
- guint64 flags,
- SysprofPerfEventCallback callback,
- gpointer callback_data,
- GDestroyNotify callback_data_destroy);
-gboolean sysprof_perf_event_stream_enable (SysprofPerfEventStream *self,
- GError **error);
-gboolean sysprof_perf_event_stream_disable (SysprofPerfEventStream *self,
- GError **error);
+DexFuture *sysprof_perf_event_stream_new (GDBusConnection *connection,
+ struct perf_event_attr *attr,
+ int cpu,
+ int group_fd,
+ guint64 flags,
+ SysprofPerfEventCallback callback,
+ gpointer callback_data,
+ GDestroyNotify callback_data_destroy);
+gboolean sysprof_perf_event_stream_enable (SysprofPerfEventStream *self,
+ GError **error);
+gboolean sysprof_perf_event_stream_disable (SysprofPerfEventStream *self,
+ GError **error);
+GVariant *_sysprof_perf_event_attr_to_variant (const struct perf_event_attr *attr);
G_END_DECLS
diff --git a/src/libsysprof/sysprof-perf-event-stream.c b/src/libsysprof/sysprof-perf-event-stream.c
index a7bf8d88..c40182ad 100644
--- a/src/libsysprof/sysprof-perf-event-stream.c
+++ b/src/libsysprof/sysprof-perf-event-stream.c
@@ -109,8 +109,8 @@ G_DEFINE_FINAL_TYPE (SysprofPerfEventStream, sysprof_perf_event_stream, G_TYPE_O
static GParamSpec *properties [N_PROPS];
-static GVariant *
-build_options_dict (const struct perf_event_attr *attr)
+GVariant *
+_sysprof_perf_event_attr_to_variant (const struct perf_event_attr *attr)
{
return g_variant_take_ref (
g_variant_new_parsed ("["
@@ -130,6 +130,8 @@ build_options_dict (const struct perf_event_attr *attr)
"{'sample_period', <%t>},"
"{'sample_type', <%t>},"
"{'task', <%b>},"
+ "{'sample_stack_user', <%u>},"
+ "{'sample_regs_user', <%t>},"
"{'type', <%u>}"
"]",
(gboolean)!!attr->comm,
@@ -148,6 +150,8 @@ build_options_dict (const struct perf_event_attr *attr)
(guint64)attr->sample_period,
(guint64)attr->sample_type,
(gboolean)!!attr->task,
+ (guint32)attr->sample_stack_user,
+ (guint64)attr->sample_regs_user,
(guint32)attr->type));
}
@@ -513,7 +517,7 @@ sysprof_perf_event_stream_new (GDBusConnection *connection,
group_fd_handle = g_unix_fd_list_append (fd_list, group_fd, NULL);
}
- options = build_options_dict (attr);
+ options = _sysprof_perf_event_attr_to_variant (attr);
g_dbus_connection_call_with_unix_fd_list (connection,
"org.gnome.Sysprof3",
diff --git a/src/sysprofd/helpers.c b/src/sysprofd/helpers.c
index 2aebc417..7e5df34a 100644
--- a/src/sysprofd/helpers.c
+++ b/src/sysprofd/helpers.c
@@ -127,6 +127,8 @@ helpers_perf_event_open (GVariant *options,
guint64 sample_period = 0;
guint64 sample_type = 0;
guint64 config = 0;
+ guint64 sample_regs_user = 0;
+ guint sample_stack_user = 0;
int clockid = CLOCK_MONOTONIC;
int comm = 0;
int mmap_ = 0;
@@ -236,6 +238,18 @@ helpers_perf_event_open (GVariant *options,
goto bad_arg;
use_clockid = g_variant_get_boolean (value);
}
+ else if (strcmp (key, "sample_stack_user") == 0)
+ {
+ if (!g_variant_is_of_type (value, G_VARIANT_TYPE_UINT32))
+ goto bad_arg;
+ sample_stack_user = g_variant_get_uint32 (value);
+ }
+ else if (strcmp (key, "sample_regs_user") == 0)
+ {
+ if (!g_variant_is_of_type (value, G_VARIANT_TYPE_UINT64))
+ goto bad_arg;
+ sample_regs_user = g_variant_get_uint64 (value);
+ }
continue;
@@ -257,6 +271,8 @@ helpers_perf_event_open (GVariant *options,
attr.task = !!task;
attr.type = type;
attr.wakeup_events = wakeup_events;
+ attr.sample_regs_user = sample_regs_user;
+ attr.sample_stack_user = sample_stack_user;
#ifdef HAVE_PERF_CLOCKID
if (!use_clockid || clockid < 0)
--
2.45.2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,694 @@
From ef191d3f52ad904a9af8908ff5c2a1e44dfd27b0 Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Sun, 3 Nov 2024 10:53:29 -0800
Subject: [PATCH 16/18] libsysprof: add SysprofUserSampler for live unwinding
This instrument triggers the live unwinder in sysprofd to capture a
pre-configured amount of stack contents and CPU registers. You can use
this instead of SysprofSampler in cases where you do not have frame-
pointers but want a useful trace.
It does have a moderate amount of CPU overhead compared to just relying
on frame-pointers so keep that in mind. Generally useful on platforms
that do not have frame pointers such as CentOS.
---
src/libsysprof/meson.build | 7 +
src/libsysprof/sysprof-user-sampler.c | 570 ++++++++++++++++++++++++++
src/libsysprof/sysprof-user-sampler.h | 43 ++
src/libsysprof/sysprof.h | 1 +
4 files changed, 621 insertions(+)
create mode 100644 src/libsysprof/sysprof-user-sampler.c
create mode 100644 src/libsysprof/sysprof-user-sampler.h
diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build
index f3fa0850..e49c3a37 100644
--- a/src/libsysprof/meson.build
+++ b/src/libsysprof/meson.build
@@ -63,6 +63,7 @@ libsysprof_public_sources = [
'sysprof-time-span.c',
'sysprof-tracefd-consumer.c',
'sysprof-tracer.c',
+ 'sysprof-user-sampler.c',
]
libsysprof_public_headers = [
@@ -130,6 +131,7 @@ libsysprof_public_headers = [
'sysprof-time-span.h',
'sysprof-tracefd-consumer.h',
'sysprof-tracer.h',
+ 'sysprof-user-sampler.h',
]
libsysprof_private_sources = [
@@ -154,6 +156,11 @@ libsysprof_private_sources = [
'sysprof-strings.c',
'sysprof-symbol-cache.c',
'timsort/gtktimsort.c',
+
+ gnome.gdbus_codegen('ipc-unwinder',
+ sources: '../sysprofd/org.gnome.Sysprof3.Unwinder.xml',
+ interface_prefix: 'org.gnome.Sysprof3.',
+ namespace: 'Ipc'),
]
if debuginfod_dep.found()
diff --git a/src/libsysprof/sysprof-user-sampler.c b/src/libsysprof/sysprof-user-sampler.c
new file mode 100644
index 00000000..0e3afeae
--- /dev/null
+++ b/src/libsysprof/sysprof-user-sampler.c
@@ -0,0 +1,570 @@
+/* sysprof-user-sampler.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 <sys/ioctl.h>
+#include <sys/eventfd.h>
+
+#include <asm/perf_regs.h>
+
+#include <glib/gstdio.h>
+
+#include "sysprof-instrument-private.h"
+#include "sysprof-perf-event-stream-private.h"
+#include "sysprof-recording-private.h"
+#include "sysprof-user-sampler.h"
+#include "sysprof-muxer-source.h"
+
+#include "ipc-unwinder.h"
+
+/* The following was provided to Sysprof by Serhei Makarov as part
+ * of the eu-stacktrace prototype work.
+ */
+#ifdef _ASM_X86_PERF_REGS_H
+/* #define SYSPROF_ARCH_PREFERRED_REGS PERF_REG_EXTENDED_MASK -- error on x86_64 due to including segment regs*/
+#define REG(R) (1ULL << PERF_REG_X86_ ## R)
+#define DWARF_NEEDED_REGS (/* no FLAGS */ REG(IP) | REG(SP) | REG(AX) | REG(CX) | REG(DX) | REG(BX) | REG(SI) | REG(DI) | REG(SP) | REG(BP) | /* no segment regs */ REG(R8) | REG(R9) | REG(R10) | REG(R11) | REG(R12) | REG(R13) | REG(R14) | REG(R15))
+/* XXX register ordering is defined in linux arch/x86/include/uapi/asm/perf_regs.h;
+ see code in tools/perf/util/intel-pt.c intel_pt_add_gp_regs()
+ and note how registers are added in the same order as the perf_regs.h enum */
+#define SYSPROF_ARCH_PREFERRED_REGS DWARF_NEEDED_REGS
+/* TODO: add other architectures, imitating the linux tools/perf tree */
+#else
+# define SYSPROF_ARCH_PREFERRED_REGS PERF_REG_EXTENDED_MASK
+#endif /* _ASM_{arch}_PERF_REGS_H */
+
+#define N_WAKEUP_EVENTS 149
+
+struct _SysprofUserSampler
+{
+ SysprofInstrument parent_instance;
+ GArray *perf_fds;
+ int capture_fd;
+ int event_fd;
+ guint stack_size;
+};
+
+struct _SysprofUserSamplerClass
+{
+ SysprofInstrumentClass parent_class;
+};
+
+G_DEFINE_FINAL_TYPE (SysprofUserSampler, sysprof_user_sampler, SYSPROF_TYPE_INSTRUMENT)
+
+static void
+close_fd (gpointer data)
+{
+ int *fdp = data;
+
+ if (*fdp != -1)
+ {
+ close (*fdp);
+ *fdp = -1;
+ }
+}
+
+static void
+sysprof_user_sampler_ioctl (SysprofUserSampler *self,
+ gboolean enable)
+{
+ for (guint i = 0; i < self->perf_fds->len; i++)
+ {
+ int perf_fd = g_array_index (self->perf_fds, int, i);
+
+ if (0 != ioctl (perf_fd, enable ? PERF_EVENT_IOC_ENABLE : PERF_EVENT_IOC_DISABLE))
+ {
+ int errsv = errno;
+ g_warning ("Failed to toggle perf_fd: %s", g_strerror (errsv));
+ }
+ }
+}
+
+static char **
+sysprof_user_sampler_list_required_policy (SysprofInstrument *instrument)
+{
+ static const char *policy[] = {"org.gnome.sysprof3.profile", NULL};
+
+ return g_strdupv ((char **)policy);
+}
+
+typedef struct _Prepare
+{
+ SysprofRecording *recording;
+ SysprofUserSampler *sampler;
+ guint stack_size;
+} Prepare;
+
+static void
+prepare_free (Prepare *prepare)
+{
+ g_clear_object (&prepare->recording);
+ g_clear_object (&prepare->sampler);
+ g_free (prepare);
+}
+
+static void
+_perf_event_open_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GDBusConnection *connection = (GDBusConnection *)object;
+ g_autoptr(DexPromise) promise = user_data;
+ g_autoptr(GUnixFDList) fd_list = NULL;
+ g_autoptr(GVariant) ret = NULL;
+ g_autoptr(GError) error = NULL;
+
+ g_assert (G_IS_DBUS_CONNECTION (connection));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (DEX_IS_PROMISE (promise));
+
+ if ((ret = g_dbus_connection_call_with_unix_fd_list_finish (connection, &fd_list, result, &error)))
+ {
+ g_autofd int fd = -1;
+ int handle;
+
+ g_variant_get (ret, "(h)", &handle);
+
+ if (-1 == (fd = g_unix_fd_list_get (fd_list, handle, &error)))
+ goto failure;
+
+ dex_promise_resolve_fd (promise, g_steal_fd (&fd));
+ return;
+ }
+
+failure:
+ dex_promise_reject (promise, g_steal_pointer (&error));
+}
+
+static int
+_perf_event_open (GDBusConnection *connection,
+ int cpu,
+ guint stack_size,
+ GError **error)
+{
+ g_autoptr(DexPromise) promise = NULL;
+ g_autoptr(GVariant) options = NULL;
+ g_autofd int perf_fd = -1;
+ struct perf_event_attr attr = {0};
+ gboolean with_mmap2 = TRUE;
+ gboolean use_software = FALSE;
+
+ g_assert (G_IS_DBUS_CONNECTION (connection));
+
+try_again:
+ attr.sample_type = PERF_SAMPLE_IP
+ | PERF_SAMPLE_TID
+ | PERF_SAMPLE_IDENTIFIER
+ | PERF_SAMPLE_CALLCHAIN
+ | PERF_SAMPLE_STACK_USER
+ | PERF_SAMPLE_REGS_USER
+ | PERF_SAMPLE_TIME;
+ attr.wakeup_events = N_WAKEUP_EVENTS;
+ attr.disabled = TRUE;
+ attr.mmap = TRUE;
+ attr.mmap2 = with_mmap2;
+ attr.comm = 1;
+ attr.task = 1;
+ attr.exclude_idle = 1;
+ attr.sample_id_all = 1;
+
+#ifdef HAVE_PERF_CLOCKID
+ attr.clockid = sysprof_clock;
+ attr.use_clockid = 1;
+#endif
+
+ attr.sample_stack_user = stack_size;
+ attr.sample_regs_user = SYSPROF_ARCH_PREFERRED_REGS;
+
+ attr.size = sizeof attr;
+
+ if (use_software)
+ {
+ attr.type = PERF_TYPE_SOFTWARE;
+ attr.config = PERF_COUNT_SW_CPU_CLOCK;
+ attr.sample_period = 1000000;
+ }
+ else
+ {
+ attr.type = PERF_TYPE_HARDWARE;
+ attr.config = PERF_COUNT_HW_CPU_CYCLES;
+ attr.sample_period = 1200000;
+ }
+
+ options = _sysprof_perf_event_attr_to_variant (&attr);
+ promise = dex_promise_new ();
+
+ g_dbus_connection_call_with_unix_fd_list (connection,
+ "org.gnome.Sysprof3",
+ "/org/gnome/Sysprof3",
+ "org.gnome.Sysprof3.Service",
+ "PerfEventOpen",
+ g_variant_new ("(@a{sv}iiht)",
+ options,
+ -1,
+ cpu,
+ -1,
+ 0),
+ G_VARIANT_TYPE ("(h)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ G_MAXUINT,
+ NULL,
+ NULL,
+ _perf_event_open_cb,
+ dex_ref (promise));
+
+ if (-1 == (perf_fd = dex_await_fd (dex_ref (promise), error)))
+ {
+ g_clear_pointer (&options, g_variant_unref);
+
+ if (with_mmap2)
+ {
+ with_mmap2 = FALSE;
+ goto try_again;
+ }
+
+ if (use_software == FALSE)
+ {
+ with_mmap2 = TRUE;
+ use_software = TRUE;
+ goto try_again;
+ }
+
+ return -1;
+ }
+
+ return g_steal_fd (&perf_fd);
+}
+
+static void
+call_unwind_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(DexPromise) promise = user_data;
+ g_autoptr(GUnixFDList) out_fd_list = NULL;
+ g_autoptr(GVariant) out_capture_fd = NULL;
+ g_autofd int capture_fd = -1;
+ GError *error = NULL;
+
+ g_assert (IPC_IS_UNWINDER (object));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (DEX_IS_PROMISE (promise));
+
+ if (ipc_unwinder_call_unwind_finish (IPC_UNWINDER (object), &out_capture_fd, &out_fd_list, result, &error) &&
+ -1 != (capture_fd = g_unix_fd_list_get (out_fd_list, g_variant_get_handle (out_capture_fd), &error)))
+ dex_promise_resolve_fd (promise, g_steal_fd (&capture_fd));
+ else
+ dex_promise_reject (promise, error);
+}
+
+static void
+create_unwinder_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(DexPromise) promise = user_data;
+ IpcUnwinder *unwinder;
+ GError *error = NULL;
+
+ if ((unwinder = ipc_unwinder_proxy_new_finish (result, &error)))
+ dex_promise_resolve_object (promise, unwinder);
+ else
+ dex_promise_reject (promise, error);
+}
+
+static IpcUnwinder *
+create_unwinder (GDBusConnection *connection,
+ GError **error)
+{
+ g_autoptr(DexPromise) promise = dex_promise_new ();
+ ipc_unwinder_proxy_new (connection, G_DBUS_PROXY_FLAGS_NONE,
+ "org.gnome.Sysprof3",
+ "/org/gnome/Sysprof3/Unwinder",
+ NULL,
+ create_unwinder_cb,
+ dex_ref (promise));
+ return dex_await_object (dex_ref (promise), error);
+}
+
+static DexFuture *
+sysprof_user_sampler_prepare_fiber (gpointer user_data)
+{
+ Prepare *prepare = user_data;
+ g_autoptr(GDBusConnection) connection = NULL;
+ g_autoptr(GUnixFDList) fd_list = NULL;
+ g_autoptr(GError) error = NULL;
+ GVariantBuilder builder;
+ gboolean all_failed = TRUE;
+ guint n_cpu;
+
+ g_assert (prepare != NULL);
+ g_assert (SYSPROF_IS_RECORDING (prepare->recording));
+ g_assert (SYSPROF_IS_USER_SAMPLER (prepare->sampler));
+
+ if (!(connection = dex_await_object (dex_bus_get (G_BUS_TYPE_SYSTEM), &error)))
+ return dex_future_new_for_error (g_steal_pointer (&error));
+
+ if (!dex_await (_sysprof_recording_add_file (prepare->recording,
+ "/proc/kallsyms",
+ TRUE),
+ &error))
+ {
+ _sysprof_recording_diagnostic (prepare->recording,
+ "Sampler",
+ "Failed to record copy of “kallsyms” to capture: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+
+ n_cpu = g_get_num_processors ();
+ fd_list = g_unix_fd_list_new ();
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(hi)"));
+
+ for (guint i = 0; i < n_cpu; i++)
+ {
+ g_autofd int fd = _perf_event_open (connection, i, prepare->stack_size, &error);
+
+ if (fd == -1)
+ {
+ _sysprof_recording_diagnostic (prepare->recording,
+ "Sampler",
+ "Failed to load Perf event stream for CPU %d with stack size %u: %s",
+ i, prepare->stack_size, error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ int handle = g_unix_fd_list_append (fd_list, fd, &error);
+
+ if (handle == -1)
+ {
+ _sysprof_recording_diagnostic (prepare->recording,
+ "Sampler",
+ "Out of FDs to add to FDList: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ g_array_append_val (prepare->sampler->perf_fds, fd);
+ fd = -1;
+
+ g_variant_builder_add (&builder, "(hi)", handle, i);
+
+ all_failed = FALSE;
+ }
+ }
+ }
+
+ if (!all_failed)
+ {
+ g_autoptr(IpcUnwinder) unwinder = create_unwinder (connection, &error);
+
+ if (unwinder == NULL)
+ {
+ _sysprof_recording_diagnostic (prepare->recording,
+ "Sampler",
+ "Failed to locate unwinder service: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ g_autoptr(DexPromise) promise = dex_promise_new ();
+ int event_fd_handle = g_unix_fd_list_append (fd_list, prepare->sampler->event_fd, NULL);
+ g_autofd int fd = -1;
+
+ ipc_unwinder_call_unwind (unwinder,
+ prepare->stack_size,
+ g_variant_builder_end (&builder),
+ g_variant_new_handle (event_fd_handle),
+ fd_list,
+ NULL,
+ call_unwind_cb,
+ dex_ref (promise));
+
+ fd = dex_await_fd (dex_ref (promise), &error);
+
+ if (fd == -1)
+ {
+ _sysprof_recording_diagnostic (prepare->recording,
+ "Sampler",
+ "Failed to setup user-space unwinder: %s",
+ error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ prepare->sampler->capture_fd = g_steal_fd (&fd);
+ }
+ }
+ }
+
+ g_variant_builder_clear (&builder);
+
+ return dex_future_new_for_boolean (TRUE);
+}
+
+static DexFuture *
+sysprof_user_sampler_prepare (SysprofInstrument *instrument,
+ SysprofRecording *recording)
+{
+ SysprofUserSampler *self = (SysprofUserSampler *)instrument;
+ Prepare *prepare;
+
+ g_assert (SYSPROF_IS_INSTRUMENT (instrument));
+ g_assert (SYSPROF_IS_RECORDING (recording));
+
+ prepare = g_new0 (Prepare, 1);
+ prepare->recording = g_object_ref (recording);
+ prepare->sampler = g_object_ref (self);
+ prepare->stack_size = self->stack_size;
+
+ return dex_scheduler_spawn (NULL, 0,
+ sysprof_user_sampler_prepare_fiber,
+ prepare,
+ (GDestroyNotify)prepare_free);
+}
+
+typedef struct _Record
+{
+ SysprofRecording *recording;
+ SysprofUserSampler *sampler;
+ DexFuture *cancellable;
+} Record;
+
+static void
+record_free (Record *record)
+{
+ g_clear_object (&record->recording);
+ g_clear_object (&record->sampler);
+ dex_clear (&record->cancellable);
+ g_free (record);
+}
+
+static DexFuture *
+sysprof_user_sampler_record_fiber (gpointer user_data)
+{
+ SysprofCaptureWriter *writer;
+ Record *record = user_data;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GSource) muxer_source = NULL;
+ guint64 exiting = 1234;
+
+ g_assert (record != NULL);
+ g_assert (SYSPROF_IS_USER_SAMPLER (record->sampler));
+ g_assert (SYSPROF_IS_RECORDING (record->recording));
+ g_assert (DEX_IS_FUTURE (record->cancellable));
+
+ writer = _sysprof_recording_writer (record->recording);
+
+ sysprof_user_sampler_ioctl (record->sampler, TRUE);
+
+ g_debug ("Staring muxer for capture_fd");
+ muxer_source = sysprof_muxer_source_new (g_steal_fd (&record->sampler->capture_fd), writer);
+ g_source_set_static_name (muxer_source, "[stack-muxer]");
+ g_source_attach (muxer_source, NULL);
+
+ if (!dex_await (dex_ref (record->cancellable), &error))
+ g_debug ("UserSampler shutting down for reason: %s", error->message);
+
+ write (record->sampler->event_fd, &exiting, sizeof exiting);
+
+ g_source_destroy (muxer_source);
+
+ sysprof_user_sampler_ioctl (record->sampler, FALSE);
+
+ return dex_future_new_for_boolean (TRUE);
+}
+
+static DexFuture *
+sysprof_user_sampler_record (SysprofInstrument *instrument,
+ SysprofRecording *recording,
+ GCancellable *cancellable)
+{
+ SysprofUserSampler *self = (SysprofUserSampler *)instrument;
+ Record *record;
+
+ g_assert (SYSPROF_IS_INSTRUMENT (instrument));
+ g_assert (SYSPROF_IS_RECORDING (recording));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ record = g_new0 (Record, 1);
+ record->recording = g_object_ref (recording);
+ record->sampler = g_object_ref (self);
+ record->cancellable = dex_cancellable_new_from_cancellable (cancellable);
+
+ return dex_scheduler_spawn (NULL, 0,
+ sysprof_user_sampler_record_fiber,
+ record,
+ (GDestroyNotify)record_free);
+}
+
+static void
+sysprof_user_sampler_finalize (GObject *object)
+{
+ SysprofUserSampler *self = (SysprofUserSampler *)object;
+
+ g_clear_pointer (&self->perf_fds, g_array_unref);
+
+ g_clear_fd (&self->capture_fd, NULL);
+ g_clear_fd (&self->event_fd, NULL);
+
+ G_OBJECT_CLASS (sysprof_user_sampler_parent_class)->finalize (object);
+}
+
+static void
+sysprof_user_sampler_class_init (SysprofUserSamplerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ SysprofInstrumentClass *instrument_class = SYSPROF_INSTRUMENT_CLASS (klass);
+
+ object_class->finalize = sysprof_user_sampler_finalize;
+
+ instrument_class->list_required_policy = sysprof_user_sampler_list_required_policy;
+ instrument_class->prepare = sysprof_user_sampler_prepare;
+ instrument_class->record = sysprof_user_sampler_record;
+}
+
+static void
+sysprof_user_sampler_init (SysprofUserSampler *self)
+{
+ self->capture_fd = -1;
+ self->event_fd = eventfd (0, EFD_CLOEXEC);
+
+ self->perf_fds = g_array_new (FALSE, FALSE, sizeof (int));
+ g_array_set_clear_func (self->perf_fds, close_fd);
+}
+
+SysprofInstrument *
+sysprof_user_sampler_new (guint stack_size)
+{
+ SysprofUserSampler *self;
+
+ g_return_val_if_fail (stack_size > 0, NULL);
+ g_return_val_if_fail (stack_size % sysprof_getpagesize () == 0, NULL);
+
+ self = g_object_new (SYSPROF_TYPE_USER_SAMPLER, NULL);
+ self->stack_size = stack_size;
+
+ return SYSPROF_INSTRUMENT (self);
+}
diff --git a/src/libsysprof/sysprof-user-sampler.h b/src/libsysprof/sysprof-user-sampler.h
new file mode 100644
index 00000000..2d9e56b5
--- /dev/null
+++ b/src/libsysprof/sysprof-user-sampler.h
@@ -0,0 +1,43 @@
+/*
+ * sysprof-user-sampler.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 "sysprof-instrument.h"
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_USER_SAMPLER (sysprof_user_sampler_get_type())
+#define SYSPROF_IS_USER_SAMPLER(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj, SYSPROF_TYPE_USER_SAMPLER)
+#define SYSPROF_USER_SAMPLER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, SYSPROF_TYPE_USER_SAMPLER, SysprofUserSampler)
+#define SYSPROF_USER_SAMPLER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST(klass, SYSPROF_TYPE_USER_SAMPLER, SysprofUserSamplerClass)
+
+typedef struct _SysprofUserSampler SysprofUserSampler;
+typedef struct _SysprofUserSamplerClass SysprofUserSamplerClass;
+
+SYSPROF_AVAILABLE_IN_ALL
+GType sysprof_user_sampler_get_type (void) G_GNUC_CONST;
+SYSPROF_AVAILABLE_IN_ALL
+SysprofInstrument *sysprof_user_sampler_new (guint stack_size);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofUserSampler, g_object_unref)
+
+G_END_DECLS
diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h
index d30f9fd4..514e332b 100644
--- a/src/libsysprof/sysprof.h
+++ b/src/libsysprof/sysprof.h
@@ -89,6 +89,7 @@ G_BEGIN_DECLS
# include "sysprof-time-span.h"
# include "sysprof-tracefd-consumer.h"
# include "sysprof-tracer.h"
+# include "sysprof-user-sampler.h"
#undef SYSPROF_INSIDE
G_END_DECLS
--
2.45.2

View File

@ -0,0 +1,60 @@
From da9f2db255c1f0c63c10993eb62db567265ced6e Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Sun, 3 Nov 2024 10:54:57 -0800
Subject: [PATCH 17/18] sysprof-cli: add support for live unwinding
This allows you to specify --stack-size=(multiple_of_page_size) to unwind
from captured stack contents. It will use the new SysprofUserSampler to
unwind stack traces via sysprof-live-unwinder.
---
src/sysprof-cli/sysprof-cli.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/sysprof-cli/sysprof-cli.c b/src/sysprof-cli/sysprof-cli.c
index e00bd6b1..1c9ce928 100644
--- a/src/sysprof-cli/sysprof-cli.c
+++ b/src/sysprof-cli/sysprof-cli.c
@@ -303,6 +303,7 @@ main (int argc,
gboolean scheduler_details = FALSE;
gboolean system_bus = FALSE;
gboolean session_bus = FALSE;
+ int stack_size = 0;
int pid = -1;
int fd;
int flags;
@@ -335,6 +336,7 @@ main (int argc,
{ "version", 0, 0, G_OPTION_ARG_NONE, &version, N_("Print the sysprof-cli version and exit") },
{ "buffer-size", 0, 0, G_OPTION_ARG_INT, &n_buffer_pages, N_("The size of the buffer in pages (1 = 1 page)") },
{ "monitor-bus", 0, 0, G_OPTION_ARG_STRING_ARRAY, &monitor_bus, N_("Additional D-Bus address to monitor") },
+ { "stack-size", 0, 0, G_OPTION_ARG_INT, &stack_size, N_("Stack size to copy for unwinding in user-space") },
{ NULL }
};
@@ -379,6 +381,10 @@ Examples:\n\
\n\
# Merge multiple syscap files into one\n\
sysprof-cli --merge a.syscap b.syscap > c.syscap\n\
+\n\
+ # Unwind by capturing stack/register contents instead of frame-pointers\n\
+ # where the stack-size is a multiple of page-size\n\
+ sysprof-cli --stack-size=8192\n\
"));
if (!g_option_context_parse (context, &argc, &argv, &error))
@@ -533,7 +539,12 @@ Examples:\n\
sysprof_profiler_add_instrument (profiler, sysprof_system_logs_new ());
if (!no_perf)
- sysprof_profiler_add_instrument (profiler, sysprof_sampler_new ());
+ {
+ if (stack_size == 0)
+ sysprof_profiler_add_instrument (profiler, sysprof_sampler_new ());
+ else
+ sysprof_profiler_add_instrument (profiler, sysprof_user_sampler_new (stack_size));
+ }
if (!no_disk)
sysprof_profiler_add_instrument (profiler, sysprof_disk_usage_new ());
--
2.45.2

View File

@ -0,0 +1,506 @@
From 927b13bb407105517783ab1d03c025fecf1d59d9 Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Sun, 3 Nov 2024 10:57:58 -0800
Subject: [PATCH 18/18] 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 @@
</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>
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 <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;
+}
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 <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
--
2.45.2

View File

@ -0,0 +1,37 @@
From 99f807240e4be653a122b9e443cd38782962bc59 Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Wed, 6 Nov 2024 13:09:31 -0800
Subject: [PATCH] sysprof: default stack-capturing as enabled
This is for CentOS/RHEL only as other distributions have frame-pointers.
---
src/sysprof/sysprof-recording-template.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/sysprof/sysprof-recording-template.c b/src/sysprof/sysprof-recording-template.c
index ee4d4f01..b3018763 100644
--- a/src/sysprof/sysprof-recording-template.c
+++ b/src/sysprof/sysprof-recording-template.c
@@ -446,7 +446,8 @@ sysprof_recording_template_class_init (SysprofRecordingTemplateClass *klass)
properties[PROP_USER_STACKS] =
g_param_spec_boolean ("user-stacks", NULL, NULL,
- FALSE,
+ /* Default to stack capturing in CentOS/RHEL */
+ TRUE,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
properties[PROP_STACK_SIZE] =
@@ -473,6 +474,9 @@ sysprof_recording_template_init (SysprofRecordingTemplate *self)
self->command_line = g_strdup ("");
self->cwd = g_strdup("");
self->stack_size = DEFAULT_STACK_SIZE;
+
+ /* Default to stack capturing in CentOS/RHEL */
+ self->user_stacks = TRUE;
}
SysprofRecordingTemplate *
--
2.45.2

View File

@ -0,0 +1,38 @@
From 44c780aed1d688695c3f15c757e4e9f14cb4214a Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Wed, 6 Nov 2024 13:33:34 -0800
Subject: [PATCH] live-unwinder: disable test in build
It requires newer libdex features we don't need so best to avoid back
porting those too.
---
meson.build | 2 +-
src/sysprof-live-unwinder/meson.build | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/meson.build b/meson.build
index 9c37ebc4..462782d9 100644
--- a/meson.build
+++ b/meson.build
@@ -45,7 +45,7 @@ need_libsysprof = (need_gtk or
get_option('tools') or
get_option('tests'))
-dex_req = '0.9'
+dex_req = '0.8'
glib_req = '2.76.0'
gtk_req = '4.15'
polkit_req = '0.105'
diff --git a/src/sysprof-live-unwinder/meson.build b/src/sysprof-live-unwinder/meson.build
index 8cef7106..af797490 100644
--- a/src/sysprof-live-unwinder/meson.build
+++ b/src/sysprof-live-unwinder/meson.build
@@ -16,4 +16,4 @@ sysprof_live_unwinder = executable('sysprof-live-unwinder', sysprof_live_unwinde
install_dir: pkglibexecdir,
)
-subdir('tests')
+# subdir('tests')
--
2.45.2

View File

@ -0,0 +1,227 @@
From 781d438e5e3d4189d8a1970707983ccc6897175c Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Wed, 6 Nov 2024 15:09:13 -0800
Subject: [PATCH] sysprof-user-sampler: implement await for FDs
This allows us to not need libdex 0.9+ for use by the sampler.
---
src/libsysprof/meson.build | 1 +
src/libsysprof/sysprof-fd-private.h | 39 ++++++++++++++++
src/libsysprof/sysprof-fd.c | 67 +++++++++++++++++++++++++++
src/libsysprof/sysprof-user-sampler.c | 33 +++++++++++--
4 files changed, 136 insertions(+), 4 deletions(-)
create mode 100644 src/libsysprof/sysprof-fd-private.h
create mode 100644 src/libsysprof/sysprof-fd.c
diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build
index e49c3a37..2ae977ca 100644
--- a/src/libsysprof/meson.build
+++ b/src/libsysprof/meson.build
@@ -144,6 +144,7 @@ libsysprof_private_sources = [
'sysprof-document-symbols.c',
'sysprof-elf-loader.c',
'sysprof-elf.c',
+ 'sysprof-fd.c',
'sysprof-leak-detector.c',
'sysprof-maps-parser.c',
'sysprof-mount-device.c',
diff --git a/src/libsysprof/sysprof-fd-private.h b/src/libsysprof/sysprof-fd-private.h
new file mode 100644
index 00000000..1d4dfabc
--- /dev/null
+++ b/src/libsysprof/sysprof-fd-private.h
@@ -0,0 +1,39 @@
+/* sysprof-fd-private.h
+ *
+ * Copyright 2024 Christian Hergert <chergert@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser 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: LGPL-2.1-or-later
+ */
+
+#pragma once
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define SYSPROF_TYPE_FD (sysprof_fd_get_type())
+
+typedef struct _SysprofFD SysprofFD;
+
+GType sysprof_fd_get_type (void) G_GNUC_CONST;
+int sysprof_fd_peek (const SysprofFD *fd);
+int sysprof_fd_steal (SysprofFD *fd);
+SysprofFD *sysprof_fd_dup (const SysprofFD *fd);
+void sysprof_fd_free (SysprofFD *fd);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofFD, sysprof_fd_free)
+
+G_END_DECLS
diff --git a/src/libsysprof/sysprof-fd.c b/src/libsysprof/sysprof-fd.c
new file mode 100644
index 00000000..5e34f8d9
--- /dev/null
+++ b/src/libsysprof/sysprof-fd.c
@@ -0,0 +1,67 @@
+/* sysprof-fd.c
+ *
+ * Copyright 2024 Christian Hergert <chergert@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser 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: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+
+#include "sysprof-fd-private.h"
+
+int
+sysprof_fd_peek (const SysprofFD *fd)
+{
+ if (fd == NULL)
+ return -1;
+
+ return *(int *)fd;
+}
+
+int
+sysprof_fd_steal (SysprofFD *fd)
+{
+ if (fd == NULL)
+ return -1;
+
+ return g_steal_fd ((int *)fd);
+}
+
+void
+sysprof_fd_free (SysprofFD *fd)
+{
+ int real = sysprof_fd_steal (fd);
+ if (real != -1)
+ close (real);
+ g_free (fd);
+}
+
+SysprofFD *
+sysprof_fd_dup (const SysprofFD *fd)
+{
+ int real = sysprof_fd_peek (fd);
+
+ if (real == -1)
+ return NULL;
+
+ real = dup (real);
+
+ return g_memdup2 (&real, sizeof real);
+}
+
+G_DEFINE_BOXED_TYPE (SysprofFD, sysprof_fd, sysprof_fd_dup, sysprof_fd_free)
diff --git a/src/libsysprof/sysprof-user-sampler.c b/src/libsysprof/sysprof-user-sampler.c
index 0e3afeae..1ae3ea27 100644
--- a/src/libsysprof/sysprof-user-sampler.c
+++ b/src/libsysprof/sysprof-user-sampler.c
@@ -32,6 +32,7 @@
#include "sysprof-recording-private.h"
#include "sysprof-user-sampler.h"
#include "sysprof-muxer-source.h"
+#include "sysprof-fd-private.h"
#include "ipc-unwinder.h"
@@ -81,6 +82,30 @@ close_fd (gpointer data)
}
}
+static void
+promise_resolve_fd (DexPromise *promise,
+ int fd)
+{
+ GValue gvalue = {SYSPROF_TYPE_FD, {{.v_pointer = &fd}, {.v_int = 0}}};
+ dex_promise_resolve (promise, &gvalue);
+}
+
+static int
+await_fd (DexFuture *future,
+ GError **error)
+{
+ SysprofFD *fd = dex_await_boxed (future, error);
+ int ret = -1;
+
+ if (fd != NULL)
+ {
+ ret = sysprof_fd_steal (fd);
+ sysprof_fd_free (fd);
+ }
+
+ return ret;
+}
+
static void
sysprof_user_sampler_ioctl (SysprofUserSampler *self,
gboolean enable)
@@ -145,7 +170,7 @@ _perf_event_open_cb (GObject *object,
if (-1 == (fd = g_unix_fd_list_get (fd_list, handle, &error)))
goto failure;
- dex_promise_resolve_fd (promise, g_steal_fd (&fd));
+ promise_resolve_fd (promise, g_steal_fd (&fd));
return;
}
@@ -230,7 +255,7 @@ try_again:
_perf_event_open_cb,
dex_ref (promise));
- if (-1 == (perf_fd = dex_await_fd (dex_ref (promise), error)))
+ if (-1 == (perf_fd = await_fd (dex_ref (promise), error)))
{
g_clear_pointer (&options, g_variant_unref);
@@ -270,7 +295,7 @@ call_unwind_cb (GObject *object,
if (ipc_unwinder_call_unwind_finish (IPC_UNWINDER (object), &out_capture_fd, &out_fd_list, result, &error) &&
-1 != (capture_fd = g_unix_fd_list_get (out_fd_list, g_variant_get_handle (out_capture_fd), &error)))
- dex_promise_resolve_fd (promise, g_steal_fd (&capture_fd));
+ promise_resolve_fd (promise, g_steal_fd (&capture_fd));
else
dex_promise_reject (promise, error);
}
@@ -402,7 +427,7 @@ sysprof_user_sampler_prepare_fiber (gpointer user_data)
call_unwind_cb,
dex_ref (promise));
- fd = dex_await_fd (dex_ref (promise), &error);
+ fd = await_fd (dex_ref (promise), &error);
if (fd == -1)
{
--
2.45.2

View File

@ -14,11 +14,34 @@ Summary: A system-wide Linux profiler
License: GPL-2.0-or-later AND GPL-3.0-or-later AND CC-BY-SA-4.0 AND BSD-2-Clause-Patent
URL: http://www.sysprof.com
Source0: https://download.gnome.org/sources/sysprof/46/sysprof-%{tarball_version}.tar.xz
Source0: https://download.gnome.org/sources/sysprof/47/sysprof-%{tarball_version}.tar.xz
%if 0%{?bundled_libunwind}
Source1: https://github.com/libunwind/libunwind/releases/download/v%{libunwind_version}/libunwind-%{libunwind_version}.tar.gz
%endif
# Backports of debuginfod and sysprof-live-unwinder from GNOME 48
Patch: 0001-sysprof-update-to-AdwSpinner.patch
Patch: 0002-libsysprof-elf-do-not-allow-setting-self-as-debug-li.patch
Patch: 0003-libsysprof-elf-do-not-generate-fallback-names.patch
Patch: 0004-sysprof-add-SysprofDocumentTask-abstraction.patch
Patch: 0005-libsysprof-add-setup-hooks-for-symbolizers.patch
Patch: 0006-libsysprof-hoist-fallback-symbol-creation.patch
Patch: 0007-libsysprof-add-debuginfod-symbolizer.patch
Patch: 0008-libsysprof-ensure-access-to-process-info.patch
Patch: 0009-libsysprof-fix-building-with-Ddebuginfod-auto.patch
Patch: 0010-libsysprof-return-NULL-instance-unless-debuginfod-wo.patch
Patch: 0011-build-always-build-debuginfod-symbolizer.patch
Patch: 0012-libsysprof-remove-unnecessary-address-calculation.patch
Patch: 0013-libsysprof-add-muxer-GSource.patch
Patch: 0014-libsysprof-add-support-for-stack-regs-options-in-att.patch
Patch: 0015-sysprofd-add-support-for-unwinding-without-frame-poi.patch
Patch: 0016-libsysprof-add-SysprofUserSampler-for-live-unwinding.patch
Patch: 0017-sysprof-cli-add-support-for-live-unwinding.patch
Patch: 0018-sysprof-add-UI-for-live-unwinding.patch
Patch: 0019-sysprof-default-stack-capturing-as-enabled.patch
Patch: 0020-live-unwinder-disable-test-in-build.patch
Patch: 0021-sysprof-user-sampler-implement-await-for-FDs.patch
BuildRequires: gcc
BuildRequires: gcc-c++
BuildRequires: gettext
@ -36,6 +59,8 @@ BuildRequires: pkgconfig(libpanel-1)
BuildRequires: pkgconfig(libsystemd)
BuildRequires: pkgconfig(polkit-gobject-1)
BuildRequires: pkgconfig(systemd)
BuildRequires: pkgconfig(libdw)
BuildRequires: pkgconfig(libdebuginfod)
BuildRequires: /usr/bin/appstream-util
BuildRequires: /usr/bin/desktop-file-validate
@ -110,6 +135,7 @@ developing applications that use %{name}.
%setup -b 1 -n libunwind-%{libunwind_version}
%endif
%setup -n sysprof-%{tarball_version}
%autopatch -p1
%build
@ -171,6 +197,7 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop
%license COPYING
%{_bindir}/sysprof-cli
%{_libexecdir}/sysprofd
%{_libexecdir}/sysprof-live-unwinder
%{_datadir}/dbus-1/system.d/org.gnome.Sysprof3.conf
%{_datadir}/dbus-1/system-services/org.gnome.Sysprof3.service
%{_datadir}/polkit-1/actions/org.gnome.sysprof3.policy