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 6cb55bf2..c6a67f68 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-capture/sysprof-version-macros.h b/src/libsysprof-capture/sysprof-version-macros.h index 67dac287..5bfb4a25 100644 --- a/src/libsysprof-capture/sysprof-version-macros.h +++ b/src/libsysprof-capture/sysprof-version-macros.h @@ -91,6 +91,7 @@ #define SYSPROF_VERSION_3_38 (SYSPROF_ENCODE_VERSION (3, 38, 0)) #define SYSPROF_VERSION_3_40 (SYSPROF_ENCODE_VERSION (3, 40, 0)) #define SYSPROF_VERSION_3_46 (SYSPROF_ENCODE_VERSION (3, 46, 0)) +#define SYSPROF_VERSION_48 (SYSPROF_ENCODE_VERSION (48, 0, 0)) #if (SYSPROF_MINOR_VERSION == 99) # define SYSPROF_VERSION_CUR_STABLE (SYSPROF_ENCODE_VERSION (SYSPROF_MAJOR_VERSION + 1, 0, 0)) @@ -232,3 +233,17 @@ #else # define SYSPROF_AVAILABLE_IN_3_46 _SYSPROF_EXTERN #endif + +#if SYSPROF_VERSION_MIN_REQUIRED >= SYSPROF_VERSION_48 +# define SYSPROF_DEPRECATED_IN_48 SYSPROF_DEPRECATED +# define SYSPROF_DEPRECATED_IN_48_FOR(f) SYSPROF_DEPRECATED_FOR(f) +#else +# define SYSPROF_DEPRECATED_IN_48 _SYSPROF_EXTERN +# define SYSPROF_DEPRECATED_IN_48_FOR(f) _SYSPROF_EXTERN +#endif + +#if SYSPROF_VERSION_MAX_ALLOWED < SYSPROF_VERSION_48 +# define SYSPROF_AVAILABLE_IN_48 SYSPROF_UNAVAILABLE(48, 0) +#else +# define SYSPROF_AVAILABLE_IN_48 _SYSPROF_EXTERN +#endif diff --git a/src/libsysprof/meson.build b/src/libsysprof/meson.build index fd53ee11..b4e58078 100644 --- a/src/libsysprof/meson.build +++ b/src/libsysprof/meson.build @@ -32,6 +32,7 @@ libsysprof_public_sources = [ 'sysprof-document-overlay.c', 'sysprof-document-process.c', 'sysprof-document-sample.c', + 'sysprof-document-task.c', 'sysprof-document-traceable.c', 'sysprof-document.c', 'sysprof-elf-symbolizer.c', @@ -96,6 +97,7 @@ libsysprof_public_headers = [ 'sysprof-document-overlay.h', 'sysprof-document-process.h', 'sysprof-document-sample.h', + 'sysprof-document-task.h', 'sysprof-document-traceable.h', 'sysprof-document.h', 'sysprof-elf-symbolizer.h', @@ -151,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 @@ -190,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 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include +#include + +#include + +#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 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "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_48 +GType sysprof_debuginfod_symbolizer_get_type (void) G_GNUC_CONST; +SYSPROF_AVAILABLE_IN_48 +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 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#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 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include +#include + +#include + +#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-private.h b/src/libsysprof/sysprof-document-loader-private.h new file mode 100644 index 00000000..f7982489 --- /dev/null +++ b/src/libsysprof/sysprof-document-loader-private.h @@ -0,0 +1,34 @@ +/* + * sysprof-document-loader-private.h + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-document-loader.h" +#include "sysprof-document-task.h" + +G_BEGIN_DECLS + +void _sysprof_document_loader_add_task (SysprofDocumentLoader *self, + SysprofDocumentTask *task); +void _sysprof_document_loader_remove_task (SysprofDocumentLoader *self, + SysprofDocumentTask *task); + +G_END_DECLS diff --git a/src/libsysprof/sysprof-document-loader.c b/src/libsysprof/sysprof-document-loader.c index 6622f325..00b525df 100644 --- a/src/libsysprof/sysprof-document-loader.c +++ b/src/libsysprof/sysprof-document-loader.c @@ -27,18 +27,21 @@ #include #include "sysprof-bundled-symbolizer.h" +#include "sysprof-debuginfod-symbolizer.h" #include "sysprof-document-bitset-index-private.h" -#include "sysprof-document-loader.h" +#include "sysprof-document-loader-private.h" #include "sysprof-document-private.h" #include "sysprof-elf-symbolizer.h" #include "sysprof-jitmap-symbolizer.h" #include "sysprof-kallsyms-symbolizer.h" #include "sysprof-multi-symbolizer.h" +#include "sysprof-symbolizer-private.h" struct _SysprofDocumentLoader { GObject parent_instance; GMutex mutex; + GListStore *tasks; SysprofSymbolizer *symbolizer; char *filename; char *message; @@ -53,6 +56,7 @@ enum { PROP_FRACTION, PROP_MESSAGE, PROP_SYMBOLIZER, + PROP_TASKS, N_PROPS }; @@ -192,6 +196,7 @@ static void set_default_symbolizer (SysprofDocumentLoader *self) { g_autoptr(SysprofMultiSymbolizer) multi = NULL; + g_autoptr(GError) error = NULL; g_assert (SYSPROF_IS_DOCUMENT_LOADER (self)); @@ -202,9 +207,31 @@ set_default_symbolizer (SysprofDocumentLoader *self) sysprof_multi_symbolizer_take (multi, sysprof_kallsyms_symbolizer_new ()); sysprof_multi_symbolizer_take (multi, sysprof_elf_symbolizer_new ()); sysprof_multi_symbolizer_take (multi, sysprof_jitmap_symbolizer_new ()); + +#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)); } +static void +sysprof_document_loader_dispose (GObject *object) +{ + SysprofDocumentLoader *self = (SysprofDocumentLoader *)object; + + g_list_store_remove_all (self->tasks); + + G_OBJECT_CLASS (sysprof_document_loader_parent_class)->dispose (object); +} + static void sysprof_document_loader_finalize (GObject *object) { @@ -212,6 +239,7 @@ sysprof_document_loader_finalize (GObject *object) g_clear_handle_id (&self->notify_source, g_source_remove); g_clear_object (&self->symbolizer); + g_clear_object (&self->tasks); g_clear_pointer (&self->filename, g_free); g_clear_pointer (&self->message, g_free); g_clear_fd (&self->fd, NULL); @@ -242,6 +270,10 @@ sysprof_document_loader_get_property (GObject *object, g_value_set_object (value, sysprof_document_loader_get_symbolizer (self)); break; + case PROP_TASKS: + g_value_take_object (value, sysprof_document_loader_list_tasks (self)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -271,6 +303,7 @@ sysprof_document_loader_class_init (SysprofDocumentLoaderClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->dispose = sysprof_document_loader_dispose; object_class->finalize = sysprof_document_loader_finalize; object_class->get_property = sysprof_document_loader_get_property; object_class->set_property = sysprof_document_loader_set_property; @@ -290,6 +323,11 @@ sysprof_document_loader_class_init (SysprofDocumentLoaderClass *klass) SYSPROF_TYPE_SYMBOLIZER, (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + properties[PROP_TASKS] = + g_param_spec_object ("tasks", NULL, NULL, + G_TYPE_LIST_MODEL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_properties (object_class, N_PROPS, properties); g_type_ensure (SYSPROF_TYPE_DOCUMENT); @@ -302,6 +340,7 @@ sysprof_document_loader_init (SysprofDocumentLoader *self) g_mutex_init (&self->mutex); self->fd = -1; + self->tasks = g_list_store_new (SYSPROF_TYPE_DOCUMENT_TASK); set_default_symbolizer (self); } @@ -546,6 +585,8 @@ sysprof_document_loader_load_async (SysprofDocumentLoader *self, set_progress (0., _("Loading document"), self); + _sysprof_symbolizer_setup (self->symbolizer, self); + if (self->fd != -1) mapped_file_new_from_fd_async (self->fd, cancellable, @@ -657,3 +698,94 @@ sysprof_document_loader_load (SysprofDocumentLoader *self, return state.document; } + +typedef struct _TaskOp +{ + SysprofDocumentLoader *loader; + SysprofDocumentTask *task; + guint remove : 1; +} TaskOp; + +static void +_g_list_store_remove (GListStore *store, + gpointer instance) +{ + GListModel *model = G_LIST_MODEL (store); + guint n_items = g_list_model_get_n_items (model); + + for (guint i = 0; i < n_items; i++) + { + g_autoptr(GObject) element = g_list_model_get_item (model, i); + + if (element == instance) + { + g_list_store_remove (store, i); + return; + } + } +} + +static gboolean +task_op_run (gpointer data) +{ + TaskOp *op = data; + + if (op->remove) + _g_list_store_remove (op->loader->tasks, op->task); + else + g_list_store_append (op->loader->tasks, op->task); + + g_clear_object (&op->loader); + g_clear_object (&op->task); + g_free (op); + + return G_SOURCE_REMOVE; +} + +static TaskOp * +task_op_new (SysprofDocumentLoader *loader, + SysprofDocumentTask *task, + gboolean remove) +{ + TaskOp op = { + g_object_ref (loader), + g_object_ref (task), + !!remove + }; + + return g_memdup2 (&op, sizeof op); +} + +void +_sysprof_document_loader_add_task (SysprofDocumentLoader *self, + SysprofDocumentTask *task) +{ + g_return_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self)); + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (task)); + + g_idle_add (task_op_run, task_op_new (self, task, FALSE)); +} + +void +_sysprof_document_loader_remove_task (SysprofDocumentLoader *self, + SysprofDocumentTask *task) +{ + g_return_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self)); + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (task)); + + g_idle_add (task_op_run, task_op_new (self, task, TRUE)); +} + +/** + * sysprof_document_loader_list_tasks: + * @self: a #SysprofDocumentLoader + * + * Returns: (transfer full): a #GListModel of #SysprofDocumentTask. + */ +GListModel * +sysprof_document_loader_list_tasks (SysprofDocumentLoader *self) +{ + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (self), NULL); + + return g_object_ref (G_LIST_MODEL (self->tasks)); +} diff --git a/src/libsysprof/sysprof-document-loader.h b/src/libsysprof/sysprof-document-loader.h index 289dae30..1a1ea7da 100644 --- a/src/libsysprof/sysprof-document-loader.h +++ b/src/libsysprof/sysprof-document-loader.h @@ -48,6 +48,8 @@ SYSPROF_AVAILABLE_IN_ALL double sysprof_document_loader_get_fraction (SysprofDocumentLoader *self); SYSPROF_AVAILABLE_IN_ALL const char *sysprof_document_loader_get_message (SysprofDocumentLoader *self); +SYSPROF_AVAILABLE_IN_48 +GListModel *sysprof_document_loader_list_tasks (SysprofDocumentLoader *self); SYSPROF_AVAILABLE_IN_ALL SysprofDocument *sysprof_document_loader_load (SysprofDocumentLoader *self, GCancellable *cancellable, 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-document-task-private.h b/src/libsysprof/sysprof-document-task-private.h new file mode 100644 index 00000000..3590b234 --- /dev/null +++ b/src/libsysprof/sysprof-document-task-private.h @@ -0,0 +1,53 @@ +/* + * sysprof-document-task-private.h + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-document-loader.h" +#include "sysprof-document-task.h" + +G_BEGIN_DECLS + +typedef struct _SysprofDocumentTask SysprofDocumentTaskScope; + +struct _SysprofDocumentTaskClass +{ + GObjectClass parent_class; + + void (*cancel) (SysprofDocumentTask *self); +}; + +GCancellable *_sysprof_document_task_get_cancellable (SysprofDocumentTask *self); +void _sysprof_document_task_set_title (SysprofDocumentTask *self, + const char *title); +void _sysprof_document_task_take_message (SysprofDocumentTask *self, + char *message); +void _sysprof_document_task_set_progress (SysprofDocumentTask *self, + double progress); +SysprofDocumentTaskScope *_sysprof_document_task_register (SysprofDocumentTask *self, + SysprofDocumentLoader *loader); +void _sysprof_document_task_unregister (SysprofDocumentTask *self); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofDocumentTaskScope, _sysprof_document_task_unregister) + +G_END_DECLS diff --git a/src/libsysprof/sysprof-document-task.c b/src/libsysprof/sysprof-document-task.c new file mode 100644 index 00000000..a8bfb30a --- /dev/null +++ b/src/libsysprof/sysprof-document-task.c @@ -0,0 +1,327 @@ +/* + * sysprof-document-task.c + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include "sysprof-document-loader-private.h" +#include "sysprof-document-task-private.h" + +typedef struct +{ + GMutex mutex; + char *message; + char *title; + double progress; + GCancellable *cancellable; + guint notify_source; + GWeakRef loader_wr; +} SysprofDocumentTaskPrivate; + +enum { + PROP_0, + PROP_CANCELLED, + PROP_MESSAGE, + PROP_PROGRESS, + PROP_TITLE, + N_PROPS +}; + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (SysprofDocumentTask, sysprof_document_task, G_TYPE_OBJECT) + +static GParamSpec *properties[N_PROPS]; + +static void +sysprof_document_task_finalize (GObject *object) +{ + SysprofDocumentTask *self = (SysprofDocumentTask *)object; + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_mutex_clear (&priv->mutex); + g_clear_handle_id (&priv->notify_source, g_source_remove); + g_clear_pointer (&priv->message, g_free); + g_clear_pointer (&priv->title, g_free); + g_clear_object (&priv->cancellable); + g_weak_ref_clear (&priv->loader_wr); + + G_OBJECT_CLASS (sysprof_document_task_parent_class)->finalize (object); +} + +static void +sysprof_document_task_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofDocumentTask *self = SYSPROF_DOCUMENT_TASK (object); + + switch (prop_id) + { + case PROP_CANCELLED: + g_value_set_boolean (value, sysprof_document_task_is_cancelled (self)); + break; + + case PROP_MESSAGE: + g_value_take_string (value, sysprof_document_task_dup_message (self)); + break; + + case PROP_PROGRESS: + g_value_set_double (value, sysprof_document_task_get_progress (self)); + break; + + case PROP_TITLE: + g_value_take_string (value, sysprof_document_task_dup_title (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_document_task_class_init (SysprofDocumentTaskClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_document_task_finalize; + object_class->get_property = sysprof_document_task_get_property; + + properties[PROP_CANCELLED] = + g_param_spec_boolean ("cancelled", NULL, NULL, + FALSE, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_MESSAGE] = + g_param_spec_string ("message", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_PROGRESS] = + g_param_spec_double ("progress", NULL, NULL, + 0., 1., 0., + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + properties[PROP_TITLE] = + g_param_spec_string ("title", NULL, NULL, + NULL, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_document_task_init (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_weak_ref_init (&priv->loader_wr, NULL); + + priv->cancellable = g_cancellable_new (); +} + +static gboolean +notify_in_idle_cb (gpointer data) +{ + SysprofDocumentTask *self = data; + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_assert (SYSPROF_IS_DOCUMENT_TASK (self)); + + g_mutex_lock (&priv->mutex); + priv->notify_source = 0; + g_mutex_unlock (&priv->mutex); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CANCELLED]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MESSAGE]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PROGRESS]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TITLE]); + + return G_SOURCE_REMOVE; +} + +static void +notify_in_idle_locked (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_assert (SYSPROF_IS_DOCUMENT_TASK (self)); + + if (priv->notify_source == 0) + priv->notify_source = g_idle_add_full (G_PRIORITY_LOW, + notify_in_idle_cb, + g_object_ref (self), + g_object_unref); +} + +char * +sysprof_document_task_dup_message (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + char *ret; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TASK (self), NULL); + + g_mutex_lock (&priv->mutex); + ret = g_strdup (priv->message); + g_mutex_unlock (&priv->mutex); + + return ret; +} + +char * +sysprof_document_task_dup_title (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + char *ret; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TASK (self), NULL); + + g_mutex_lock (&priv->mutex); + ret = g_strdup (priv->title); + g_mutex_unlock (&priv->mutex); + + return ret; +} + +void +_sysprof_document_task_take_message (SysprofDocumentTask *self, + char *message) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (self)); + + g_mutex_lock (&priv->mutex); + g_free (priv->message); + priv->message = message; + notify_in_idle_locked (self); + g_mutex_unlock (&priv->mutex); +} + +double +sysprof_document_task_get_progress (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + double ret; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TASK (self), 0); + + g_mutex_lock (&priv->mutex); + ret = priv->progress; + g_mutex_unlock (&priv->mutex); + + return ret; +} + +void +_sysprof_document_task_set_progress (SysprofDocumentTask *self, + double progress) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (self)); + + progress = CLAMP (progress, 0., 1.); + + g_mutex_lock (&priv->mutex); + priv->progress = progress; + notify_in_idle_locked (self); + g_mutex_unlock (&priv->mutex); +} + +void +_sysprof_document_task_set_title (SysprofDocumentTask *self, + const char *title) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (self)); + + g_mutex_lock (&priv->mutex); + if (g_set_str (&priv->title, title)) + notify_in_idle_locked (self); + g_mutex_unlock (&priv->mutex); +} + +gboolean +sysprof_document_task_is_cancelled (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + gboolean ret; + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TASK (self), FALSE); + + g_mutex_lock (&priv->mutex); + ret = g_cancellable_is_cancelled (priv->cancellable); + g_mutex_unlock (&priv->mutex); + + return ret; +} + +GCancellable * +_sysprof_document_task_get_cancellable (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TASK (self), NULL); + + return priv->cancellable; +} + +SysprofDocumentTaskScope * +_sysprof_document_task_register (SysprofDocumentTask *self, + SysprofDocumentLoader *loader) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_TASK (self), NULL); + g_return_val_if_fail (SYSPROF_IS_DOCUMENT_LOADER (loader), NULL); + + g_weak_ref_set (&priv->loader_wr, loader); + + _sysprof_document_loader_add_task (loader, self); + + return self; +} + +void +_sysprof_document_task_unregister (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + g_autoptr(SysprofDocumentLoader) loader = NULL; + + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (self)); + + if ((loader = g_weak_ref_get (&priv->loader_wr))) + _sysprof_document_loader_remove_task (loader, self); +} + +void +sysprof_document_task_cancel (SysprofDocumentTask *self) +{ + SysprofDocumentTaskPrivate *priv = sysprof_document_task_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_DOCUMENT_TASK (self)); + + g_cancellable_cancel (priv->cancellable); + + if (SYSPROF_DOCUMENT_TASK_GET_CLASS (self)->cancel) + SYSPROF_DOCUMENT_TASK_GET_CLASS (self)->cancel (self); +} diff --git a/src/libsysprof/sysprof-document-task.h b/src/libsysprof/sysprof-document-task.h new file mode 100644 index 00000000..1f0be928 --- /dev/null +++ b/src/libsysprof/sysprof-document-task.h @@ -0,0 +1,46 @@ +/* + * sysprof-document-task.h + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +#include "sysprof-version-macros.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_DOCUMENT_TASK (sysprof_document_task_get_type()) + +SYSPROF_AVAILABLE_IN_48 +G_DECLARE_DERIVABLE_TYPE (SysprofDocumentTask, sysprof_document_task, SYSPROF, DOCUMENT_TASK, GObject) + +SYSPROF_AVAILABLE_IN_48 +double sysprof_document_task_get_progress (SysprofDocumentTask *self); +SYSPROF_AVAILABLE_IN_48 +char *sysprof_document_task_dup_message (SysprofDocumentTask *self); +SYSPROF_AVAILABLE_IN_48 +char *sysprof_document_task_dup_title (SysprofDocumentTask *self); +SYSPROF_AVAILABLE_IN_48 +gboolean sysprof_document_task_is_cancelled (SysprofDocumentTask *self); +SYSPROF_AVAILABLE_IN_48 +void sysprof_document_task_cancel (SysprofDocumentTask *self); + +G_END_DECLS 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 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); +} diff --git a/src/libsysprof/sysprof.h b/src/libsysprof/sysprof.h index 529386c8..c2176619 100644 --- a/src/libsysprof/sysprof.h +++ b/src/libsysprof/sysprof.h @@ -56,6 +56,7 @@ G_BEGIN_DECLS # include "sysprof-document-overlay.h" # include "sysprof-document-process.h" # include "sysprof-document-sample.h" +# include "sysprof-document-task.h" # include "sysprof-document-traceable.h" # include "sysprof-document.h" # include "sysprof-elf-symbolizer.h" diff --git a/src/sysprof/meson.build b/src/sysprof/meson.build index 989bbd11..ca42ead3 100644 --- a/src/sysprof/meson.build +++ b/src/sysprof/meson.build @@ -59,6 +59,7 @@ sysprof_sources = [ 'sysprof-split-layer.c', 'sysprof-storage-section.c', 'sysprof-symbol-label.c', + 'sysprof-task-row.c', 'sysprof-time-filter-model.c', 'sysprof-time-label.c', 'sysprof-time-ruler.c', diff --git a/src/sysprof/style.css b/src/sysprof/style.css index 946ab248..d5d7b5e2 100644 --- a/src/sysprof/style.css +++ b/src/sysprof/style.css @@ -87,3 +87,12 @@ flamegraph { timespanlayer { font-size: 10px; } + +popover.tasks listview row { + background: transparent; + padding: 6px; +} + +popover.tasks listview row label.heading { + padding-bottom: 3px; +} diff --git a/src/sysprof/sysprof-task-row.c b/src/sysprof/sysprof-task-row.c new file mode 100644 index 00000000..a8e51754 --- /dev/null +++ b/src/sysprof/sysprof-task-row.c @@ -0,0 +1,139 @@ +/* + * sysprof-task-row.c + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "config.h" + +#include + +#include "sysprof-task-row.h" + +struct _SysprofTaskRow +{ + GtkWidget parent_instance; + SysprofDocumentTask *task; +}; + +enum { + PROP_0, + PROP_TASK, + N_PROPS +}; + +G_DEFINE_FINAL_TYPE (SysprofTaskRow, sysprof_task_row, GTK_TYPE_WIDGET) + +static GParamSpec *properties[N_PROPS]; + +static void +sysprof_task_row_cancel (GtkWidget *widget, + const char *action, + GVariant *param) +{ + SysprofTaskRow *self = SYSPROF_TASK_ROW (widget); + + if (self->task) + sysprof_document_task_cancel (self->task); +} + +static void +sysprof_task_row_dispose (GObject *object) +{ + SysprofTaskRow *self = (SysprofTaskRow *)object; + GtkWidget *child; + + gtk_widget_dispose_template (GTK_WIDGET (self), SYSPROF_TYPE_TASK_ROW); + + while ((child = gtk_widget_get_first_child (GTK_WIDGET (self)))) + gtk_widget_unparent (child); + + g_clear_object (&self->task); + + G_OBJECT_CLASS (sysprof_task_row_parent_class)->dispose (object); +} + +static void +sysprof_task_row_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofTaskRow *self = SYSPROF_TASK_ROW (object); + + switch (prop_id) + { + case PROP_TASK: + g_value_set_object (value, self->task); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_task_row_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofTaskRow *self = SYSPROF_TASK_ROW (object); + + switch (prop_id) + { + case PROP_TASK: + if (g_set_object (&self->task, g_value_get_object (value))) + g_object_notify_by_pspec (object, pspec); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_task_row_class_init (SysprofTaskRowClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->dispose = sysprof_task_row_dispose; + object_class->get_property = sysprof_task_row_get_property; + object_class->set_property = sysprof_task_row_set_property; + + properties[PROP_TASK] = + g_param_spec_object ("task", NULL, NULL, + SYSPROF_TYPE_DOCUMENT_TASK, + (G_PARAM_READWRITE | + G_PARAM_EXPLICIT_NOTIFY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/sysprof-task-row.ui"); + gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); + + gtk_widget_class_install_action (widget_class, "task.cancel", NULL, sysprof_task_row_cancel); +} + +static void +sysprof_task_row_init (SysprofTaskRow *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} diff --git a/src/sysprof/sysprof-task-row.h b/src/sysprof/sysprof-task-row.h new file mode 100644 index 00000000..39bc3606 --- /dev/null +++ b/src/sysprof/sysprof-task-row.h @@ -0,0 +1,32 @@ +/* + * sysprof-task-row.h + * + * Copyright 2024 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_TASK_ROW (sysprof_task_row_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofTaskRow, sysprof_task_row, SYSPROF, TASK_ROW, GtkWidget) + +G_END_DECLS diff --git a/src/sysprof/sysprof-task-row.ui b/src/sysprof/sysprof-task-row.ui new file mode 100644 index 00000000..ac91d79f --- /dev/null +++ b/src/sysprof/sysprof-task-row.ui @@ -0,0 +1,71 @@ + + + + diff --git a/src/sysprof/sysprof-window.c b/src/sysprof/sysprof-window.c index 869d6916..5fd6e9a1 100644 --- a/src/sysprof/sysprof-window.c +++ b/src/sysprof/sysprof-window.c @@ -38,29 +38,32 @@ #include "sysprof-samples-section.h" #include "sysprof-sidebar.h" #include "sysprof-storage-section.h" +#include "sysprof-task-row.h" #include "sysprof-util.h" #include "sysprof-window.h" struct _SysprofWindow { - AdwApplicationWindow parent_instance; + AdwApplicationWindow parent_instance; - SysprofDocument *document; - SysprofSession *session; + SysprofDocument *document; + SysprofDocumentLoader *loader; + SysprofSession *session; - GtkToggleButton *show_right_sidebar; - GtkWidget *left_split_overlay; - GtkWidget *right_split_overlay; - GtkProgressBar *progress_bar; - AdwWindowTitle *stack_title; + GtkToggleButton *show_right_sidebar; + GtkWidget *left_split_overlay; + GtkWidget *right_split_overlay; + GtkProgressBar *progress_bar; + AdwWindowTitle *stack_title; - guint disposed : 1; + guint disposed : 1; }; enum { PROP_0, PROP_DOCUMENT, PROP_IS_LOADED, + PROP_LOADER, PROP_SESSION, N_PROPS }; @@ -513,6 +516,10 @@ sysprof_window_get_property (GObject *object, g_value_set_object (value, sysprof_window_get_document (self)); break; + case PROP_LOADER: + g_value_set_object (value, self->loader); + break; + case PROP_IS_LOADED: g_value_set_boolean (value, !!sysprof_window_get_document (self)); break; @@ -560,6 +567,11 @@ sysprof_window_class_init (SysprofWindowClass *klass) SYSPROF_TYPE_DOCUMENT, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + properties[PROP_LOADER] = + g_param_spec_object ("loader", NULL, NULL, + SYSPROF_TYPE_DOCUMENT_LOADER, + (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + properties [PROP_IS_LOADED] = g_param_spec_boolean ("is-loaded", NULL, NULL, FALSE, @@ -617,6 +629,7 @@ sysprof_window_class_init (SysprofWindowClass *klass) g_type_ensure (SYSPROF_TYPE_SESSION); g_type_ensure (SYSPROF_TYPE_SYMBOL); g_type_ensure (SYSPROF_TYPE_SIDEBAR); + g_type_ensure (SYSPROF_TYPE_TASK_ROW); } static void @@ -711,6 +724,9 @@ sysprof_window_load_cb (GObject *object, g_binding_unbind (g_object_get_data (G_OBJECT (loader), "message-binding")); g_object_set_data (G_OBJECT (loader), "message-binding", NULL); + if (g_set_object (&self->loader, NULL)) + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADER]); + if (!(document = sysprof_document_loader_load_finish (loader, result, &error))) { GtkWidget *dialog; @@ -750,6 +766,8 @@ sysprof_window_create (SysprofApplication *app, self = g_object_new (SYSPROF_TYPE_WINDOW, "application", app, NULL); + self->loader = g_object_ref (loader); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_LOADER]); g_object_bind_property (loader, "fraction", self->progress_bar, "fraction", G_BINDING_SYNC_CREATE); diff --git a/src/sysprof/sysprof-window.ui b/src/sysprof/sysprof-window.ui index 1b361e37..f4e1e7cb 100644 --- a/src/sysprof/sysprof-window.ui +++ b/src/sysprof/sysprof-window.ui @@ -108,17 +108,6 @@ - - - - - - SysprofWindow - - - - - @@ -215,6 +204,70 @@ Toggle Right Panel + + + true + false + + + + + + + + SysprofWindow + + + + + + + 300 + + + never + true + 600 + + + + + + + SysprofWindow + + + + + + + + + + +]]> + + + + + + + + + + + diff --git a/src/sysprof/sysprof.gresource.xml b/src/sysprof/sysprof.gresource.xml index aefb57a9..70040947 100644 --- a/src/sysprof/sysprof.gresource.xml +++ b/src/sysprof/sysprof.gresource.xml @@ -53,6 +53,7 @@ sysprof-samples-section.ui sysprof-sidebar.ui sysprof-storage-section.ui + sysprof-task-row.ui sysprof-time-scrubber.ui sysprof-traceables-utility.ui sysprof-weighted-callgraph-view.ui